NestJS (6 services): implement @RequirePermission decorator with SetMetadata+Reflector, register APP_GUARD globally, fix as assertions to type guards, add explicit return types, fix import type for express, fix /metrics implicit any, replace native Error with ApplicationError, remove typeorm remnants, register LifecycleService. teacher-bff: add logger, ApplicationError, GlobalErrorFilter, forward real userId to downstream, log downstream failures, migrate health controller to shared/health. Go (2 services): interface to any, doc comments, CORS dev whitelist, JWT secret fail-fast, push-gateway internal API auth, metrics and readyz endpoints, remove dead code. Python (2 services): lifespan return type, dev_mode to bool, data-ana APIRouter, ai POST body model, ClickHouse async wrapping.
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import {
|
||
Body,
|
||
Controller,
|
||
Delete,
|
||
Get,
|
||
Param,
|
||
Post,
|
||
Put,
|
||
Req,
|
||
} from "@nestjs/common";
|
||
import { ClassesService } from "./classes.service.js";
|
||
import { createClassSchema, updateClassSchema } from "./classes.dto.js";
|
||
import {
|
||
Permissions,
|
||
RequirePermission,
|
||
} from "../middleware/permission.guard.js";
|
||
import type { AuthenticatedRequest } from "../middleware/auth.middleware.js";
|
||
import type { Class } from "./classes.schema.js";
|
||
|
||
interface ClassResponse {
|
||
id: string;
|
||
name: string;
|
||
gradeId: string;
|
||
headTeacherId: string | null;
|
||
description: string | null;
|
||
createdAt: number;
|
||
updatedAt: number;
|
||
}
|
||
|
||
interface SuccessResponse<T> {
|
||
success: true;
|
||
data: T;
|
||
}
|
||
|
||
@Controller("classes")
|
||
export class ClassesController {
|
||
constructor(private readonly service: ClassesService) {}
|
||
|
||
@Post()
|
||
@RequirePermission(Permissions.CLASSES_CREATE)
|
||
async create(@Body() body: unknown): Promise<SuccessResponse<ClassResponse>> {
|
||
const dto = createClassSchema.parse(body);
|
||
const result = await this.service.create(dto);
|
||
return { success: true as const, data: this.toResponse(result) };
|
||
}
|
||
|
||
@Get()
|
||
@RequirePermission(Permissions.CLASSES_READ)
|
||
async list(
|
||
@Req() req: AuthenticatedRequest,
|
||
): Promise<SuccessResponse<ClassResponse[]>> {
|
||
const gradeIdRaw = req.query.gradeId;
|
||
const gradeId = typeof gradeIdRaw === "string" ? gradeIdRaw : undefined;
|
||
const result = await this.service.list(gradeId);
|
||
return {
|
||
success: true as const,
|
||
data: result.map((c) => this.toResponse(c)),
|
||
};
|
||
}
|
||
|
||
@Get(":id")
|
||
@RequirePermission(Permissions.CLASSES_READ)
|
||
async getById(
|
||
@Param("id") id: string,
|
||
): Promise<SuccessResponse<ClassResponse>> {
|
||
const result = await this.service.getById(id);
|
||
return { success: true as const, data: this.toResponse(result) };
|
||
}
|
||
|
||
@Put(":id")
|
||
@RequirePermission(Permissions.CLASSES_UPDATE)
|
||
async update(
|
||
@Param("id") id: string,
|
||
@Body() body: unknown,
|
||
): Promise<SuccessResponse<ClassResponse>> {
|
||
const dto = updateClassSchema.parse(body);
|
||
const result = await this.service.update(id, dto);
|
||
return { success: true as const, data: this.toResponse(result) };
|
||
}
|
||
|
||
@Delete(":id")
|
||
@RequirePermission(Permissions.CLASSES_DELETE)
|
||
async delete(@Param("id") id: string): Promise<{ success: true }> {
|
||
await this.service.delete(id);
|
||
return { success: true as const };
|
||
}
|
||
|
||
// 修复 #2: 消除 any,使用 Class 类型;drizzle timestamp 返回 Date,统一转毫秒数(epoch ms)
|
||
private toResponse(c: Class): ClassResponse {
|
||
return {
|
||
id: c.id,
|
||
name: c.name,
|
||
gradeId: c.gradeId,
|
||
headTeacherId: c.headTeacherId ?? null,
|
||
description: c.description ?? null,
|
||
createdAt: c.createdAt.getTime(),
|
||
updatedAt: c.updatedAt.getTime(),
|
||
};
|
||
}
|
||
}
|