R19/R20: 项目主域进 OA 并打通报销差旅周报待办

这个提交包含在:
2026-03-22 08:34:18 +08:00
父节点 0235ccb968
当前提交 3f9738ac9c
共修改 28 个文件,包含 3236 行新增和 21 行删除
+11 -2
查看文件
@@ -19,6 +19,7 @@ const CREATE_SCHEMA = z.object({
dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
priority: z.enum(["low", "normal", "high"]).optional(),
source: z.enum(["manual", "weekly_report"]).optional(),
projectId: UUID_SCHEMA.nullable().optional(),
}).strict();
const PATCH_SCHEMA = z.object({
title: z.string().trim().min(1).max(200).optional(),
@@ -27,6 +28,7 @@ const PATCH_SCHEMA = z.object({
dueDate: z.string().regex(/^\d{4}-\d{2}-\d{2}$/).nullable().optional(),
priority: z.enum(["low", "normal", "high"]).optional(),
status: z.enum(["todo", "doing", "done", "cancelled"]).optional(),
projectId: UUID_SCHEMA.nullable().optional(),
}).strict().refine((value) => Object.keys(value).length > 0, { message: "empty_patch" });
function buildValidationError(details, statusCode = 400) {
@@ -60,11 +62,13 @@ function mapTodo(row) {
priority: row.priority,
status: row.status,
source: row.source,
projectId: row.project_id ?? null,
createdAt: row.created_at?.toISOString?.() ?? null,
updatedAt: row.updated_at?.toISOString?.() ?? null,
completedAt: row.completed_at?.toISOString?.() ?? null,
assignee: row.assignee ? { id: row.assignee.id, employeeNo: row.assignee.employee_no, name: row.assignee.name } : null,
creator: row.creator ? { id: row.creator.id, employeeNo: row.creator.employee_no, name: row.creator.name } : null,
project: row.project ? { id: row.project.id, projectNo: row.project.project_no, name: row.project.name } : null,
};
}
@@ -122,6 +126,7 @@ async function todosRoutes(fastify) {
include: {
assignee: { select: { id: true, employee_no: true, name: true } },
creator: { select: { id: true, employee_no: true, name: true } },
project: { select: { id: true, project_no: true, name: true } },
},
});
return sendData(reply, request, { items: rows.map(mapTodo) });
@@ -144,10 +149,12 @@ async function todosRoutes(fastify) {
priority: body.priority ?? "normal",
status: "todo",
source: body.source ?? "manual",
project_id: body.projectId ?? null,
},
include: {
assignee: { select: { id: true, employee_no: true, name: true } },
creator: { select: { id: true, employee_no: true, name: true } },
project: { select: { id: true, project_no: true, name: true } },
},
});
@@ -188,11 +195,13 @@ async function todosRoutes(fastify) {
...(body.assigneeEmployeeId !== undefined ? { assignee_employee_id: body.assigneeEmployeeId } : {}),
...(body.dueDate !== undefined ? { due_date: body.dueDate ? new Date(`${body.dueDate}T00:00:00.000Z`) : null } : {}),
...(body.priority !== undefined ? { priority: body.priority } : {}),
...(body.projectId !== undefined ? { project_id: body.projectId ?? null } : {}),
...(body.status !== undefined ? { status: body.status, completed_at: body.status === "done" ? new Date() : body.status === "cancelled" ? current.completed_at : null } : {}),
},
include: {
assignee: { select: { id: true, employee_no: true, name: true } },
creator: { select: { id: true, employee_no: true, name: true } },
project: { select: { id: true, project_no: true, name: true } },
},
});
return sendData(reply, request, { item: mapTodo(updated) });
@@ -211,7 +220,7 @@ async function todosRoutes(fastify) {
const updated = await fastify.prisma.todoItem.update({
where: { id: todoId },
data: { status: "done", completed_at: new Date() },
include: { assignee: { select: { id: true, employee_no: true, name: true } }, creator: { select: { id: true, employee_no: true, name: true } } },
include: { assignee: { select: { id: true, employee_no: true, name: true } }, creator: { select: { id: true, employee_no: true, name: true } }, project: { select: { id: true, project_no: true, name: true } } },
});
return sendData(reply, request, { item: mapTodo(updated) });
});
@@ -229,7 +238,7 @@ async function todosRoutes(fastify) {
const updated = await fastify.prisma.todoItem.update({
where: { id: todoId },
data: { status: "cancelled", completed_at: null },
include: { assignee: { select: { id: true, employee_no: true, name: true } }, creator: { select: { id: true, employee_no: true, name: true } } },
include: { assignee: { select: { id: true, employee_no: true, name: true } }, creator: { select: { id: true, employee_no: true, name: true } }, project: { select: { id: true, project_no: true, name: true } } },
});
return sendData(reply, request, { item: mapTodo(updated) });
});