API/src/service/user.service.ts

197 lines
6.3 KiB
TypeScript

// src/service/user.service.ts
import { Body, httpError, Inject, Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Like, Repository } from 'typeorm';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@midwayjs/jwt';
import { User } from '../entity/user.entity';
import { LoginResDTO } from '../dto/user.dto';
import { plainToInstance } from 'class-transformer';
import { MailService } from './mail.service';
import { AuthCodeService } from './authCode.service';
import { DeviceWhitelistService } from './deviceWhitelist.service';
@Provide()
export class UserService {
@InjectEntityModel(User)
userModel: Repository<User>;
@Inject()
jwtService: JwtService;
@Inject()
mailService: MailService;
@Inject()
authCodeService: AuthCodeService;
@Inject()
deviceWhitelistService: DeviceWhitelistService;
async requestAuthCode(deviceId: string) {
const code = await this.authCodeService.generateCode(deviceId);
await this.mailService.sendMail(
'info@canpouches.com',
'Your Login Authorization Code',
`Your authorization code is: ${code}, valid for 10 minutes.`
);
}
async login(@Body() body): Promise<LoginResDTO> {
const { username, password, deviceId, authCode } = body;
const user = await this.userModel.findOne({
where: { username, isActive: true },
});
if (!user || !(await bcrypt.compare(password, user.password))) {
throw new Error('用户名或者密码错误');
}
const isWhite = await this.deviceWhitelistService.isWhitelisted(deviceId);
if (!isWhite) {
if (!authCode) {
await this.requestAuthCode(deviceId);
const err = new httpError.BadRequestError('非白名单设备请填写验证码');
(err as any).code = 10001; // 添加业务错误码
throw err;
}
const valid = await this.authCodeService.verifyCode(deviceId, authCode);
if (!valid) {
throw new Error('验证码错误');
}
// 校验通过后,将设备加入白名单
await this.deviceWhitelistService.addToWhitelist(deviceId);
}
// 生成 JWT,包含角色和权限信息
const token = await this.jwtService.sign({
id: user.id,
deviceId,
username: user.username,
isSuper: user.isSuper,
});
return {
token, //role: user.role,
username: user.username,
userId: user.id,
permissions: user.permissions,
};
}
// 新增用户(支持可选备注)
async addUser(username: string, password: string, remark?: string) {
const existingUser = await this.userModel.findOne({
where: { username },
});
if (existingUser) {
throw new Error('用户已存在');
}
const hashedPassword = await bcrypt.hash(password, 10);
const user = this.userModel.create({
username,
password: hashedPassword,
// 备注字段赋值(若提供)
...(remark ? { remark } : {}),
});
return this.userModel.save(user);
}
// 用户列表支持分页与备注模糊查询(以及可选的布尔过滤)
async listUsers(
current: number,
pageSize: number,
filters: {
remark?: string;
username?: string;
isActive?: boolean;
isSuper?: boolean;
isAdmin?: boolean;
} = {},
sorter: {
field?: string;
order?: 'ASC' | 'DESC';
} = {}
) {
// 条件判断:构造 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 validSortFields = ['id', 'username', 'isActive', 'isSuper', 'isAdmin', 'remark'];
const sortField = validSortFields.includes(sorter.field) ? sorter.field : 'id';
const sortOrder = sorter.order === 'ASC' ? 'ASC' : 'DESC';
const [items, total] = await this.userModel.findAndCount({
where,
skip: (current - 1) * pageSize,
take: pageSize,
order: { [sortField]: sortOrder },
});
return { items, total, current, pageSize };
}
async toggleUserActive(userId: number, isActive: boolean) {
const user = await this.userModel.findOne({ where: { id: userId } });
if (!user) {
throw new Error('User not found');
}
user.isActive = isActive;
return this.userModel.save(user);
}
// 更新用户信息(支持用户名唯一校验与可选密码修改)
async updateUser(
userId: number,
payload: {
username?: string;
password?: string;
isSuper?: boolean;
isAdmin?: boolean;
permissions?: string[];
remark?: string;
}
) {
// 条件判断:查询用户是否存在
const user = await this.userModel.findOne({ where: { id: userId } });
if (!user) {
throw new Error('User not found');
}
// 条件判断:若提供了新用户名且与原用户名不同,校验唯一性
if (payload.username && payload.username !== user.username) {
const exist = await this.userModel.findOne({ where: { username: payload.username } });
if (exist) throw new Error('用户名已存在');
user.username = payload.username;
}
// 条件判断:若提供密码则进行加密存储
if (payload.password) {
user.password = await bcrypt.hash(payload.password, 10);
}
// 条件判断:更新布尔与权限字段(若提供则覆盖)
if (typeof payload.isSuper === 'boolean') user.isSuper = payload.isSuper;
if (typeof payload.isAdmin === 'boolean') user.isAdmin = payload.isAdmin;
if (Array.isArray(payload.permissions)) user.permissions = payload.permissions;
// 条件判断:更新备注(若提供则覆盖)
if (typeof payload.remark === 'string') user.remark = payload.remark;
// 保存更新
return await this.userModel.save(user);
}
async getUser(userId: number) {
return plainToInstance(
User,
await this.userModel.findOne({ where: { id: userId } })
);
}
}