R23-1/R23-2/R23-4:完成项目沟通回填与收口治理后端
这个提交包含在:
+45
-36
@@ -44,6 +44,23 @@ async function evaluateOptionalFlag(fastify, request, flagKey) {
|
||||
return gate.enabled;
|
||||
}
|
||||
|
||||
function canReadAllProjects(fastify, employee) {
|
||||
return fastify.hasPermission(employee, "project.view_all") || fastify.hasPermission(employee, "project.manage") || fastify.hasPermission(employee, "*");
|
||||
}
|
||||
|
||||
function projectScopeWhere(fastify, employee) {
|
||||
if (canReadAllProjects(fastify, employee)) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
OR: [
|
||||
{ owner_employee_id: employee.id },
|
||||
{ manager_employee_id: employee.id },
|
||||
{ members: { some: { employee_id: employee.id, left_at: null } } },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function resolveWeekStart(rawDateString) {
|
||||
const date = parseDateOnlyString(rawDateString);
|
||||
const day = date.getUTCDay() || 7;
|
||||
@@ -190,6 +207,8 @@ async function portalRoutes(fastify) {
|
||||
todoBoard: await evaluateOptionalFlag(fastify, request, FEATURE_FLAG_KEYS.todoBoard),
|
||||
paymentBatch: await evaluateOptionalFlag(fastify, request, FEATURE_FLAG_KEYS.paymentBatch),
|
||||
projectDomain: await evaluateOptionalFlag(fastify, request, FEATURE_FLAG_KEYS.projectDomain),
|
||||
projectCommunicationLog: await evaluateOptionalFlag(fastify, request, FEATURE_FLAG_KEYS.projectCommunicationLog),
|
||||
projectClosureChecklist: await evaluateOptionalFlag(fastify, request, FEATURE_FLAG_KEYS.projectClosureChecklist),
|
||||
};
|
||||
|
||||
const [
|
||||
@@ -210,6 +229,8 @@ async function portalRoutes(fastify) {
|
||||
overdueProjectMilestoneCount,
|
||||
highPriorityProjectTaskCount,
|
||||
openProjectRiskCount,
|
||||
projectFollowUpDueTodayCount,
|
||||
closurePendingProjectCount,
|
||||
] = await Promise.all([
|
||||
fastify.prisma.workflowTask.count({ where: { status: "pending", OR: assigneeFilters } }),
|
||||
fastify.prisma.reimbursementForm.count({ where: { OR: [{ owner_id: request.employee.id }, { submitter_id: request.employee.id }] } }),
|
||||
@@ -270,15 +291,7 @@ async function portalRoutes(fastify) {
|
||||
: Promise.resolve(0),
|
||||
featureEnabled.projectDomain
|
||||
? fastify.prisma.project.count({
|
||||
where: fastify.hasPermission(request.employee, "project.view_all") || fastify.hasPermission(request.employee, "project.manage") || fastify.hasPermission(request.employee, "*")
|
||||
? {}
|
||||
: {
|
||||
OR: [
|
||||
{ owner_employee_id: request.employee.id },
|
||||
{ manager_employee_id: request.employee.id },
|
||||
{ members: { some: { employee_id: request.employee.id, left_at: null } } },
|
||||
],
|
||||
},
|
||||
where: projectScopeWhere(fastify, request.employee) ?? {},
|
||||
})
|
||||
: Promise.resolve(0),
|
||||
featureEnabled.projectDomain
|
||||
@@ -286,15 +299,7 @@ async function portalRoutes(fastify) {
|
||||
where: {
|
||||
status: { in: ["pending", "in_progress", "delayed"] },
|
||||
due_date: { lt: new Date() },
|
||||
project: fastify.hasPermission(request.employee, "project.view_all") || fastify.hasPermission(request.employee, "project.manage") || fastify.hasPermission(request.employee, "*")
|
||||
? undefined
|
||||
: {
|
||||
OR: [
|
||||
{ owner_employee_id: request.employee.id },
|
||||
{ manager_employee_id: request.employee.id },
|
||||
{ members: { some: { employee_id: request.employee.id, left_at: null } } },
|
||||
],
|
||||
},
|
||||
project: projectScopeWhere(fastify, request.employee),
|
||||
},
|
||||
})
|
||||
: Promise.resolve(0),
|
||||
@@ -303,15 +308,7 @@ async function portalRoutes(fastify) {
|
||||
where: {
|
||||
status: { in: ["todo", "doing", "blocked"] },
|
||||
priority: { in: ["high", "urgent"] },
|
||||
project: fastify.hasPermission(request.employee, "project.view_all") || fastify.hasPermission(request.employee, "project.manage") || fastify.hasPermission(request.employee, "*")
|
||||
? undefined
|
||||
: {
|
||||
OR: [
|
||||
{ owner_employee_id: request.employee.id },
|
||||
{ manager_employee_id: request.employee.id },
|
||||
{ members: { some: { employee_id: request.employee.id, left_at: null } } },
|
||||
],
|
||||
},
|
||||
project: projectScopeWhere(fastify, request.employee),
|
||||
},
|
||||
})
|
||||
: Promise.resolve(0),
|
||||
@@ -319,18 +316,28 @@ async function portalRoutes(fastify) {
|
||||
? fastify.prisma.projectRisk.count({
|
||||
where: {
|
||||
status: { not: "closed" },
|
||||
project: fastify.hasPermission(request.employee, "project.view_all") || fastify.hasPermission(request.employee, "project.manage") || fastify.hasPermission(request.employee, "*")
|
||||
? undefined
|
||||
: {
|
||||
OR: [
|
||||
{ owner_employee_id: request.employee.id },
|
||||
{ manager_employee_id: request.employee.id },
|
||||
{ members: { some: { employee_id: request.employee.id, left_at: null } } },
|
||||
],
|
||||
},
|
||||
project: projectScopeWhere(fastify, request.employee),
|
||||
},
|
||||
})
|
||||
: Promise.resolve(0),
|
||||
featureEnabled.projectDomain && featureEnabled.projectCommunicationLog
|
||||
? fastify.prisma.projectCommunicationLog.count({
|
||||
where: {
|
||||
next_follow_up_date: parseDateOnlyString(today),
|
||||
project: projectScopeWhere(fastify, request.employee),
|
||||
},
|
||||
})
|
||||
: Promise.resolve(0),
|
||||
featureEnabled.projectDomain && featureEnabled.projectClosureChecklist
|
||||
? fastify.prisma.projectClosureChecklistItem.findMany({
|
||||
where: {
|
||||
status: "pending",
|
||||
project: projectScopeWhere(fastify, request.employee),
|
||||
},
|
||||
distinct: ["project_id"],
|
||||
select: { project_id: true },
|
||||
}).then((items) => items.length)
|
||||
: Promise.resolve(0),
|
||||
]);
|
||||
|
||||
const todayPresence = featureEnabled.presenceBoard
|
||||
@@ -507,6 +514,8 @@ async function portalRoutes(fastify) {
|
||||
overdueMilestonesCount: overdueProjectMilestoneCount,
|
||||
highPriorityTaskCount: highPriorityProjectTaskCount,
|
||||
openRiskCount: openProjectRiskCount,
|
||||
followUpDueTodayCount: projectFollowUpDueTodayCount,
|
||||
closurePendingProjectCount,
|
||||
href: "/projects",
|
||||
}
|
||||
: null,
|
||||
|
||||
在新工单中引用
屏蔽一个用户