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

@@ -1,22 +1,23 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module.js';
import { env } from './config/env.js';
import { connectKafka, disconnectKafka } from './config/kafka.js';
import { outboxPublisher } from './shared/outbox/outbox.publisher.js';
import { GlobalErrorFilter } from './shared/errors/global-error.filter.js';
import { initTracer, shutdownTracer } from './shared/observability/tracer.js';
import { logger } from './shared/observability/logger.js';
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module.js";
import { env } from "./config/env.js";
import { connectKafka, disconnectKafka } from "./config/kafka.js";
import { outboxPublisher } from "./shared/outbox/outbox.publisher.js";
import { GlobalErrorFilter } from "./shared/errors/global-error.filter.js";
import { initTracer, shutdownTracer } from "./shared/observability/tracer.js";
import { logger } from "./shared/observability/logger.js";
async function bootstrap(): Promise<void> {
initTracer();
const app = await NestFactory.create(AppModule, { bufferLogs: true });
app.setGlobalPrefix('api');
app.useGlobalFilters(new GlobalErrorFilter());
app.enableShutdownHooks();
// Connect Kafka producer/consumer before starting the outbox publisher
await connectKafka();
// Connect Kafka producer/consumer before starting the outbox publisher.
// Non-blocking: if Kafka is unavailable, service still starts; outbox
// publisher will retry sends and messages stay pending until Kafka recovers.
void connectKafka();
// Start the transactional outbox publisher - polls pending messages
// and publishes them to Kafka topics defined in TOPIC_MAP.
@@ -24,12 +25,12 @@ async function bootstrap(): Promise<void> {
await app.listen(env.PORT);
logger.info(
{ port: env.PORT, service: 'core-edu' },
'CoreEdu service is listening',
{ port: env.PORT, service: "core-edu" },
"CoreEdu service is listening",
);
process.on('SIGTERM', async () => {
logger.info('SIGTERM received, shutting down gracefully...');
process.on("SIGTERM", async () => {
logger.info("SIGTERM received, shutting down gracefully...");
await outboxPublisher.stop();
await disconnectKafka();
await shutdownTracer();
@@ -37,8 +38,8 @@ async function bootstrap(): Promise<void> {
process.exit(0);
});
process.on('SIGINT', async () => {
logger.info('SIGINT received, shutting down gracefully...');
process.on("SIGINT", async () => {
logger.info("SIGINT received, shutting down gracefully...");
await outboxPublisher.stop();
await disconnectKafka();
await shutdownTracer();