import { OakOperationUnpermittedException, OakPreConditionUnsetException } from "oak-domain/lib/types"; import { generateNewIdAsync } from "oak-domain/lib/utils/uuid"; import { encryptPasswordSha1 } from '../utils/password'; import { assert } from 'oak-domain/lib/utils/assert'; import dayjs from 'dayjs'; export async function mergeUser(params, context, innerLogic) { const { from, to, mergeMobile, mergeEmail, mergeWechatUser } = params; if (!innerLogic && !context.isRoot()) { throw new OakOperationUnpermittedException('user', { id: 'merge', action: 'merge', data: {}, filter: { id: from } }, context.getCurrentUserId(), '不允许执行mergeUser操作'); } assert(from); assert(to); assert(from !== to, '不能merge到相同user'); const schema = context.getSchema(); /* for (const entity in schema) { if (['oper', 'modi', 'operEntity', 'modiEntity', 'userEntityGrant', 'wechatQrCode'].includes(entity)) { continue; } const entityDesc = schema[entity]; if (entityDesc.view) { continue; } const { attributes } = entityDesc; for (const attr in attributes) { const attrDef = attributes[attr as keyof typeof attributes]; if (attrDef.type === 'ref' && attrDef.ref === 'user') { await context.operate(entity, { action: 'update', data: { [attr]: to, }, filter: { [attr]: from, } } as any, { dontCollect: true }); } if (attr === 'entity' && attributes.hasOwnProperty('entityId')) { await context.operate(entity, { action: 'update', data: { entityId: to, }, filter: { entity: 'user', entityId: from, } } as any, { dontCollect: true }); } } } */ // 如果from是root,to也得赋上 const [fromUser] = await context.select('user', { data: { id: 1, isRoot: 1, }, filter: { id: from, } }, { dontCollect: true }); if (fromUser.isRoot) { await context.operate('user', { id: await generateNewIdAsync(), action: 'update', data: { isRoot: true, }, filter: { id: to, }, }, {}); } await context.operate('token', { id: await generateNewIdAsync(), action: 'disable', data: {}, filter: { ableState: 'enabled', playerId: from, // todo 这里是playerId, root如果正在扮演该用户待处理 }, }, { dontCollect: true }); await context.operate('user', { id: await generateNewIdAsync(), action: 'merge', data: { refId: to, userState: 'merged', }, filter: { $or: [ { id: from, }, { userState: 'merged', refId: from, } ], }, }, {}); if (mergeEmail) { await context.operate('email', { id: await generateNewIdAsync(), action: 'update', data: { userId: to, }, filter: { userId: from, } }, { dontCollect: true }); } if (mergeMobile) { await context.operate('mobile', { id: await generateNewIdAsync(), action: 'update', data: { userId: to, }, filter: { userId: from, } }, { dontCollect: true }); } if (mergeWechatUser) { await context.operate('wechatUser', { id: await generateNewIdAsync(), action: 'update', data: { userId: to, }, filter: { userId: from, } }, { dontCollect: true }); } } export async function getChangePasswordChannels(params, context, innerLogic) { const { userId } = params; const mobileList = await context.select('mobile', { data: { id: 1, mobile: 1, userId: 1, }, filter: { userId, ableState: 'enabled', }, }, { dontCollect: true, }); const [user] = await context.select('user', { data: { id: 1, password: 1, passwordSha1: 1, }, filter: { id: userId, } }, { dontCollect: true }); const result = []; if (mobileList.length > 0) { result.push('mobile'); } if (user.password || user.passwordSha1) { result.push('password'); } return result; } export async function updateUserPassword(params, context, innerLogic) { const { userId, prevPassword, captcha, mobile, newPassword } = params; const systemId = context.getSystemId(); const closeRootMode = context.openRootMode(); try { const [system] = await context.select('system', { data: { id: 1, config: 1, }, filter: { id: systemId, } }, { forUpdate: true }); assert(system); const config = system.config?.Password; const mode = config?.mode ?? 'all'; const [user] = await context.select('user', { data: { id: 1, password: 1, passwordSha1: 1, }, filter: { id: userId, }, }, { dontCollect: true }); if (prevPassword) { const [lastSuccessfulTemp] = await context.select('changePasswordTemp', { data: { id: 1, $$seq$$: 1, }, filter: { userId, $$createAt$$: { $gt: dayjs().startOf('day').valueOf(), }, result: 'success', }, sorter: [ { $attr: { $$seq$$: 1, }, $direction: 'desc', }, ], indexFrom: 0, count: 1, }, { dontCollect: true, }); const count1 = await context.count('changePasswordTemp', { filter: lastSuccessfulTemp ? { userId, $$seq$$: { $gt: lastSuccessfulTemp.$$seq$$, }, result: 'fail', } : { userId, $$createAt$$: { $gt: dayjs().startOf('day').valueOf(), }, result: 'fail', }, }, { dontCollect: true, }); if (count1 >= 5) { closeRootMode(); return { result: '您今天已尝试过太多次,请稍候再进行操作', times: count1, }; } const allowUpdate = mode === 'sha1' ? user.passwordSha1 === prevPassword : user.password === prevPassword; //sha1密文模式判断密文是否相等 let userData = {}, changeCreateData = {}; if (mode === 'all') { userData = { password: newPassword, passwordSha1: encryptPasswordSha1(newPassword), }; changeCreateData = { prevPassword, newPassword, prevPasswordSha1: encryptPasswordSha1(prevPassword), newPasswordSha1: encryptPasswordSha1(newPassword), }; } else if (mode === 'plain') { userData = { password: newPassword, }; changeCreateData = { prevPassword, newPassword, }; } else if (mode === 'sha1') { userData = { passwordSha1: newPassword, }; changeCreateData = { prevPasswordSha1: prevPassword, newPasswordSha1: newPassword, }; } if (allowUpdate) { await context.operate('user', { id: await generateNewIdAsync(), action: 'update', data: userData, filter: { id: userId, }, }, { dontCollect: true, }); await context.operate('changePasswordTemp', { id: await generateNewIdAsync(), action: 'create', data: { id: await generateNewIdAsync(), userId, result: 'success', ...changeCreateData, }, }, { dontCollect: true, }); closeRootMode(); return { result: 'success' }; } else { await context.operate('changePasswordTemp', { id: await generateNewIdAsync(), action: 'create', data: { id: await generateNewIdAsync(), userId, result: 'fail', ...changeCreateData, }, }, { dontCollect: true, }); closeRootMode(); return { result: '原密码不正确,请检查后输入', times: count1, }; } } if (mobile && captcha) { const [aliveCaptcha] = await context.select('captcha', { data: { id: 1, }, filter: { origin: 'mobile', content: mobile, code: captcha, expired: false, }, indexFrom: 0, count: 1, }, { dontCollect: true, }); if (aliveCaptcha) { let userData = {}, changeCreateData = {}; if (mode === 'all') { userData = { password: newPassword, passwordSha1: encryptPasswordSha1(newPassword), }; changeCreateData = { prevPassword: user.password, newPassword, prevPasswordSha1: user.passwordSha1, newPasswordSha1: encryptPasswordSha1(newPassword), }; } else if (mode === 'plain') { userData = { password: newPassword, }; changeCreateData = { prevPassword: user.password, newPassword, }; } else if (mode === 'sha1') { userData = { passwordSha1: newPassword, }; changeCreateData = { prevPasswordSha1: user.passwordSha1, newPasswordSha1: newPassword, }; } await context.operate('user', { id: await generateNewIdAsync(), action: 'update', data: userData, filter: { id: userId, }, }, { dontCollect: true, }); await context.operate('changePasswordTemp', { id: await generateNewIdAsync(), action: 'create', data: { id: await generateNewIdAsync(), userId, result: 'success', ...changeCreateData, }, }, { dontCollect: true, }); closeRootMode(); return { result: 'success' }; } else { closeRootMode(); return { result: '验证码错误', }; } } closeRootMode(); return { result: '缺少原密码或验证码,请检查后再进行操作' }; } catch (err) { closeRootMode(); throw err; } } /** * 用户账号注册 * @param params * @param context */ export async function registerUserByLoginName(params, context) { const { loginName, password } = params; const systemId = context.getSystemId(); const closeRootMode = context.openRootMode(); try { // 检查loginName是否重复 const [existLoginName] = await context.select('loginName', { data: { id: 1, name: 1, }, filter: { name: loginName, ableState: 'enabled', }, }, { dontCollect: true, forUpdate: true }); if (existLoginName) { closeRootMode(); throw new OakPreConditionUnsetException('账号已存在,请重新设置'); } // 创建user并附上密码,级联创建loginName const [system] = await context.select('system', { data: { id: 1, config: 1, }, filter: { id: systemId, } }, { forUpdate: true }); assert(system); const config = system.config?.Password; const mode = config?.mode ?? 'all'; let passwordData = {}; if (mode === 'all') { passwordData = { password: password, passwordSha1: encryptPasswordSha1(password), }; } else if (mode === 'plain') { passwordData = { password: password, }; } else if (mode === 'sha1') { passwordData = { passwordSha1: password, }; } const userData = { id: await generateNewIdAsync(), loginName$user: [ { id: await generateNewIdAsync(), action: 'create', data: { id: await generateNewIdAsync(), name: loginName, } } ] }; Object.assign(userData, passwordData); await context.operate('user', { id: await generateNewIdAsync(), action: 'create', data: userData, }, {}); } catch (err) { closeRootMode(); throw err; } }