This commit is contained in:
2025-12-25 16:59:45 +08:00
parent 18a7c15a76
commit 868979a423
4 changed files with 63 additions and 32 deletions

View File

@@ -14,6 +14,7 @@ type UserData = {
orgs?: string[]; orgs?: string[];
wxUnionId?: string; wxUnionId?: string;
phone?: string; phone?: string;
canChangeUsername?: boolean;
}; };
export enum UserTypes { export enum UserTypes {
'user' = 'user', 'user' = 'user',
@@ -182,6 +183,9 @@ export class User extends Model {
avatar: this.avatar, avatar: this.avatar,
orgs, orgs,
}; };
if(this.data?.canChangeUsername) {
info.canChangeUsername = this.data.canChangeUsername;
}
const tokenUser = this.tokenUser; const tokenUser = this.tokenUser;
if (uid) { if (uid) {
info.uid = uid; info.uid = uid;

View File

@@ -8,29 +8,26 @@ import { AppModel } from '@/routes/app-manager/index.ts';
export const checkUsername = (username: string) => { export const checkUsername = (username: string) => {
if (username.length > 30) { if (username.length > 30) {
throw new CustomError(400, 'Username cannot be too long'); throw new CustomError(400, '用户名不能过长');
} }
if (!/^[a-zA-Z0-9_@]+$/.test(username)) { if (!/^[a-zA-Z0-9_@]+$/.test(username)) {
throw new CustomError(400, 'Username cannot contain special characters'); throw new CustomError(400, '用户名包含非法字符');
} }
if (username.includes(' ')) { if (username.includes(' ')) {
throw new CustomError(400, 'Username cannot contain spaces'); throw new CustomError(400, '用户名不能包含空格');
} }
}; };
export const checkUsernameShort = (username: string) => { export const checkUsernameShort = (username: string) => {
if (username.length < 3) { if (username.length <= 3) {
throw new CustomError(400, 'Username cannot be too short'); throw new CustomError(400, '用户名不能过短');
} }
}; };
app export const toChangeName = async (opts: { id: number; newName: string; admin?: boolean, ctx: any }) => {
.route({ const { id, newName, admin = false, ctx } = opts;
path: 'user', if (!admin) {
key: 'changeName', checkUsernameShort(newName);
middleware: ['auth-admin'], }
})
.define(async (ctx) => {
const { id, newName } = ctx.query.data || {};
const user = await User.findByPk(id); const user = await User.findByPk(id);
if (!user) { if (!user) {
ctx.throw(404, 'User not found'); ctx.throw(404, 'User not found');
@@ -42,8 +39,11 @@ app
ctx.throw(400, 'Username already exists'); ctx.throw(400, 'Username already exists');
} }
user.username = newName; user.username = newName;
const data = user.data || {};
data.canChangeUsername = false;
user.data = data;
try { try {
await user.save(); await user.save({ fields: ['username', 'data'] });
// 迁移文件数据 // 迁移文件数据
await backupUserA(oldName, user.id); // 备份文件数据 await backupUserA(oldName, user.id); // 备份文件数据
await mvUserAToUserB(oldName, newName, true); // 迁移文件数据 await mvUserAToUserB(oldName, newName, true); // 迁移文件数据
@@ -58,7 +58,26 @@ app
console.error('迁移文件数据失败', error); console.error('迁移文件数据失败', error);
ctx.throw(500, 'Failed to change username'); ctx.throw(500, 'Failed to change username');
} }
ctx.body = user; return user;
}
app
.route({
path: 'user',
key: 'changeName',
middleware: ['auth-admin'],
})
.define(async (ctx) => {
const { id, newName } = ctx.query.data || {};
try {
if (!id || !newName) {
ctx.throw(400, '参数错误');
}
const user = await toChangeName({ id, newName, admin: true, ctx });
ctx.body = await user?.getInfo?.();
} catch (error) {
console.error('changeName error', error);
ctx.throw(500, 'Failed to change username');
}
}) })
.addTo(app); .addTo(app);

View File

@@ -209,7 +209,7 @@ app
middleware: ['auth'], middleware: ['auth'],
}) })
.define(async (ctx) => { .define(async (ctx) => {
const { username, password, description, avatar, email } = ctx.query.data || {}; const { username, nickname, password, description, avatar, email } = ctx.query.data || {};
const tokenUser = ctx.state?.tokenUser || {}; const tokenUser = ctx.state?.tokenUser || {};
const { id } = tokenUser; const { id } = tokenUser;
const user = await User.findByPk(id); const user = await User.findByPk(id);
@@ -217,8 +217,16 @@ app
ctx.throw(404, 'user not found'); ctx.throw(404, 'user not found');
} }
user.setTokenUser(tokenUser); user.setTokenUser(tokenUser);
if (username) { if (username && user.data?.canChangeUsername) {
user.username = username; user.username = username;
const data = user.data || {};
data.canChangeUsername = false;
user.data = data;
} else if (username && username !== user.username) {
ctx.throw(400, '请求更改用户名失败,用户名不可再更改,请联系管理员');
}
if (nickname) {
user.nickname = nickname;
} }
if (password && user.type !== 'org') { if (password && user.type !== 'org') {
user.createPassword(password); user.createPassword(password);
@@ -232,7 +240,7 @@ app
if (email) { if (email) {
user.email = email; user.email = email;
} }
await user.save(); await user.save({ fields: ['username', 'nickname', 'data', 'password', 'description', 'avatar', 'email'] });
ctx.body = await user.getInfo(); ctx.body = await user.getInfo();
}) })
.addTo(app); .addTo(app);

View File

@@ -53,7 +53,7 @@ app
.route({ .route({
path: 'wx', path: 'wx',
key: 'mp-get-openid', key: 'mp-get-openid',
description: '微信公众平台获取openid', description: '微信公众平台获取openid, 根据code回调去获取openid',
}) })
.define(async (ctx) => { .define(async (ctx) => {
const code = ctx.query.code; const code = ctx.query.code;