iconsulting/packages/services/evolution-service/src/admin/admin.service.ts

345 lines
7.9 KiB
TypeScript

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as bcrypt from 'bcrypt';
import * as jwt from 'jsonwebtoken';
import { ConfigService } from '@nestjs/config';
import { AdminORM } from '../infrastructure/database/entities/admin.orm';
import { v4 as uuidv4 } from 'uuid';
/**
* 管理员角色
*/
export enum AdminRole {
SUPER_ADMIN = 'SUPER_ADMIN',
ADMIN = 'ADMIN',
OPERATOR = 'OPERATOR',
VIEWER = 'VIEWER',
}
/**
* 角色权限映射
*/
const ROLE_PERMISSIONS: Record<AdminRole, string[]> = {
[AdminRole.SUPER_ADMIN]: ['*'],
[AdminRole.ADMIN]: [
'knowledge:*',
'experience:*',
'user:read',
'conversation:read',
'statistics:*',
'admin:read',
],
[AdminRole.OPERATOR]: [
'knowledge:read',
'knowledge:create',
'knowledge:update',
'experience:read',
'experience:approve',
'user:read',
'conversation:read',
'statistics:read',
],
[AdminRole.VIEWER]: [
'knowledge:read',
'experience:read',
'user:read',
'conversation:read',
'statistics:read',
],
};
/**
* 登录结果
*/
export interface LoginResult {
admin: {
id: string;
username: string;
name: string;
role: string;
permissions: string[];
};
token: string;
expiresIn: number;
}
/**
* 管理员服务
*/
@Injectable()
export class AdminService {
private readonly jwtSecret: string;
private readonly jwtExpiresIn: number = 24 * 60 * 60; // 24小时
constructor(
@InjectRepository(AdminORM)
private adminRepo: Repository<AdminORM>,
private configService: ConfigService,
) {
this.jwtSecret = this.configService.get('JWT_SECRET') || 'iconsulting-secret-key';
}
/**
* 管理员登录
*/
async login(username: string, password: string, ip?: string): Promise<LoginResult> {
const admin = await this.adminRepo.findOne({ where: { username } });
if (!admin || !admin.isActive) {
throw new Error('用户名或密码错误');
}
const isPasswordValid = await bcrypt.compare(password, admin.passwordHash);
if (!isPasswordValid) {
throw new Error('用户名或密码错误');
}
// 更新登录信息
admin.lastLoginAt = new Date();
admin.lastLoginIp = ip;
await this.adminRepo.save(admin);
// 生成Token
const token = jwt.sign(
{
sub: admin.id,
username: admin.username,
role: admin.role,
},
this.jwtSecret,
{ expiresIn: this.jwtExpiresIn },
);
// 获取权限
const permissions = this.getPermissions(admin.role as AdminRole, admin.permissions);
return {
admin: {
id: admin.id,
username: admin.username,
name: admin.name,
role: admin.role,
permissions,
},
token,
expiresIn: this.jwtExpiresIn,
};
}
/**
* 验证Token
*/
async verifyToken(token: string): Promise<{
valid: boolean;
admin?: {
id: string;
username: string;
role: string;
permissions: string[];
};
}> {
try {
const decoded = jwt.verify(token, this.jwtSecret) as {
sub: string;
username: string;
role: string;
};
const admin = await this.adminRepo.findOne({ where: { id: decoded.sub } });
if (!admin || !admin.isActive) {
return { valid: false };
}
const permissions = this.getPermissions(admin.role as AdminRole, admin.permissions);
return {
valid: true,
admin: {
id: admin.id,
username: admin.username,
role: admin.role,
permissions,
},
};
} catch {
return { valid: false };
}
}
/**
* 创建管理员
*/
async createAdmin(params: {
username: string;
password: string;
name: string;
email?: string;
phone?: string;
role: AdminRole;
}): Promise<AdminORM> {
// 检查用户名是否存在
const existing = await this.adminRepo.findOne({
where: { username: params.username },
});
if (existing) {
throw new Error('用户名已存在');
}
// 加密密码
const passwordHash = await bcrypt.hash(params.password, 10);
const admin = this.adminRepo.create({
id: uuidv4(),
username: params.username,
passwordHash,
name: params.name,
email: params.email,
phone: params.phone,
role: params.role,
permissions: ROLE_PERMISSIONS[params.role],
isActive: true,
});
await this.adminRepo.save(admin);
return admin;
}
/**
* 获取管理员列表
*/
async listAdmins(options?: {
role?: AdminRole;
isActive?: boolean;
page?: number;
pageSize?: number;
}): Promise<{
items: AdminORM[];
total: number;
}> {
const page = options?.page || 1;
const pageSize = options?.pageSize || 20;
const query = this.adminRepo.createQueryBuilder('admin');
if (options?.role) {
query.andWhere('admin.role = :role', { role: options.role });
}
if (options?.isActive !== undefined) {
query.andWhere('admin.isActive = :active', { active: options.isActive });
}
query.orderBy('admin.createdAt', 'DESC');
const [items, total] = await query
.skip((page - 1) * pageSize)
.take(pageSize)
.getManyAndCount();
return { items, total };
}
/**
* 更新管理员
*/
async updateAdmin(
adminId: string,
params: {
name?: string;
email?: string;
phone?: string;
role?: AdminRole;
isActive?: boolean;
},
): Promise<AdminORM> {
const admin = await this.adminRepo.findOne({ where: { id: adminId } });
if (!admin) {
throw new Error('管理员不存在');
}
if (params.name) admin.name = params.name;
if (params.email !== undefined) admin.email = params.email;
if (params.phone !== undefined) admin.phone = params.phone;
if (params.role) {
admin.role = params.role;
admin.permissions = ROLE_PERMISSIONS[params.role];
}
if (params.isActive !== undefined) admin.isActive = params.isActive;
await this.adminRepo.save(admin);
return admin;
}
/**
* 修改密码
*/
async changePassword(
adminId: string,
oldPassword: string,
newPassword: string,
): Promise<void> {
const admin = await this.adminRepo.findOne({ where: { id: adminId } });
if (!admin) {
throw new Error('管理员不存在');
}
const isOldPasswordValid = await bcrypt.compare(oldPassword, admin.passwordHash);
if (!isOldPasswordValid) {
throw new Error('原密码错误');
}
admin.passwordHash = await bcrypt.hash(newPassword, 10);
await this.adminRepo.save(admin);
}
/**
* 重置密码(超管功能)
*/
async resetPassword(adminId: string, newPassword: string): Promise<void> {
const admin = await this.adminRepo.findOne({ where: { id: adminId } });
if (!admin) {
throw new Error('管理员不存在');
}
admin.passwordHash = await bcrypt.hash(newPassword, 10);
await this.adminRepo.save(admin);
}
/**
* 检查权限
*/
hasPermission(adminPermissions: string[], requiredPermission: string): boolean {
// 超管拥有所有权限
if (adminPermissions.includes('*')) {
return true;
}
// 完全匹配
if (adminPermissions.includes(requiredPermission)) {
return true;
}
// 通配符匹配 (如 knowledge:* 匹配 knowledge:read)
const [resource, action] = requiredPermission.split(':');
if (adminPermissions.includes(`${resource}:*`)) {
return true;
}
return false;
}
/**
* 获取管理员权限列表
*/
private getPermissions(role: AdminRole, customPermissions?: string[]): string[] {
const rolePermissions = ROLE_PERMISSIONS[role] || [];
if (customPermissions && customPermissions.length > 0) {
return [...new Set([...rolePermissions, ...customPermissions])];
}
return rolePermissions;
}
}