oa_based/src/modules/user/user.service.ts

364 lines
11 KiB
TypeScript
Raw Normal View History

2024-02-28 17:02:46 +08:00
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectEntityManager, InjectRepository } from '@nestjs/typeorm';
import Redis from 'ioredis';
import { isEmpty, isNil, isNumber } from 'lodash';
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
import { EntityManager, In, Like, Repository } from 'typeorm';
2024-04-09 15:24:54 +08:00
import pinyin from 'pinyin';
2024-02-28 17:02:46 +08:00
import { BusinessException } from '~/common/exceptions/biz.exception';
import { ErrorEnum } from '~/constants/error-code.constant';
import { ROOT_ROLE_ID, SYS_USER_INITPASSWORD } from '~/constants/system.constant';
import { genAuthPVKey, genAuthPermKey, genAuthTokenKey } from '~/helper/genRedisKey';
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
import { paginate } from '~/helper/paginate';
import { Pagination } from '~/helper/paginate/pagination';
import { AccountUpdateDto } from '~/modules/auth/dto/account.dto';
import { RegisterDto } from '~/modules/auth/dto/auth.dto';
import { QQService } from '~/shared/helper/qq.service';
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
import { md5, randomValue } from '~/utils';
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
import { DeptEntity } from '../system/dept/dept.entity';
import { ParamConfigService } from '../system/param-config/param-config.service';
import { RoleEntity } from '../system/role/role.entity';
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
import { UserStatus } from './constant';
import { PasswordUpdateDto } from './dto/password.dto';
import { UserDto, UserQueryDto, UserUpdateDto } from './dto/user.dto';
import { UserEntity } from './user.entity';
import { AccountInfo } from './user.model';
2024-02-28 08:32:35 +08:00
@Injectable()
export class UserService {
constructor(
@InjectRedis()
private readonly redis: Redis,
@InjectRepository(UserEntity)
private readonly userRepository: Repository<UserEntity>,
@InjectRepository(RoleEntity)
private readonly roleRepository: Repository<RoleEntity>,
@InjectEntityManager() private entityManager: EntityManager,
private readonly paramConfigService: ParamConfigService,
2024-02-28 17:02:46 +08:00
private readonly qqService: QQService
2024-02-28 08:32:35 +08:00
) {}
async findUserById(id: number): Promise<UserEntity | undefined> {
return this.userRepository
.createQueryBuilder('user')
.where({
id,
2024-02-29 09:29:03 +08:00
status: UserStatus.Enabled
2024-02-28 08:32:35 +08:00
})
2024-02-28 17:02:46 +08:00
.getOne();
2024-02-28 08:32:35 +08:00
}
async findUserByUserName(username: string): Promise<UserEntity | undefined> {
return this.userRepository
.createQueryBuilder('user')
.where({
username,
2024-02-29 09:29:03 +08:00
status: UserStatus.Enabled
2024-02-28 08:32:35 +08:00
})
2024-02-28 17:02:46 +08:00
.getOne();
2024-02-28 08:32:35 +08:00
}
/**
*
* @param uid user id
*/
async getAccountInfo(uid: number): Promise<AccountInfo> {
const user: UserEntity = await this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.roles', 'role')
2024-04-16 13:02:41 +08:00
.leftJoinAndSelect('user.dept', 'dept')
2024-02-28 08:32:35 +08:00
.where(`user.id = :uid`, { uid })
2024-02-28 17:02:46 +08:00
.getOne();
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
delete user?.psalt;
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
return user;
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async updateAccountInfo(uid: number, info: AccountUpdateDto): Promise<void> {
2024-02-28 17:02:46 +08:00
const user = await this.userRepository.findOneBy({ id: uid });
if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
2024-02-28 08:32:35 +08:00
const data = {
...(info.nickname ? { nickname: info.nickname } : null),
...(info.avatar ? { avatar: info.avatar } : null),
...(info.email ? { email: info.email } : null),
...(info.phone ? { phone: info.phone } : null),
...(info.qq ? { qq: info.qq } : null),
2024-02-29 09:29:03 +08:00
...(info.remark ? { remark: info.remark } : null)
2024-02-28 17:02:46 +08:00
};
2024-02-28 08:32:35 +08:00
if (!info.avatar && info.qq) {
// 如果qq不等于原qq则更新qq头像
2024-02-28 17:02:46 +08:00
if (info.qq !== user.qq) data.avatar = await this.qqService.getAvater(info.qq);
2024-02-28 08:32:35 +08:00
}
2024-02-28 17:02:46 +08:00
await this.userRepository.update(uid, data);
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async updatePassword(uid: number, dto: PasswordUpdateDto): Promise<void> {
2024-02-28 17:02:46 +08:00
const user = await this.userRepository.findOneBy({ id: uid });
if (isEmpty(user)) throw new BusinessException(ErrorEnum.USER_NOT_FOUND);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const comparePassword = md5(`${dto.oldPassword}${user.psalt}`);
2024-02-28 08:32:35 +08:00
// 原密码不一致,不允许更改
2024-02-28 17:02:46 +08:00
if (user.password !== comparePassword) throw new BusinessException(ErrorEnum.PASSWORD_MISMATCH);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const password = md5(`${dto.newPassword}${user.psalt}`);
await this.userRepository.update({ id: uid }, { password });
await this.upgradePasswordV(user.id);
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async forceUpdatePassword(uid: number, password: string): Promise<void> {
2024-02-28 17:02:46 +08:00
const user = await this.userRepository.findOneBy({ id: uid });
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const newPassword = md5(`${password}${user.psalt}`);
await this.userRepository.update({ id: uid }, { password: newPassword });
await this.upgradePasswordV(user.id);
2024-02-28 08:32:35 +08:00
}
/**
* false则表示已存在该用户
*/
2024-02-28 17:02:46 +08:00
async create({ username, password, roleIds, deptId, ...data }: UserDto): Promise<void> {
2024-02-28 08:32:35 +08:00
const exists = await this.userRepository.findOneBy({
2024-02-29 09:29:03 +08:00
username
2024-02-28 17:02:46 +08:00
});
if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
await this.entityManager.transaction(async manager => {
const salt = randomValue(32);
2024-02-28 08:32:35 +08:00
if (!password) {
2024-02-28 17:02:46 +08:00
const initPassword = await this.paramConfigService.findValueByKey(SYS_USER_INITPASSWORD);
password = md5(`${initPassword ?? '123456'}${salt}`);
} else {
password = md5(`${password ?? '123456'}${salt}`);
2024-02-28 08:32:35 +08:00
}
const u = manager.create(UserEntity, {
username,
password,
...data,
psalt: salt,
roles: await this.roleRepository.findBy({ id: In(roleIds) }),
2024-02-29 09:29:03 +08:00
dept: await DeptEntity.findOneBy({ id: deptId })
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const result = await manager.save(u);
return result;
});
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async update(
id: number,
2024-02-28 17:02:46 +08:00
{ password, deptId, roleIds, status, ...data }: UserUpdateDto
2024-02-28 08:32:35 +08:00
): Promise<void> {
2024-02-28 17:02:46 +08:00
await this.entityManager.transaction(async manager => {
if (password) await this.forceUpdatePassword(id, password);
2024-02-28 08:32:35 +08:00
await manager.update(UserEntity, id, {
...data,
2024-02-29 09:29:03 +08:00
status
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
const user = await this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.roles', 'roles')
.leftJoinAndSelect('user.dept', 'dept')
.where('user.id = :id', { id })
2024-02-28 17:02:46 +08:00
.getOne();
2024-02-28 08:32:35 +08:00
await manager
.createQueryBuilder()
.relation(UserEntity, 'roles')
.of(id)
2024-02-28 17:02:46 +08:00
.addAndRemove(roleIds, user.roles);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
await manager.createQueryBuilder().relation(UserEntity, 'dept').of(id).set(deptId);
2024-02-28 08:32:35 +08:00
if (status === 0) {
// 禁用状态
2024-02-28 17:02:46 +08:00
await this.forbidden(id);
2024-02-28 08:32:35 +08:00
}
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
}
/**
*
* @param id id
*/
async info(id: number): Promise<UserEntity> {
const user = await this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.roles', 'roles')
.leftJoinAndSelect('user.dept', 'dept')
.where('user.id = :id', { id })
2024-02-28 17:02:46 +08:00
.getOne();
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
delete user.password;
delete user.psalt;
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
return user;
2024-02-28 08:32:35 +08:00
}
/**
* ID列表删除用户
*/
async delete(userIds: number[]): Promise<void | never> {
2024-02-28 17:02:46 +08:00
const rootUserId = await this.findRootUserId();
if (userIds.includes(rootUserId)) throw new BadRequestException('不能删除root用户!');
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
await this.userRepository.delete(userIds);
2024-02-28 08:32:35 +08:00
}
/**
* ID
*/
async findRootUserId(): Promise<number> {
const user = await this.userRepository.findOneBy({
2024-02-29 09:29:03 +08:00
roles: { id: ROOT_ROLE_ID }
2024-02-28 17:02:46 +08:00
});
return user.id;
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async list({
page,
pageSize,
username,
nickname,
deptId,
email,
2024-03-28 14:35:00 +08:00
status,
keyword
2024-02-28 08:32:35 +08:00
}: UserQueryDto): Promise<Pagination<UserEntity>> {
const queryBuilder = this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.dept', 'dept')
.leftJoinAndSelect('user.roles', 'role')
// .where('user.id NOT IN (:...ids)', { ids: [rootUserId, uid] })
.where({
...(username ? { username: Like(`%${username}%`) } : null),
...(nickname ? { nickname: Like(`%${nickname}%`) } : null),
...(email ? { email: Like(`%${email}%`) } : null),
2024-02-29 09:29:03 +08:00
...(isNumber(status) ? { status } : null)
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
if (deptId) queryBuilder.andWhere('dept.id = :deptId', { deptId });
2024-03-28 14:35:00 +08:00
if (keyword) {
//关键字模糊查询product的name,productNumber,productSpecification
queryBuilder.andWhere(
2024-04-10 17:39:17 +08:00
`(user.nickname like :keyword or user.namePinyin like :keyword
or dept.name like :keyword
or user.phone like :keyword
or user.email like :keyword
or user.qq like :keyword
or user.remark like :keyword
)`,
2024-03-28 14:35:00 +08:00
{
keyword: `%${keyword}%`
}
);
}
2024-02-28 08:32:35 +08:00
return paginate<UserEntity>(queryBuilder, {
page,
2024-02-29 09:29:03 +08:00
pageSize
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async forbidden(uid: number): Promise<void> {
2024-02-28 17:02:46 +08:00
await this.redis.del(genAuthPVKey(uid));
await this.redis.del(genAuthTokenKey(uid));
await this.redis.del(genAuthPermKey(uid));
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async multiForbidden(uids: number[]): Promise<void> {
if (uids) {
2024-02-28 17:02:46 +08:00
const pvs: string[] = [];
const ts: string[] = [];
const ps: string[] = [];
uids.forEach(uid => {
pvs.push(genAuthPVKey(uid));
ts.push(genAuthTokenKey(uid));
ps.push(genAuthPermKey(uid));
});
await this.redis.del(pvs);
await this.redis.del(ts);
await this.redis.del(ps);
2024-02-28 08:32:35 +08:00
}
}
/**
*
*/
async upgradePasswordV(id: number): Promise<void> {
// admin:passwordVersion:${param.id}
2024-02-28 17:02:46 +08:00
const v = await this.redis.get(genAuthPVKey(id));
if (!isEmpty(v)) await this.redis.set(genAuthPVKey(id), Number.parseInt(v) + 1);
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async exist(username: string) {
2024-02-28 17:02:46 +08:00
const user = await this.userRepository.findOneBy({ username });
if (isNil(user)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
return true;
2024-02-28 08:32:35 +08:00
}
/**
*
*/
async register({ username, ...data }: RegisterDto): Promise<void> {
const exists = await this.userRepository.findOneBy({
2024-02-29 09:29:03 +08:00
username
2024-02-28 17:02:46 +08:00
});
if (!isEmpty(exists)) throw new BusinessException(ErrorEnum.SYSTEM_USER_EXISTS);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
await this.entityManager.transaction(async manager => {
const salt = randomValue(32);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const password = md5(`${data.password ?? 'a123456'}${salt}`);
2024-02-28 08:32:35 +08:00
const u = manager.create(UserEntity, {
username,
password,
status: 1,
2024-02-29 09:29:03 +08:00
psalt: salt
2024-02-28 17:02:46 +08:00
});
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
const user = await manager.save(u);
2024-02-28 08:32:35 +08:00
2024-02-28 17:02:46 +08:00
return user;
});
2024-02-28 08:32:35 +08:00
}
}