import { HttpException, HttpStatus, Inject, Injectable, UnauthorizedException, } from '@nestjs/common'; import { CreateUserDto } from './dto/create-user.dto'; import { UpdateUserDto } from './dto/update-user.dto'; import { Prisma, SysUser } from '@prisma/client'; import { PrismaService } from '@/prisma/prisma.service'; import { QueryListDto } from './dto/query-list.dto'; import * as bcrypt from 'bcryptjs'; import { UpdatePasswordDto } from './dto/update-password.dto'; import { SkipAuth } from '@/common/decorators/skip-auth.decorator'; import { RedisGroup } from '@/common/enum'; import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager'; @Injectable() @SkipAuth() export class UserService { constructor( private readonly prisma: PrismaService, @Inject(CACHE_MANAGER) private cacheManager: Cache, ) {} async findOne( params: Prisma.SysUserWhereUniqueInput, ): Promise { return this.prisma.sysUser.findUnique({ where: params, }); } async create(dto: CreateUserDto) { const userExists = await this.findOne({ account: dto.account }); if (userExists) throw new HttpException('账号已存在!', HttpStatus.UNPROCESSABLE_ENTITY); const passwordSalt = await bcrypt.genSalt(); const password = await bcrypt.hash(dto.password, passwordSalt); const data = { ...dto, passwordSalt, password, }; await this.prisma.sysUser.create({ data }); } async findAll(dto: QueryListDto): Promise { const { page, pageSize, account, status, name } = dto; const query: Prisma.SysUserFindManyArgs = { select: { id: true, name: true, account: true, status: true, gender: true, createdAt: true, updatedAt: true, avatar: true, userRole: { select: { roleId: true }, }, }, skip: pageSize * (page - 1), take: pageSize, where: { ...(account && { account: { contains: account } }), ...(!isNaN(status) && { status }), ...(name && { name: { contains: name } }), }, }; const [list, count] = await this.prisma.$transaction([ this.prisma.sysUser.findMany(query), this.prisma.sysUser.count({ where: query.where }), ]); return { page, pageSize, count, list, }; } async findOneById(id: string) { return this.prisma.sysUser.findUnique({ select: { id: true, name: true, account: true, status: true, gender: true, avatar: { select: { id: true, path: true, }, }, }, where: { id, }, }); } async getUserInfo(id: string, isAdmin: boolean) { const user = await this.prisma.sysUser.findUnique({ select: { name: true, gender: true, account: true, avatar: { select: { path: true, }, }, createdAt: true, }, where: { id, status: 1, }, }); const query: Prisma.SysMenuFindManyArgs = { where: {} }; if (!isAdmin) { query.where = { sysMenuPermission: { some: { permission: { sysRolePermission: { some: { role: { sysUserRole: { some: { userId: id } } } }, }, }, }, }, }; } const menus = await this.prisma.sysMenu.findMany(query); return { ...user, menus, }; } async update(id: string, data: UpdateUserDto) { const user = await this.findOne({ id }); if (!user) throw new HttpException('修改失败,用户不存在!', HttpStatus.NOT_FOUND); await this.prisma.$transaction(async (prisma) => { await prisma.sysUser.update({ data, where: { id }, }); // 禁用用户 删除token if (data.status === 0) { if (data.status === 0) { const keys = await this.cacheManager.store.keys( `${RedisGroup.JwtJti}:${id}:*`, ); await this.cacheManager.store.mdel(...keys); } } }); } async updatePassword( id: string, { password, newPassword }: UpdatePasswordDto, ) { const user = await this.findOne({ id }); if (!user) throw new HttpException('修改失败,用户不存在!', HttpStatus.NOT_FOUND); // 同时存在密码和新密码 const data: Partial = {}; const comparePasswordSalt = user.passwordSalt; const comparePassword = await bcrypt.hash(password, comparePasswordSalt); if (comparePassword !== user.password) { throw new UnauthorizedException('密码错误,修改失败!'); } else { data.passwordSalt = await bcrypt.genSalt(); data.password = await bcrypt.hash(newPassword, data.passwordSalt); } await this.prisma.sysUser.update({ data, where: { id }, }); } async remove(id: string) { const user = await this.findOne({ id }); if (!user) throw new HttpException('删除失败,用户不存在!', HttpStatus.NOT_FOUND); await this.prisma.sysUser.delete({ where: { id } }); } }