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:
@@ -1,24 +1,16 @@
|
||||
import { drizzle } from 'drizzle-orm/mysql2';
|
||||
import mysql from 'mysql2/promise';
|
||||
import { env } from './env.js';
|
||||
import { drizzle } from "drizzle-orm/mysql2";
|
||||
import mysql from "mysql2/promise";
|
||||
import { env } from "./env.js";
|
||||
|
||||
let pool: mysql.Pool | null = null;
|
||||
const pool = mysql.createPool({
|
||||
uri: env.DATABASE_URL,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
});
|
||||
|
||||
export function getDb() {
|
||||
if (!pool) {
|
||||
pool = mysql.createPool({
|
||||
uri: env.DATABASE_URL,
|
||||
waitForConnections: true,
|
||||
connectionLimit: 10,
|
||||
queueLimit: 0,
|
||||
});
|
||||
}
|
||||
return drizzle(pool);
|
||||
}
|
||||
export const db = drizzle(pool);
|
||||
|
||||
export async function closeDb(): Promise<void> {
|
||||
if (pool) {
|
||||
await pool.end();
|
||||
pool = null;
|
||||
}
|
||||
await pool.end();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import { z } from 'zod';
|
||||
import { z } from "zod";
|
||||
|
||||
const envSchema = z.object({
|
||||
PORT: z.string().default('3004'),
|
||||
PORT: z.string().default("3004"),
|
||||
DATABASE_URL: z.string().url(),
|
||||
REDIS_URL: z.string().url().optional(),
|
||||
JWT_SECRET: z.string(),
|
||||
JWT_ISSUER: z.string().default('next-edu-cloud'),
|
||||
KAFKA_BROKERS: z.string().default('localhost:9092'),
|
||||
JWT_SECRET: z.string().optional(),
|
||||
JWT_ISSUER: z.string().default("next-edu-cloud"),
|
||||
KAFKA_BROKERS: z.string().default("localhost:9092"),
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: z.string().url().optional(),
|
||||
LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),
|
||||
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
|
||||
LOG_LEVEL: z
|
||||
.enum(["fatal", "error", "warn", "info", "debug", "trace"])
|
||||
.default("info"),
|
||||
NODE_ENV: z
|
||||
.enum(["development", "production", "test"])
|
||||
.default("development"),
|
||||
DEV_MODE: z.string().optional().default("false"),
|
||||
});
|
||||
|
||||
export type Env = z.infer<typeof envSchema>;
|
||||
@@ -17,8 +22,11 @@ export type Env = z.infer<typeof envSchema>;
|
||||
export function loadEnv(): Env {
|
||||
const result = envSchema.safeParse(process.env);
|
||||
if (!result.success) {
|
||||
console.error('❌ Invalid environment variables:', result.error.flatten().fieldErrors);
|
||||
throw new Error('Invalid environment configuration');
|
||||
console.error(
|
||||
"❌ Invalid environment variables:",
|
||||
result.error.flatten().fieldErrors,
|
||||
);
|
||||
throw new Error("Invalid environment configuration");
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,29 @@
|
||||
import { Kafka } from 'kafkajs';
|
||||
import { env } from './env.js';
|
||||
import { Kafka } from "kafkajs";
|
||||
import { env } from "./env.js";
|
||||
|
||||
export const kafka = new Kafka({
|
||||
brokers: env.KAFKA_BROKERS.split(','),
|
||||
clientId: 'core-edu-service',
|
||||
brokers: env.KAFKA_BROKERS.split(","),
|
||||
clientId: "core-edu-service",
|
||||
});
|
||||
|
||||
export const producer = kafka.producer({
|
||||
idempotent: true,
|
||||
transactionalId: 'core-edu-tx',
|
||||
transactionalId: "core-edu-tx",
|
||||
});
|
||||
|
||||
export const consumer = kafka.consumer({ groupId: 'core-edu-group' });
|
||||
export const consumer = kafka.consumer({ groupId: "core-edu-group" });
|
||||
|
||||
export async function connectKafka(): Promise<void> {
|
||||
await producer.connect();
|
||||
await consumer.connect();
|
||||
console.log('Kafka connected');
|
||||
try {
|
||||
await producer.connect();
|
||||
await consumer.connect();
|
||||
console.log("Kafka connected");
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"Kafka connect failed, running without Kafka:",
|
||||
err instanceof Error ? err.message : String(err),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function disconnectKafka(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user