|
|
@@ -147,6 +147,56 @@ async function handler(request, response) {
|
|
|
const resources = [...ALLOWED_CLASSES].sort().map((className) => ({ className, label: CLASS_LABELS[className] || className, writable: !READ_ONLY_CLASSES.has(className), route: '/admin/resources/' + className }));
|
|
|
return response.json({ success: true, data: { total: resources.length, resources } });
|
|
|
}
|
|
|
+ if (operation === 'createUser') {
|
|
|
+ const username = String(input.username || '').trim();
|
|
|
+ const password = typeof input.password === 'string' ? input.password : '';
|
|
|
+ const displayName = String(input.displayName || '').trim();
|
|
|
+ const mobile = String(input.mobile || '').trim();
|
|
|
+ const inviteUsername = String(input.inviteUsername || '').trim();
|
|
|
+ if (username.length < 3 || username.length > 64) fail(400, '账号长度应为 3 至 64 位');
|
|
|
+ if (password.length < 8 || password.length > 72) fail(400, '密码长度应为 8 至 72 位');
|
|
|
+ if (displayName.length > 100) fail(400, '显示名不能超过 100 位');
|
|
|
+ if (mobile.length > 32) fail(400, '手机号不能超过 32 位');
|
|
|
+ let companyId = pointerId(context.company);
|
|
|
+ if (context.isSuperAdmin && input.companyId) companyId = String(input.companyId);
|
|
|
+ if (!companyId) fail(400, '开户必须指定帐套');
|
|
|
+ const company = await new Parse.Query('Company').get(companyId, { useMasterKey: true });
|
|
|
+ const userFields = await schemaFor('_User');
|
|
|
+ if (!userFields.company) fail(500, '用户表缺少帐套字段');
|
|
|
+ const duplicate = await Psql.oneOrNone('SELECT "objectId" FROM "_User" WHERE "company"=$1 AND "username"=$2 AND ("isDeleted" IS NULL OR "isDeleted"=FALSE) LIMIT 1', [companyId, username]);
|
|
|
+ if (duplicate) fail(409, '此用户名已被使用');
|
|
|
+ let parentId = 0;
|
|
|
+ if (inviteUsername) {
|
|
|
+ const inviter = await Psql.oneOrNone('SELECT COALESCE("legacyUserId", CASE WHEN COALESCE("legacyUserData"->>\'UserID\',\'\') ~ \'^[0-9]+$\' THEN ("legacyUserData"->>\'UserID\')::numeric END) AS id FROM "_User" WHERE "company"=$1 AND "username"=$2 AND ("isDeleted" IS NULL OR "isDeleted"=FALSE) LIMIT 1', [companyId, inviteUsername]);
|
|
|
+ parentId = Number(inviter && inviter.id) || 0;
|
|
|
+ if (!parentId) fail(400, '邀请账号不存在');
|
|
|
+ }
|
|
|
+ const created = new Parse.User();
|
|
|
+ created.setUsername(username); created.setPassword(password); created.set('company', company);
|
|
|
+ if (userFields.type) created.set('type', 'user');
|
|
|
+ if (userFields.isDisabled) created.set('isDisabled', false);
|
|
|
+ if (userFields.isDeleted) created.set('isDeleted', false);
|
|
|
+ if (userFields.legacyGroupId) created.set('legacyGroupId', 1);
|
|
|
+ if (userFields.nickname) created.set('nickname', displayName || username);
|
|
|
+ if (mobile && userFields.mobile) created.set('mobile', mobile);
|
|
|
+ if (userFields.isAdmin) created.set('isAdmin', false);
|
|
|
+ if (userFields.roles) created.set('roles', []);
|
|
|
+ try { await created.signUp(null, { useMasterKey: true }); }
|
|
|
+ catch (error) { if (Number(error && error.code) === 202) fail(409, '此用户名已被使用'); throw error; }
|
|
|
+ try {
|
|
|
+ const regTime = new Date().toISOString();
|
|
|
+ const legacyData = { UserID:0,UserName:username,HoneyName:displayName || username,GroupID:1,ParentUserID:parentId,VIP:0,RegTime:regTime,Purse:0,SilverCoin:0,UserExp:0,UserPoint:0,boffExp:0,State:1 };
|
|
|
+ const rows = await Psql.query('WITH lock_row AS MATERIALIZED (SELECT pg_advisory_xact_lock(hashtext(\'xiaoshu-legacy-user-id\'))), next_id AS MATERIALIZED (SELECT COALESCE(MAX("legacyUserId"),0)+1 AS id FROM "_User",lock_row WHERE "company"=$1 AND COALESCE("legacyUserId",0)>0) UPDATE "_User" SET "legacyUserId"=next_id.id,"legacyGroupId"=1,"legacyUserData"=jsonb_set($3::jsonb,\'{UserID}\',to_jsonb(next_id.id),true),"updatedAt"=NOW() FROM next_id WHERE "objectId"=$2 RETURNING next_id.id', [companyId, created.id, JSON.stringify(legacyData)]);
|
|
|
+ const legacyUserId = Number(rows[0] && rows[0].id) || 0;
|
|
|
+ if (!legacyUserId) throw new Error('无法分配旧系统用户 ID');
|
|
|
+ await created.fetch({ useMasterKey: true });
|
|
|
+ await audit({ ...context, company }, 'create-user', '_User', created.id);
|
|
|
+ return response.json({ success: true, data: { ...serializeObject(created), userId: legacyUserId, groupId: 1 } });
|
|
|
+ } catch (error) {
|
|
|
+ await created.destroy({ useMasterKey: true }).catch(() => undefined);
|
|
|
+ throw error;
|
|
|
+ }
|
|
|
+ }
|
|
|
const className = String(input.className || '');
|
|
|
assertClass(className);
|
|
|
const fields = await schemaFor(className);
|