const userModel = require('../model/user'); const jwt = require('jsonwebtoken'); const bcrypt = require('bcrypt'); const secret = require('../config/secret'); const Response = require('../util/response'); const aliClient = require('../util/aliClient'); const getTokenUser = require('../util/tokenProcessor'); class UserController { /** * 登录 * @param ctx * @returns {Promise.} */ static async login(ctx) { const data = ctx.request.body; const user = await userModel.findByPhone(data.phone); if (user) { const confirmRes = bcrypt.compareSync(data.password, user.password); if(!confirmRes){ ctx.body = Response.failed('密码错误,请重试') }else if(!user.isValid){ ctx.body = Response.failed('账号封禁中') }else{ // console.log('ctx',ctx); // console.log('ctx.cookies.redirect_uri',ctx.cookies.get('redirect_uri')); let redirect_uri = ctx.cookies.get('redirect_uri'); let client_id = ctx.cookies.get('client_id'); if(!redirect_uri){ redirect_uri = ctx.origin+"/api/receiveIDToken"; } if(!client_id){ client_id = 'seec-portal'; } let data = { iss : 'p.seecoder.cn', sub: user.phone, aud: client_id, auth_time: Math.floor(new Date().getTime() / 1000), iat: Math.floor(new Date().getTime() / 1000), exp: Math.floor(new Date().getTime() / 1000 + 24 * 60 * 60), user_info: {phone: user.phone, email: user.email, id: user.id, name: user.name ,role: user.role} }; const token = jwt.sign(data, secret.sign); //ctx.redirect(redirect_uri + "?id_token=" + token); ctx.cookies.set('redirect_uri', '', { maxAge:0 }); ctx.cookies.set('client_id', '', { maxAge:0 }); ctx.body = Response.success({token: token, name: user.name, path:redirect_uri}); } } else { ctx.body = Response.failed('此账号不存在'); } } static canManageUser(ctx){ let curUser = getTokenUser(ctx); if (curUser === null){// 没有身份,不可以 return false; } // 管理员或者老师可以管理 return curUser.role === "ADMIN" || curUser.role === "TEACHER"; } /* 批量注册用户 接收一个数组,返回一个map */ static async registerWithAdministrator(ctx){ if (!UserController.canManageUser(ctx)){ return "Method Not Allowed." } const userToRegister = ctx.request.body; const {name, password, phone, email} = userToRegister let msg = "" let id = null let result try { let user = await userModel.findByPhone(phone) // 若用户已经找到,直接返回 console.log(result) if (user){ msg = "用户已存在" id = user.id }else { result = await UserController.createUser(name, password, phone, email); msg = "新建用户" id = result.data.id } }catch (e){ msg = e.message } const registrationResult={phone:phone,result:msg,id:id} console.log(registrationResult) ctx.body = Response.success(registrationResult); } /** * 注册用户 * @param ctx * @returns {Promise.} */ static async register(ctx) { const { name, password, phone, email, identifyCode } = ctx.request.body; let curUser = await getTokenUser(ctx); try { if (curUser === null || curUser.role === "STUDENT"){ // 普通用户注册流程 UserController.validateIdentifyCode(ctx,identifyCode,phone) //需要验证手机验证码 ctx.session.verification_phone = undefined; ctx.session.verification_code = undefined; } const result = await UserController.createUser(name, password, phone, email) ctx.body = Response.success({name:result.data.name}) }catch (e) { ctx.body = Response.failed(e.message) } } //创建用户 static async createUser(name, password, phone, email){ UserController.validateRegistrationData(name, password, phone, email); //注册逻辑 const hashPassword = bcrypt.hashSync(password, 10); const result = await userModel.create(name, hashPassword, phone, email); if (result.code === 0) { return result //返回创建成功的对象 } else if (result.code === -2) { throw new Error('此手机号已注册'); } else if (result.code === -1) { throw new Error('此用户名已使用'); }else { throw new Error("未知错误") } } static validateIdentifyCode(ctx,idetifyCode,phone){ if (!ctx.session.verification_phone || !ctx.session.verification_code) { throw new Error('请重新获取验证码') } if (ctx.session.verification_code !== identifyCode || ctx.session.verification_phone !== phone) { throw new Error('验证码错误'); } } // 验证注册数据是否合法 static validateRegistrationData(name, password, phone, email) { const emailRegex = /^[a-zA-Z0-9_-]+@([a-zA-Z0-9]+\.)+(com|cn|net|org)$/; const nameRegex = /^\w+$/; //由字母、数字、下划线组成且不包含其他字符的字符串 if (!emailRegex.test(email)) { throw new Error('邮箱格式有误'); } if (name.length < 1 || name.length > 10) { throw new Error('用户名长度应在1到10之间'); } if (password.length < 6 || password.length > 20) { throw new Error('密码长度应在6到20之间'); } if (phone.length !== 11) { throw new Error('手机号码长度应为11位'); } if (!nameRegex.test(name)) { throw new Error('用户名只能包含字母、数字和下划线'); } } static async sendMessage(ctx){ let {phone} = ctx.request.body; let code = ""; for(let i = 0; i < 6; i++){ code+=Math.floor(Math.random()*10) } let params = { PhoneNumbers: phone, SignName: 'SEECODER', TemplateCode: 'SMS_253920073', TemplateParam: '{"code":"' + code + '"}' }; let options = { timeout: 3000, // default 3000 ms formatAction: true, // default true, format the action to Action formatParams: true, // default true, format the parameter name to first letter upper case method: 'GET', // set the http method, default is GET headers: {}, // set the http request headers }; try{ const result = await aliClient.request('SendSms', params); // console.log("result:", result); if(result.Code === "OK"){ ctx.session.maxAge = 5 * 60 * 1000; ctx.session.verification_code = code; ctx.session.verification_phone = phone; console.log('verification_code:', ctx.session.verification_code); ctx.body = Response.success({message: '发送成功'}); }else{ console.log("error:", result.message); ctx.body = Response.failed("发送失败,请重试"); } }catch (e) { console.log(e); ctx.body = Response.failed("发送失败,请重试"); } } static async getInfo(ctx) { let theUser = await getTokenUser(ctx); if(!theUser){ ctx.status = 401; }else{ let id = theUser.id; const user = await userModel.findByUid(id); if (user) { ctx.body = Response.success({phone: user.phone, email: user.email, id: user.id, name: user.name, signature: user.signature}); } else { ctx.body = Response.failed('用户信息获取失败'); } } } static async saveName(ctx){ let theUser = await getTokenUser(ctx); if(!theUser){ ctx.status = 401; }else{ let id = theUser.id; let newName = ctx.request.body.newName; let result = await userModel.updateName(id, newName); if(result.code === 0){ ctx.body = Response.success({}); }else{ ctx.body = Response.failed('此昵称已被占用'); } } } static async saveEmail(ctx){ let theUser = await getTokenUser(ctx); if(!theUser){ ctx.status = 401; }else{ let id = theUser.id; let newEmail = ctx.request.body.newEmail; const myReg=/^[a-zA-Z0-9_-]+@([a-zA-Z0-9]+\.)+(com|cn|net|org)$/; if(!myReg.test(newEmail)){ ctx.body = Response.failed('邮箱格式有误'); }else{ let result = await userModel.updateEmail(id, newEmail); if(result.code === 0){ ctx.body = Response.success({}); }else{ ctx.body = Response.failed('修改失败'); } } } } static async saveSignature(ctx){ let theUser = await getTokenUser(ctx); if(!theUser){ ctx.status = 401; }else{ let id = theUser.id; let newSignature = ctx.request.body.newSignature; if(newSignature === 0){ ctx.body = Response.failed('个性签名不得为空'); }else if(newSignature > 20){ ctx.body = Response.failed('个性签名过长'); }else { let result = await userModel.updateSignature(id, newSignature); if(result.code === 0){ ctx.body = Response.success({}); }else{ ctx.body = Response.failed('修改失败'); } } } } static async resetPassword(ctx) { const data = ctx.request.body; if(!ctx.session.verification_phone || !ctx.session.verification_code){ ctx.body = Response.failed('请重新获取验证码'); }else if(ctx.session.verification_code !== data.identifyCode || ctx.session.verification_phone !== data.phone){ ctx.body = Response.failed('验证码错误'); }else if( data.password.length<6 || data.password.length>20 || data.phone.length !== 11){ ctx.body = Response.failed('参数非法'); }else{ const hashPassword = bcrypt.hashSync(data.password, 10); const result = await userModel.resetPassword(data.phone, hashPassword); ctx.session.verification_phone = undefined; ctx.session.verification_code = undefined; if (result) { ctx.body = Response.success({}); } else { ctx.body = Response.failed('此手机号未注册'); } } } } module.exports = UserController;