Files
ai-learning-platform/backend/src/modules/admin/admin.service.ts
T
yuzhiran-dev 538de50bb1 注册支持用户名/手机号/邮箱 + 镜像站部署脚本 + AI 助手 Tool Calling 重构
- Prisma User 模型新增 username 字段(唯一索引)
- 注册先查重复再创建,返回友好中文提示(非 500)
- 登录支持用户名/手机号/邮箱三种方式
- 前端注册表单增加用户名输入框,预校验 2-20 位格式
- 新增 scripts/deploy.sh:一键构建并部署主站+镜像站+重启后端+重载 Nginx
- 镜像站 www.yuzhiran.com.cn Nginx 配置与主站同步
- AI 助手架构升级:用户端/管理后台均采用完整 Tool Calling 架构
- 新增 UserAiAssistantService(18 工具)+ AiAssistantController
- admin 助手新增 search + mark-all-notifications-read 工具
- 修复注册 500 错误:catch Prisma P2002 → BadRequestException
- Baidu Analytics Script 注入 root layout
2026-06-01 23:14:42 +08:00

60 lines
1.9 KiB
TypeScript
Executable File

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcryptjs';
import { PrismaService } from '../../prisma/prisma.service';
@Injectable()
export class AdminService {
constructor(
private prisma: PrismaService,
private jwtService: JwtService,
) {}
async login(username: string, password: string) {
const admin = await this.prisma.adminUser.findUnique({
where: { username },
include: { role: true },
});
if (!admin || admin.status !== 'ACTIVE') {
throw new UnauthorizedException('管理员账号不可用');
}
const isValid = await bcrypt.compare(password, admin.passwordHash);
if (!isValid) {
throw new UnauthorizedException('密码错误');
}
await this.prisma.adminUser.update({
where: { id: admin.id },
data: { lastLoginAt: new Date() },
});
const token = this.jwtService.sign(
{ sub: admin.id, type: 'admin' },
{ expiresIn: '7d' },
);
return { token, id: admin.id, username: admin.username, role: admin.role?.name };
}
async getDashboard() {
const [userCount, courseCount, contentCount, promptCount, orderCount] = await Promise.all([
this.prisma.user.count({ where: { deletedAt: null } }),
this.prisma.course.count({ where: { deletedAt: null } }),
this.prisma.content.count({ where: { deletedAt: null, status: 'PUBLISHED' } }),
this.prisma.prompt.count({ where: { deletedAt: null, status: 'PUBLISHED' } }),
this.prisma.order.count(),
]);
return {
stats: { userCount, courseCount, contentCount, promptCount, orderCount },
};
}
async logAction(adminId: number, action: string, target?: string, detail?: string, ip?: string) {
return this.prisma.adminLog.create({
data: { adminId, action, target, detail, ip },
});
}
}