'use strict'; /** * 账号相关控制器 * * @author CaiAoLin * @date 2018/1/26 * @version */ const moment = require('moment'); const profileMenu = require('../../config/menu').profileMenu; const smsTypeConst = require('../const/sms_type'); const qr = require('qr-image'); const path = require('path'); const sendToWormhole = require('stream-wormhole'); const loginWay = require('../const/setting').loginWay; const wxWork = require('../lib/wx_work'); const profileConst = require('../const/profile'); const DSK = require('../lib/dsk'); module.exports = app => { class ProfileController extends app.BaseController { /** * 构造函数 * * @param {Object} ctx - egg全局context * @return {void} */ constructor(ctx) { super(ctx); ctx.subMenu = profileMenu; } /** * 账号资料页面 * * @param {Object} ctx - egg全局变量 * @return {void} */ async info(ctx) { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); // 获取基础数据的字段规则 const baseRule = ctx.service.projectAccount.rule('profileBase'); const baseJsValidator = await this.jsValidator.convert(baseRule).setSelector('#base-form').build(); const renderData = { accountData, baseJsValidator, }; await this.layout('profile/info.ejs', renderData); } /** * 保存基本信息 * * @param {Object} ctx - egg全局变量 * @return {void} */ async saveBase(ctx) { try { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取基础数据的字段规则 const baseRule = ctx.service.projectAccount.rule('profileBase'); ctx.helper.validate(baseRule); const result = await ctx.service.projectAccount.saveInfo(ctx.request.body, sessionUser.accountId); if (!result) { throw '保存信息失败'; } this.setMessage('修改成功', this.messageType.SUCCESS); } catch (error) { this.log(error); this.setMessage(error.toString(), this.messageType.ERROR); } ctx.redirect(ctx.request.header.referer); } /** * 账号资料页面 * * @param {Object} ctx - egg全局变量 * @return {void} */ async cert(ctx) { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); const certList = await ctx.service.accountCert.getAllDataByCondition({ where: { uid: sessionUser.accountId }, orders: [['create_time', 'desc']] }); // json转换 certList.forEach(item => { item.edu_json = item.edu_json ? JSON.parse(item.edu_json) : []; }); const renderData = { accountData, certList, fujianOssPath: ctx.app.config.fujianOssPath, certTypeConst: profileConst.cert.certType, certSourceConst: { cert_reg: ctx.helper._.orderBy(profileConst.cert.certReg, ['sort'], ['asc']), cert_qual: ctx.helper._.orderBy(profileConst.cert.certQual, ['sort'], ['asc']), }, jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.profile.cert), }; await this.layout('profile/cert.ejs', renderData, 'profile/cert_modal.ejs'); } async certSave(ctx) { const response = { err: 0, msg: '', data: {}, }; try { const sessionUser = ctx.session.sessionUser; const data = JSON.parse(ctx.request.body.data); switch (data.type) { case 'add_cert': response.data = await ctx.service.accountCert.addCert(sessionUser.accountId); break; case 'update_cert': response.data = await ctx.service.accountCert.updateCert(data.update_data); break; case 'add_jx': response.data = await ctx.service.accountCert.addEduJson(data.id); break; case 'update_jx': response.data = await ctx.service.accountCert.updateEduJson(data.update_data); break; case 'del_cert': // 包括删除附件 response.data = await ctx.service.accountCert.delCert(data.delete_data); break; default:throw '参数有误'; } } catch (error) { response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 上传证书(单选) * * @param {object} ctx - egg全局变量 * @return {void} */ async certUpload(ctx) { const responseData = { err: 0, msg: '', data: null, }; try { const stream = await ctx.getFileStream(); const create_time = Date.parse(new Date()) / 1000; const fileInfo = path.parse(stream.filename); const id = stream.fields && stream.fields.id ? stream.fields.id : 0; if (!id) throw '参数有误'; let jxid = ''; let type = ''; if (stream.fields && stream.fields.type === 'upload_jx') { jxid = stream.fields.jxid ? stream.fields.jxid : ''; if (!jxid) throw '参数有误'; type = 'jx'; } else if (stream.fields && stream.fields.type === 'upload_cert') { type = 'cert'; } if (!type) throw '参数有误'; // 判断用户是否选择上传文件 if (!stream.filename) { throw '请选择上传的文件!'; } const filename = stream.filename; const filepath = `app/public/upload/profile/${ctx.session.sessionUser.accountId}/cert/zhengshu_${create_time + fileInfo.ext}`; // await ctx.helper.saveStreamFile(stream, path.resolve(this.app.baseDir, filepath)); await ctx.app.fujianOss.put(ctx.app.config.fujianOssFolder + filepath, stream); await sendToWormhole(stream); if (type === 'jx') { const info = await ctx.service.accountCert.getDataById(id); if (!info) throw '数据有误'; const edu_json = info.edu_json ? JSON.parse(info.edu_json) : []; const jxInfo = edu_json.find(item => item.id === jxid); if (!jxInfo) throw '数据有误'; jxInfo.file_path = filepath; jxInfo.file_name = filename; await ctx.service.accountCert.update({ edu_json: JSON.stringify(edu_json) }, { id }); } else { await ctx.service.accountCert.update({ file_path: filepath, file_name: filename }, { id }); } responseData.data = { file_path: filepath, file_name: filename }; } catch (err) { console.log(err); responseData.err = 1; responseData.msg = err; } ctx.body = responseData; } /** * 修改密码操作 * * @param {Object} ctx - egg全局变量 * @return {void} */ async modifyPassword(ctx) { const password = ctx.request.body.password; const newPassword = ctx.request.body.new_password; try { const sessionUser = ctx.session.sessionUser; let accountId = sessionUser.accountId; accountId = parseInt(accountId); if (isNaN(accountId) || accountId <= 0) { throw '参数错误'; } // 验证数据 const passwordRule = ctx.service.projectAccount.rule('modifyPassword'); ctx.helper.validate(passwordRule); // 判断新密码的强度 const reg = /^(?![0-9]+$)(?![a-zA-Z]+$).{6,16}$/; if (!reg.test(newPassword)) { throw '请设置至少包含数字和字母的新密码'; } const result = await ctx.service.projectAccount.modifyPassword(accountId, password, newPassword); if (!result) { throw '修改密码失败'; } this.setMessage('修改密码成功', this.messageType.SUCCESS); ctx.redirect('/logout'); } catch (error) { ctx.session.postError = error.toString(); this.setMessage(error.toString(), this.messageType.ERROR); ctx.redirect(ctx.request.header.referer); } } /** * 设置短信验证码 * * @param {object} ctx - egg全局变量 * @return {void} */ async getCode(ctx) { const response = { err: 0, msg: '', }; try { const sessionUser = ctx.session.sessionUser; const mobile = ctx.request.body.mobile; let type = null; if (ctx.request.body.type) { type = ctx.request.body.type; delete ctx.request.body.type; } const rule = { mobile: { type: 'mobile', allowEmpty: false } }; ctx.helper.validate(rule); if (type === null || type !== 'shenpi') { // 查找是否有重复的认证手机 const accountData = await ctx.service.projectAccount.getDataByCondition({ project_id: ctx.session.sessionProject.id, auth_mobile: mobile }); if (accountData !== null) { throw '此手机号码已被使用,请重新输入!'; } } const result = await ctx.service.projectAccount.setSMSCode(sessionUser.accountId, mobile); if (!result) { throw '获取验证码失败'; } } catch (error) { response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 绑定认证手机 * * @param {object} ctx - egg全局变量 * @return {void} */ async bindMobile(ctx) { const response = { err: 0, msg: '', }; try { const rule = ctx.service.projectAccount.rule('bindMobile'); ctx.helper.validate(rule); const sessionUser = ctx.session.sessionUser; const result = await ctx.service.projectAccount.bindMobile(sessionUser.accountId, ctx.request.body, ctx.session.sessionProject.id); if (!result) { throw '绑定手机失败!'; } // this.setMessage('绑定成功', this.messageType.SUCCESS); response.msg = '绑定成功'; response.url = ctx.request.header.referer; } catch (error) { this.ctx.helper.log(error); response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 短信通知 * * @param {object} ctx - egg全局变量 * @return {void} */ async sms(ctx) { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); accountData.dsk_account = accountData.dsk_account ? JSON.parse(accountData.dsk_account) : null; const renderData = { accountData, smsType: smsTypeConst.type, }; await this.layout('profile/sms.ejs', renderData, 'profile/modal.ejs'); } /** * 短信通知类型设置 * * @param {object} ctx - egg全局变量 * @return {void} */ async smsType(ctx) { try { const sessionUser = ctx.session.sessionUser; const result = await ctx.service.projectAccount.noticeTypeSet(sessionUser.accountId, ctx.request.body); if (!result) { throw '修改通知类型失败!'; } this.setMessage('通知类型绑定成功', this.messageType.SUCCESS); } catch (error) { console.log(error); this.setMessage(error.toString(), this.messageType.ERROR); } ctx.redirect(ctx.request.header.referer); } async dskApi(ctx) { const response = { err: 0, msg: '', }; try { const sessionUser = ctx.session.sessionUser; const data = JSON.parse(ctx.request.body.data); const dskAccount = await ctx.service.projectAccount.getDataById(sessionUser.accountId); const dsk = new DSK(ctx); switch (data.type) { case 'sms': if (!data.mobile) { throw '参数有误'; } if (!(/^1[3456789]\d{9}$/.test(data.mobile))) { throw '手机号码格式有误'; } await dsk.sendSms(data.mobile); break; case 'bind': if (dskAccount.dsk_account) { throw '已经绑定过大司空账号'; } if ((data.method !== 1 && data.method !== 2) || !data.value || !data.mobile) { throw '参数有误'; } if (!(/^1[3456789]\d{9}$/.test(data.mobile))) { throw '手机号码格式有误'; } const isExist = await ctx.service.projectAccount.isDskExist(ctx.session.sessionProject.id, data.mobile); if (isExist) { throw '此大司空账号已被该项目用户绑定,请重新输入'; } const result = await dsk.accountAuth(data.mobile, data.method, data.value); response.data = await ctx.service.projectAccount.bindDsk(result, data.mobile, sessionUser.accountId); break; case 'unbind': if (!dskAccount.dsk_account) { throw '未绑定大司空账号'; } response.data = await ctx.service.projectAccount.unbindDsk(sessionUser.accountId); break; case 'hadbind': if (!dskAccount.auth_mobile) { response.data = 1; // 未绑定手机 } else if (!dskAccount.dsk_account) { response.data = 2; // 未绑定大司空账号 } else { response.data = JSON.parse(dskAccount.dsk_account); } break; case 'compilation': if (!dskAccount.dsk_account) { throw '未绑定大司空账号'; } const account = JSON.parse(dskAccount.dsk_account); const compilation = await dsk.getCompilation(data.mobile); response.data = {}; response.data.compilation = compilation; if (data.getProject) { const compilationInfo = data.compilationId ? ctx.helper._.find(compilation, { ID: data.compilationId }) : null; const compilationId = compilationInfo ? compilationInfo.ID : (compilation.length > 0 ? compilation[0].ID : null); response.data.select_compilation = compilationId; const projectInfo = compilationId ? await dsk.getProjectList(account.mobile, compilationId) : []; response.data.project = projectInfo; } response.data.dskAccountData = account; break; case 'project': if (!dskAccount.dsk_account) { throw '未绑定大司空账号'; } const account2 = JSON.parse(dskAccount.dsk_account); response.data = await dsk.getProjectList(account2.mobile, data.compilationId); break; case 'save_projects': if (!dskAccount.dsk_account) { throw '未绑定大司空账号'; } const tender = await ctx.service.tender.getDataById(data.tid); if (!tender || tender.user_id !== sessionUser.accountId) { throw '无权限操作'; } await ctx.service.projectAccount.saveDskProjects(sessionUser.accountId, data.project_list); const account3 = JSON.parse(dskAccount.dsk_account); if (data.project_id && ctx.helper._.findIndex(data.project_list, { pid: data.project_id }) !== -1) { account3.select_project = data.project_id; } else { account3.select_project = data.project_list && data.project_list.length > 0 ? data.project_list[0].pid : null; } await ctx.service.projectAccount.defaultUpdate({ id: sessionUser.accountId, dsk_account: JSON.stringify(account3) }); response.data = account3.select_project; break; case 'project_tree': response.data = await dsk.getProjectTree(data.compilationId, data.projectId); const tender2 = data.tid ? await ctx.service.tender.getDataById(data.tid) : null; if (tender2 && tender2.user_id === sessionUser.accountId && dskAccount.dsk_account) { const account4 = JSON.parse(dskAccount.dsk_account); account4.select_project = data.projectId; let treeId = null; if (response.data && response.data.length > 0) { const treeInfo = ctx.helper._.find(response.data, { type: 4 }); if (treeInfo && treeInfo.ID) { treeId = treeInfo.ID; } } account4.select_tree = treeId; await ctx.service.projectAccount.defaultUpdate({ id: sessionUser.accountId, dsk_account: JSON.stringify(account4) }); } break; case 'project_bills': response.data = await dsk.getProjectBills(data.compilationId, data.treeId); const tender3 = data.tid ? await ctx.service.tender.getDataById(data.tid) : null; if (tender3 && tender3.user_id === sessionUser.accountId && dskAccount.dsk_account) { const account5 = JSON.parse(dskAccount.dsk_account); account5.select_tree = data.treeId; await ctx.service.projectAccount.defaultUpdate({ id: sessionUser.accountId, dsk_account: JSON.stringify(account5) }); } break; default:throw '参数有误'; } } catch (error) { response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 电子签名 * * @param {object} ctx - egg全局变量 * @return {void} */ async sign(ctx) { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); const renderData = { accountData, fujianOssPath: ctx.app.config.fujianOssPath, }; await this.layout('profile/sign.ejs', renderData); } /** * 网证通电子签名页 * * @param {object} ctx - egg全局变量 * @return {void} */ async netcasign(ctx) { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); const signData = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId }); const renderData = { accountData, signData, }; await this.layout('profile/netcasign.ejs', renderData, 'profile/sign_modal.ejs'); } /** * 网证通电子签名页面操作 * * @param {object} ctx - egg全局变量 * @return {void} */ async signSave(ctx) { const response = { err: 0, msg: '', }; try { const sessionUser = ctx.session.sessionUser; const data = JSON.parse(ctx.request.body.data); let signData; switch (data.type) { case 'bind': signData = await ctx.service.netcasign.getDataByCondition({ pid: ctx.session.sessionProject.id, keyId: data.updateData.keyId }); if (signData) { const accountData = await ctx.service.projectAccount.getDataByCondition({ id: signData.uid }); throw '该Ukey已绑定于 ' + accountData.name + ', 不可重复绑定'; } const result = await ctx.service.netcasign.add(data.updateData, sessionUser.accountId); if (!result) { throw '绑定Ukey失败'; } response.data = await ctx.service.netcasign.getDataByCondition({ uid: sessionUser.accountId }); break; case 'unbind': signData = await ctx.service.netcasign.getDataByCondition({ pid: ctx.session.sessionProject.id, uid: sessionUser.accountId }); if (!signData) { throw '当前用户不存在绑定证书,解除绑定失败'; } await ctx.service.netcasign.del(signData.id); break; case 'savesign': signData = await ctx.service.netcasign.getDataByCondition({ pid: ctx.session.sessionProject.id, uid: sessionUser.accountId }); if (!signData) { throw '当前用户不存在绑定证书'; } await ctx.service.netcasign.save({ sign_base64: data.sign_base64 }, signData.id); break; case 'delsign': signData = await ctx.service.netcasign.getDataByCondition({ pid: ctx.session.sessionProject.id, uid: sessionUser.accountId }); if (!signData) { throw '当前用户不存在绑定证书'; } if (signData && !signData.sign_base64) { throw '当前用户不存在签名,移除签名失败'; } await ctx.service.netcasign.save({ sign_base64: null }, signData.id); break; default:throw '参数有误'; } } catch (error) { response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 电子签名删除 * * @param {object} ctx - egg全局变量 * @return {void} */ async signDelete(ctx) { const response = { err: 0, msg: '', }; try { const sessionUser = ctx.session.sessionUser; // 获取账号数据 const accountData = await ctx.service.projectAccount.getDataByCondition({ id: sessionUser.accountId }); const data = JSON.parse(ctx.request.body.data); let result = false; if (data.type && data.type === 'stamp') { if (!accountData.stamp_path) { throw '不存在签章'; } const stamp_path_list = accountData.stamp_path.split('!;!'); const spIndex = ctx.helper._.indexOf(stamp_path_list, data.src); if (spIndex === -1) { throw '不存在此签章'; } // 不删除地址,只删除数据库数据,防止已签章的报表丢失 // await ctx.app.fujianOss.delete(ctx.app.config.fujianOssFolder + stamp_path_list[spIndex]); stamp_path_list.splice(spIndex, 1); // 删除库 result = await ctx.service.projectAccount.update({ stamp_path: stamp_path_list.length === 0 ? null : stamp_path_list.join('!;!') }, { id: sessionUser.accountId }); } else { if (accountData.sign_path === '') { throw '不存在签名'; } result = await ctx.service.projectAccount.update({ sign_path: '' }, { id: sessionUser.accountId }); } if (!result) { throw '移除签名失败'; } } catch (error) { response.err = 1; response.msg = error.toString(); } ctx.body = response; } /** * 生成二维码 * * @param {object} ctx - egg全局变量 * @return {void} */ async qrCode(ctx) { const size = 5; const margin = 1; try { // 获取当前用户数据 const sessionUser = ctx.session.sessionUser; let text = ctx.protocol + '://' + ctx.host + '/sign?user_id=' + sessionUser.accountId + '&app_token=' + sessionUser.sessionToken; if (ctx.query.from === 'netcasign') { text += '&from=netcasign'; } // 大小默认5,二维码周围间距默认1 const img = qr.image(text || '', { type: 'png', size: size || 5, margin: margin || 1 }); ctx.status = 200; ctx.type = 'image/png'; ctx.body = img; } catch (e) { ctx.status = 414; ctx.set('Content-Type', 'text/html'); ctx.body = '