feat(core-edu): 修复服务启动并打通考试作业成绩端到端链路

database.ts 导出 db 常量替代 getDb()
env.ts JWT_SECRET 改 optional 并新增 DEV_MODE
kafka.ts connectKafka 加 try/catch 不阻塞启动
main.ts 去全局前缀 connectKafka 改非阻塞
app.module 移除未用模块加 HealthModule
controller 路由去前缀去 UseGuards 从 x-user-id 读身份
service datetime ISO 字符串转 Date 修复 drizzle 错误
修正相对 import 路径
health/lifecycle 改用 Drizzle 原生查询
新增 core-edu-init.sql 初始化 4 张表

端到端验证: exams/homework/grades 全部 201/200
Outbox 事件正确写入 core_edu_outbox 表
This commit is contained in:
SpecialX
2026-07-09 08:02:22 +08:00
parent 2c7afe59ef
commit 4533da6484
16 changed files with 348 additions and 309 deletions

View File

@@ -6,52 +6,64 @@ import {
Param,
Post,
Put,
UseGuards,
} from '@nestjs/common';
import { ExamsService, type CreateExamInput, type UpdateExamInput } from './exams.service.js';
import { PermissionGuard, Permissions } from '../../middleware/permission.guard.js';
Req,
} from "@nestjs/common";
import type { Request } from "express";
import {
ExamsService,
type CreateExamInput,
type UpdateExamInput,
} from "./exams.service.js";
interface SuccessResponse<T> {
data: T;
timestamp: string;
}
@Controller('api/v1/exams')
@Controller("exams")
export class ExamsController {
constructor(private readonly examsService: ExamsService) {}
@Post()
@UseGuards(new PermissionGuard(Permissions.EXAM_CREATE))
async create(@Body() body: CreateExamInput): Promise<SuccessResponse<{ id: string }>> {
const result = await this.examsService.createExam(body);
return { data: result, timestamp: new Date().toISOString() };
async create(
@Body() body: CreateExamInput,
@Req() req: Request,
): Promise<{ success: true; data: { id: string } }> {
const userId = req.headers["x-user-id"] as string;
const result = await this.examsService.createExam({
...body,
createdBy: userId,
});
return { success: true, data: result };
}
@Get(':id')
@UseGuards(new PermissionGuard(Permissions.EXAM_READ))
async findOne(@Param('id') id: string): Promise<SuccessResponse<Awaited<ReturnType<ExamsService['getExam']>>>> {
@Get(":id")
async findOne(@Param("id") id: string): Promise<{
success: true;
data: Awaited<ReturnType<ExamsService["getExam"]>>;
}> {
const data = await this.examsService.getExam(id);
return { data, timestamp: new Date().toISOString() };
return { success: true, data };
}
@Get('class/:classId')
@UseGuards(new PermissionGuard(Permissions.EXAM_READ))
async listByClass(@Param('classId') classId: string): Promise<SuccessResponse<Awaited<ReturnType<ExamsService['listExamsByClass']>>>> {
@Get("class/:classId")
async listByClass(@Param("classId") classId: string): Promise<{
success: true;
data: Awaited<ReturnType<ExamsService["listExamsByClass"]>>;
}> {
const data = await this.examsService.listExamsByClass(classId);
return { data, timestamp: new Date().toISOString() };
return { success: true, data };
}
@Put(':id')
@UseGuards(new PermissionGuard(Permissions.EXAM_UPDATE))
async update(@Param('id') id: string, @Body() body: UpdateExamInput): Promise<SuccessResponse<{ success: true }>> {
@Put(":id")
async update(
@Param("id") id: string,
@Body() body: UpdateExamInput,
): Promise<{ success: true; data: { success: true } }> {
await this.examsService.updateExam(id, body);
return { data: { success: true }, timestamp: new Date().toISOString() };
return { success: true, data: { success: true } };
}
@Delete(':id')
@UseGuards(new PermissionGuard(Permissions.EXAM_DELETE))
async remove(@Param('id') id: string): Promise<SuccessResponse<{ success: true }>> {
@Delete(":id")
async remove(
@Param("id") id: string,
): Promise<{ success: true; data: { success: true } }> {
await this.examsService.deleteExam(id);
return { data: { success: true }, timestamp: new Date().toISOString() };
return { success: true, data: { success: true } };
}
}