docs: 完善项目说明和注释
This commit is contained in:
+99
@@ -0,0 +1,99 @@
|
||||
import Fastify from "fastify";
|
||||
import { ZodError } from "zod";
|
||||
import { pingDatabase } from "./db/pool";
|
||||
import { catalogRoutes } from "./modules/catalog/catalog.controller";
|
||||
import { employeeRoutes } from "./modules/employees/employee.controller";
|
||||
import { HttpError } from "./shared/http-error";
|
||||
import { ok } from "./shared/response";
|
||||
|
||||
// createApp 只创建并配置 Fastify 实例,不直接监听端口。
|
||||
// 这样 server.ts 可以负责启动服务,测试代码也可以单独创建 app 实例。
|
||||
export function createApp() {
|
||||
const app = Fastify({
|
||||
logger: true,
|
||||
});
|
||||
|
||||
// 前端 Axios 默认会给 DELETE 带上 application/json;空 body 不应被当作服务端异常。
|
||||
app.addContentTypeParser(
|
||||
"application/json",
|
||||
{ parseAs: "string" },
|
||||
(_request, body, done) => {
|
||||
if (body === "") {
|
||||
done(null, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
done(null, JSON.parse(body as string));
|
||||
} catch (error) {
|
||||
done(error as Error, undefined);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// 健康检查接口,供负载均衡器和监控系统使用。
|
||||
app.get("/health", async () => {
|
||||
await pingDatabase();
|
||||
|
||||
return ok({
|
||||
status: "ok",
|
||||
database: "up",
|
||||
now: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
// 注册业务路由,所有接口都以 /api 开头,便于区分静态资源和 API 请求。
|
||||
app.register(catalogRoutes, { prefix: "/api" });
|
||||
// 员工管理相关接口,包含员工的增删改查和状态更新等功能。
|
||||
app.register(employeeRoutes, { prefix: "/api" });
|
||||
|
||||
// 全局错误处理器,捕获所有未处理的异常,并根据错误类型返回合适的 HTTP 状态码和错误信息。
|
||||
app.setErrorHandler((error, request, reply) => {
|
||||
if (error instanceof ZodError) {
|
||||
return reply.code(400).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: "VALIDATION_ERROR",
|
||||
message: "请求参数不合法",
|
||||
details: error.issues,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (error instanceof HttpError) {
|
||||
return reply.code(error.statusCode).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: error.code,
|
||||
message: error.message,
|
||||
details: error.details,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const mysqlCode = (error as { code?: string }).code;
|
||||
|
||||
// 数据库唯一索引冲突也转成统一的业务错误响应,避免把 MySQL 原始错误直接暴露给调用方。
|
||||
if (mysqlCode === "ER_DUP_ENTRY") {
|
||||
return reply.code(409).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: "CONFLICT",
|
||||
message: "数据已存在,请检查唯一字段",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
request.log.error({ error }, "未处理的服务异常");
|
||||
|
||||
return reply.code(500).send({
|
||||
success: false,
|
||||
error: {
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "服务器内部错误",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
Reference in New Issue
Block a user