kt-template-online-api/src/admin/user/admin-user.service.ts

277 lines
8.0 KiB
TypeScript

import { HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Not, Repository } from 'typeorm';
import { throwVbenError } from '@/common';
import { AdminDept } from '../dept/admin-dept.entity';
import { AdminRole } from '../role/admin-role.entity';
import { AdminUser } from './admin-user.entity';
import type { AdminUserInput, AdminUserListQuery } from '../admin.types';
@Injectable()
export class AdminUserService {
constructor(
@InjectRepository(AdminUser)
private readonly userRepository: Repository<AdminUser>,
@InjectRepository(AdminRole)
private readonly roleRepository: Repository<AdminRole>,
@InjectRepository(AdminDept)
private readonly deptRepository: Repository<AdminDept>,
) {}
async getUserList(query: AdminUserListQuery) {
const page = Number(query.page || 1);
const pageSize = Number(query.pageSize || 20);
const builder = this.userRepository
.createQueryBuilder('user')
.leftJoinAndSelect('user.roles', 'role')
.leftJoinAndSelect('user.dept', 'dept')
.where('user.isDeleted = :isDeleted', { isDeleted: false });
if (query.id) {
builder.andWhere('user.id LIKE :id', { id: `%${query.id}%` });
}
if (query.username) {
builder.andWhere('user.username LIKE :username', {
username: `%${query.username}%`,
});
}
if (query.realName) {
builder.andWhere('user.realName LIKE :realName', {
realName: `%${query.realName}%`,
});
}
if (['0', '1'].includes(String(query.status))) {
builder.andWhere('user.status = :status', {
status: Number(query.status),
});
}
if (query.deptId) {
const deptIds = await this.collectDeptIds(String(query.deptId));
builder.andWhere('user.deptId IN (:...deptIds)', { deptIds });
}
if (query.roleId) {
builder.andWhere('role.id = :roleId', { roleId: String(query.roleId) });
}
if (query.startTime) {
builder.andWhere('user.createTime >= :startTime', {
startTime: query.startTime,
});
}
if (query.endTime) {
builder.andWhere('user.createTime <= :endTime', {
endTime: query.endTime,
});
}
const [users, total] = await builder
.orderBy('user.createTime', 'ASC')
.skip((page - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
return {
items: users.map((user) => this.serializeUserForList(user)),
total,
};
}
async createUser(data: AdminUserInput) {
await this.ensureUsernameAvailable(String(data.username || ''));
const user = this.userRepository.create({
deptId: data.deptId || null,
homePath: data.homePath || '/workspace',
password: data.password || '123456',
realName: data.realName,
status: data.status ?? 1,
timezone: data.timezone || 'Asia/Shanghai',
username: data.username,
});
user.roles = await this.findRolesByIds(data.roleIds || []);
await this.userRepository.save(user);
return null;
}
async updateUser(id: string, data: AdminUserInput) {
const user = await this.userRepository.findOne({
relations: ['roles'],
where: {
id,
isDeleted: false,
},
});
if (!user) throwVbenError('用户不存在', HttpStatus.BAD_REQUEST);
if (data.username !== undefined && data.username !== user.username) {
await this.ensureUsernameAvailable(data.username, id);
user.username = data.username;
}
if (data.password) user.password = data.password;
if (data.deptId !== undefined) user.deptId = data.deptId || null;
if (data.realName !== undefined) user.realName = data.realName;
if (data.homePath !== undefined) user.homePath = data.homePath;
if (data.timezone !== undefined) user.timezone = data.timezone;
if (data.status !== undefined) user.status = data.status;
if (data.roleIds) user.roles = await this.findRolesByIds(data.roleIds);
await this.userRepository.save(user);
return null;
}
async deleteUser(id: string, currentUserId?: string) {
const user = await this.userRepository.findOne({
where: {
id,
isDeleted: false,
},
});
if (!user) throwVbenError('用户不存在', HttpStatus.BAD_REQUEST);
if (id === currentUserId) {
throwVbenError('不能删除当前登录用户', HttpStatus.BAD_REQUEST);
}
if (user.username === 'admin') {
throwVbenError('不能删除内置管理员账号', HttpStatus.BAD_REQUEST);
}
await this.userRepository.update(
{ id },
{
isDeleted: true,
},
);
return null;
}
async updateCurrentProfile(userId: string, data: AdminUserInput) {
const user = await this.findActiveUser(userId);
if (data.realName !== undefined) {
const realName = String(data.realName || '').trim();
if (!realName) throwVbenError('姓名不能为空', HttpStatus.BAD_REQUEST);
user.realName = realName;
}
if (data.homePath !== undefined) {
user.homePath = String(data.homePath || '').trim() || '/workspace';
}
if (data.avatar !== undefined) {
user.avatar = String(data.avatar || '').trim();
}
await this.userRepository.save(user);
return this.findActiveUser(userId);
}
serializeUser(user: AdminUser) {
return {
avatar: user.avatar || '',
homePath: user.homePath,
id: user.id,
realName: user.realName,
roles: (user.roles || [])
.filter((role) => !role.isDeleted && role.status === 1)
.map((role) => role.roleCode),
timezone: user.timezone,
userId: user.id,
username: user.username,
};
}
private serializeUserForList(user: AdminUser) {
const activeRoles = (user.roles || []).filter((role) => !role.isDeleted);
return {
createTime: user.createTime,
dept: user.dept
? {
id: user.dept.id,
name: user.dept.name,
}
: null,
deptId: user.deptId,
deptName: user.dept?.name || '',
homePath: user.homePath,
id: user.id,
avatar: user.avatar,
realName: user.realName,
roleIds: activeRoles.map((role) => role.id),
roleNames: activeRoles.map((role) => role.name),
roles: activeRoles.map((role) => ({
id: role.id,
name: role.name,
roleCode: role.roleCode,
status: role.status,
})),
status: user.status,
timezone: user.timezone,
updateTime: user.updateTime,
username: user.username,
};
}
private async findRolesByIds(ids: string[]) {
const normalizedIds = ids.map((id) => String(id)).filter(Boolean);
if (!normalizedIds.length) return [];
return this.roleRepository.find({
where: normalizedIds.map((id) => ({
id,
isDeleted: false,
})),
});
}
private async collectDeptIds(deptId: string) {
if (deptId === '0') return ['0'];
const depts = await this.deptRepository.find({
where: {
isDeleted: false,
},
});
const result = new Set<string>([deptId]);
let changed = true;
while (changed) {
changed = false;
for (const dept of depts) {
if (result.has(String(dept.pid)) && !result.has(dept.id)) {
result.add(dept.id);
changed = true;
}
}
}
return Array.from(result);
}
private async findActiveUser(id: string) {
const user = await this.userRepository.findOne({
relations: ['roles', 'dept'],
where: {
id,
isDeleted: false,
},
});
if (!user) throwVbenError('用户不存在', HttpStatus.BAD_REQUEST);
return user;
}
private async ensureUsernameAvailable(username: string, ignoreId?: string) {
if (!username.trim()) {
throwVbenError('用户名不能为空', HttpStatus.BAD_REQUEST);
}
const where = ignoreId
? {
id: Not(ignoreId),
username,
}
: {
username,
};
const existing = await this.userRepository.findOne({ where });
if (existing) {
throwVbenError('用户名已存在', HttpStatus.BAD_REQUEST);
}
}
}