feat(用户): 添加备注字段并增强用户数据安全性

在用户实体中添加可选的备注字段
修改用户服务层和控制器以支持备注字段的增删改查
在所有用户数据返回接口中移除密码字段以增强安全性
This commit is contained in:
tikkhun 2025-11-29 10:07:31 +08:00
parent 7b3c7540d7
commit 58004dd091
3 changed files with 84 additions and 13 deletions

View File

@ -40,10 +40,11 @@ export class UserController {
} }
@Post('/add') @Post('/add')
async addUser(@Body() body: { username: string; password: string }) { async addUser(@Body() body: { username: string; password: string; remark?: string }) {
const { username, password } = body; const { username, password, remark } = body;
try { try {
await this.userService.addUser(username, password); // 中文注释:新增用户(支持备注)
await this.userService.addUser(username, password, remark);
return successResponse(true); return successResponse(true);
} catch (error) { } catch (error) {
console.log(error); console.log(error);
@ -52,20 +53,53 @@ export class UserController {
} }
@Get('/list') @Get('/list')
async listUsers(@Query() query: { current: number; pageSize: number }) { async listUsers(
const { current = 1, pageSize = 10 } = query; @Query()
return successResponse(await this.userService.listUsers(current, pageSize)); query: {
current: number;
pageSize: number;
remark?: string;
username?: string;
isActive?: string;
isSuper?: string;
isAdmin?: string;
}
) {
const { current = 1, pageSize = 10, remark, username, isActive, isSuper, isAdmin } = query;
// 中文注释:将字符串布尔转换为真实布尔
const toBool = (v?: string) => (v === undefined ? undefined : v === 'true');
// 中文注释:列表移除密码字段
const { items, total } = await this.userService.listUsers(current, pageSize, {
remark,
username,
isActive: toBool(isActive),
isSuper: toBool(isSuper),
isAdmin: toBool(isAdmin),
});
const safeItems = (items || []).map((it: any) => {
const { password, ...rest } = it || {};
return rest;
});
return successResponse({ items: safeItems, total, current, pageSize });
} }
@Post('/toggleActive') @Post('/toggleActive')
async toggleActive(@Body() body: { userId: number; isActive: boolean }) { async toggleActive(@Body() body: { userId: number; isActive: boolean }) {
return this.userService.toggleUserActive(body.userId, body.isActive); try {
// 中文注释:调用服务层更新启用状态
const data = await this.userService.toggleUserActive(body.userId, body.isActive);
// 中文注释:移除密码字段,保证安全
const { password, ...safe } = data as any;
return successResponse(safe);
} catch (error) {
return errorResponse(error?.message || '操作失败');
}
} }
// 中文注释:更新用户(支持用户名/密码/权限/角色更新) // 中文注释:更新用户(支持用户名/密码/权限/角色更新)
@Post('/update/:id') @Post('/update/:id')
async updateUser( async updateUser(
@Body() body: { username?: string; password?: string; isSuper?: boolean; isAdmin?: boolean; permissions?: string[] }, @Body() body: { username?: string; password?: string; isSuper?: boolean; isAdmin?: boolean; permissions?: string[]; remark?: string },
@Query('id') id?: number @Query('id') id?: number
) { ) {
try { try {
@ -73,7 +107,9 @@ export class UserController {
const userId = Number((this.ctx?.params?.id ?? id)); const userId = Number((this.ctx?.params?.id ?? id));
if (!userId) throw new Error('缺少用户ID'); if (!userId) throw new Error('缺少用户ID');
const data = await this.userService.updateUser(userId, body); const data = await this.userService.updateUser(userId, body);
return successResponse(data); // 中文注释:移除密码字段,保证安全
const { password, ...safe } = data as any;
return successResponse(safe);
} catch (error) { } catch (error) {
return errorResponse(error?.message || '更新失败'); return errorResponse(error?.message || '更新失败');
} }
@ -83,7 +119,10 @@ export class UserController {
@Get() @Get()
async getUser(@User() user) { async getUser(@User() user) {
try { try {
return successResponse(await this.userService.getUser(user.id)); // 中文注释:详情移除密码字段
const data = await this.userService.getUser(user.id);
const { password, ...safe } = (data as any) || {};
return successResponse(safe);
} catch (error) { } catch (error) {
return errorResponse('获取失败'); return errorResponse('获取失败');
} }

View File

@ -28,4 +28,8 @@ export class User {
@Column({ default: true }) @Column({ default: true })
isActive: boolean; // 用户是否启用 isActive: boolean; // 用户是否启用
// 中文注释:备注字段(可选)
@Column({ nullable: true })
remark?: string;
} }

View File

@ -1,7 +1,7 @@
// src/service/user.service.ts // src/service/user.service.ts
import { Body, httpError, Inject, Provide } from '@midwayjs/core'; import { Body, httpError, Inject, Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm'; import { Like, Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs'; import * as bcrypt from 'bcryptjs';
import { JwtService } from '@midwayjs/jwt'; import { JwtService } from '@midwayjs/jwt';
import { User } from '../entity/user.entity'; import { User } from '../entity/user.entity';
@ -81,7 +81,8 @@ export class UserService {
}; };
} }
async addUser(username: string, password: string) { // 中文注释:新增用户(支持可选备注)
async addUser(username: string, password: string, remark?: string) {
const existingUser = await this.userModel.findOne({ const existingUser = await this.userModel.findOne({
where: { username }, where: { username },
}); });
@ -92,14 +93,37 @@ export class UserService {
const user = this.userModel.create({ const user = this.userModel.create({
username, username,
password: hashedPassword, password: hashedPassword,
// 中文注释:备注字段赋值(若提供)
...(remark ? { remark } : {}),
}); });
return this.userModel.save(user); return this.userModel.save(user);
} }
async listUsers(current: number, pageSize: number) { // 中文注释:用户列表支持分页与备注模糊查询(以及可选的布尔过滤)
async listUsers(
current: number,
pageSize: number,
filters: {
remark?: string;
username?: string;
isActive?: boolean;
isSuper?: boolean;
isAdmin?: boolean;
} = {}
) {
// 条件判断:构造 where 条件
const where: Record<string, any> = {};
if (filters.username) where.username = Like(`%${filters.username}%`); // 中文注释:用户名精确匹配(如需模糊可改为 Like
if (typeof filters.isActive === 'boolean') where.isActive = filters.isActive; // 中文注释:按启用状态过滤
if (typeof filters.isSuper === 'boolean') where.isSuper = filters.isSuper; // 中文注释:按超管过滤
if (typeof filters.isAdmin === 'boolean') where.isAdmin = filters.isAdmin; // 中文注释:按管理员过滤
if (filters.remark) where.remark = Like(`%${filters.remark}%`); // 中文注释:备注模糊搜索
const [items, total] = await this.userModel.findAndCount({ const [items, total] = await this.userModel.findAndCount({
where,
skip: (current - 1) * pageSize, skip: (current - 1) * pageSize,
take: pageSize, take: pageSize,
order: { id: 'DESC' },
}); });
return { items, total, current, pageSize }; return { items, total, current, pageSize };
} }
@ -122,6 +146,7 @@ export class UserService {
isSuper?: boolean; isSuper?: boolean;
isAdmin?: boolean; isAdmin?: boolean;
permissions?: string[]; permissions?: string[];
remark?: string;
} }
) { ) {
// 条件判断:查询用户是否存在 // 条件判断:查询用户是否存在
@ -147,6 +172,9 @@ export class UserService {
if (typeof payload.isAdmin === 'boolean') user.isAdmin = payload.isAdmin; if (typeof payload.isAdmin === 'boolean') user.isAdmin = payload.isAdmin;
if (Array.isArray(payload.permissions)) user.permissions = payload.permissions; if (Array.isArray(payload.permissions)) user.permissions = payload.permissions;
// 条件判断:更新备注(若提供则覆盖)
if (typeof payload.remark === 'string') user.remark = payload.remark;
// 保存更新 // 保存更新
return await this.userModel.save(user); return await this.userModel.save(user);
} }