|
|
@@ -49,8 +49,9 @@ async function handler(request, response) {
|
|
|
|
|
|
console.log(`📅 查询时间范围: ${rangeStart.toISOString()} ~ ${rangeEnd.toISOString()}`);
|
|
|
|
|
|
- // --- SQL 定义 ---
|
|
|
+ // --- SQL 定义 (性能优化版) ---
|
|
|
|
|
|
+ // 1. Workload 查询 - 保持不变,已经够高效
|
|
|
const workloadSql = `
|
|
|
SELECT
|
|
|
u."objectId" as "id",
|
|
|
@@ -70,8 +71,7 @@ async function handler(request, response) {
|
|
|
ORDER BY "weightedLoad" DESC
|
|
|
`;
|
|
|
|
|
|
- // 项目查询 - 添加时间范围筛选
|
|
|
- // 筛选逻辑:项目的 deadline 或 createdAt 在指定时间范围内
|
|
|
+ // 2. 项目查询 - 优化:分离子查询,先查项目基础信息
|
|
|
const projectsSql = `
|
|
|
SELECT
|
|
|
p."objectId" as "id",
|
|
|
@@ -83,89 +83,93 @@ async function handler(request, response) {
|
|
|
p."createdAt",
|
|
|
p."data",
|
|
|
p."date" as "projectDate",
|
|
|
- EXTRACT(DAY FROM (p."deadline" - NOW())) as "daysLeft",
|
|
|
- (
|
|
|
- SELECT string_agg(pr."name", ', ')
|
|
|
- FROM "ProjectTeam" pt
|
|
|
- JOIN "Profile" pr ON pt."profile" = pr."objectId"
|
|
|
- WHERE pt."project" = p."objectId" AND pt."isDeleted" IS NOT TRUE
|
|
|
- ) as "designerName",
|
|
|
- (
|
|
|
- SELECT array_agg(pt."profile")
|
|
|
- FROM "ProjectTeam" pt
|
|
|
- WHERE pt."project" = p."objectId" AND pt."isDeleted" IS NOT TRUE
|
|
|
- ) as "designerIds"
|
|
|
+ EXTRACT(DAY FROM (p."deadline" - NOW())) as "daysLeft"
|
|
|
FROM "Project" p
|
|
|
WHERE p."company" = $1
|
|
|
AND p."isDeleted" IS NOT TRUE
|
|
|
AND p."status" != '已完成'
|
|
|
AND (
|
|
|
- -- 项目截止时间在范围内
|
|
|
- (p."deadline" >= $2 AND p."deadline" <= $3)
|
|
|
- -- 或者项目创建时间在范围内
|
|
|
- OR (p."createdAt" >= $2 AND p."createdAt" <= $3)
|
|
|
- -- 或者项目跨越整个范围(开始于范围前,结束于范围后)
|
|
|
- OR (p."createdAt" < $2 AND p."deadline" > $3)
|
|
|
- -- 或者没有截止时间但状态为进行中
|
|
|
- OR (p."deadline" IS NULL AND p."status" = '进行中')
|
|
|
+ p."deadline" >= $2 AND p."deadline" <= $3
|
|
|
+ OR p."createdAt" >= $2 AND p."createdAt" <= $3
|
|
|
+ OR p."createdAt" < $2 AND p."deadline" > $3
|
|
|
+ OR p."deadline" IS NULL
|
|
|
)
|
|
|
ORDER BY p."updatedAt" DESC
|
|
|
- LIMIT 1000
|
|
|
+ LIMIT 500
|
|
|
`;
|
|
|
|
|
|
+ // 2.1 项目团队成员查询 - 一次性获取所有相关项目的团队成员
|
|
|
+ const projectTeamsSql = `
|
|
|
+ SELECT
|
|
|
+ pt."project" as "projectId",
|
|
|
+ string_agg(pr."name", ', ' ORDER BY pr."name") as "designerName",
|
|
|
+ array_agg(pt."profile") as "designerIds"
|
|
|
+ FROM "ProjectTeam" pt
|
|
|
+ JOIN "Profile" pr ON pt."profile" = pr."objectId"
|
|
|
+ WHERE pt."isDeleted" IS NOT TRUE
|
|
|
+ AND pt."project" IN (
|
|
|
+ SELECT p."objectId"
|
|
|
+ FROM "Project" p
|
|
|
+ WHERE p."company" = $1
|
|
|
+ AND p."isDeleted" IS NOT TRUE
|
|
|
+ AND p."status" != '已完成'
|
|
|
+ AND (
|
|
|
+ p."deadline" >= $2 AND p."deadline" <= $3
|
|
|
+ OR p."createdAt" >= $2 AND p."createdAt" <= $3
|
|
|
+ OR p."createdAt" < $2 AND p."deadline" > $3
|
|
|
+ OR p."deadline" IS NULL
|
|
|
+ )
|
|
|
+ )
|
|
|
+ GROUP BY pt."project"
|
|
|
+ `;
|
|
|
+
|
|
|
+ // 3. Space Stats - 优化:简化查询结构
|
|
|
const spaceStatsSql = `
|
|
|
- WITH ActiveProjects AS (
|
|
|
+ WITH ActiveProjectIds AS (
|
|
|
SELECT p."objectId"
|
|
|
FROM "Project" p
|
|
|
WHERE p."company" = $1
|
|
|
AND p."isDeleted" IS NOT TRUE
|
|
|
AND p."status" != '已完成'
|
|
|
AND (
|
|
|
- (p."deadline" >= $2 AND p."deadline" <= $3)
|
|
|
- OR (p."createdAt" >= $2 AND p."createdAt" <= $3)
|
|
|
- OR (p."createdAt" < $2 AND p."deadline" > $3)
|
|
|
- OR (p."deadline" IS NULL AND p."status" = '进行中')
|
|
|
+ p."deadline" >= $2 AND p."deadline" <= $3
|
|
|
+ OR p."createdAt" >= $2 AND p."createdAt" <= $3
|
|
|
+ OR p."createdAt" < $2 AND p."deadline" > $3
|
|
|
+ OR p."deadline" IS NULL
|
|
|
)
|
|
|
- LIMIT 1000
|
|
|
+ LIMIT 500
|
|
|
),
|
|
|
ProjectSpaces AS (
|
|
|
SELECT
|
|
|
- p."objectId" as "spaceId",
|
|
|
- p."productName" as "spaceName",
|
|
|
- p."productType" as "spaceType",
|
|
|
- p."project" as "projectId"
|
|
|
- FROM "Product" p
|
|
|
- WHERE p."project" IN (SELECT "objectId" FROM ActiveProjects)
|
|
|
- AND (p."isDeleted" IS NULL OR p."isDeleted" = false)
|
|
|
+ prod."objectId" as "spaceId",
|
|
|
+ prod."productName" as "spaceName",
|
|
|
+ prod."productType" as "spaceType",
|
|
|
+ prod."project" as "projectId"
|
|
|
+ FROM "Product" prod
|
|
|
+ WHERE prod."project" IN (SELECT "objectId" FROM ActiveProjectIds)
|
|
|
+ AND (prod."isDeleted" IS NULL OR prod."isDeleted" = false)
|
|
|
),
|
|
|
Deliverables AS (
|
|
|
SELECT
|
|
|
- COALESCE(d."data"->>'spaceId', d."data"->>'productId') as "spaceId",
|
|
|
+ COALESCE(pf."data"->>'spaceId', pf."data"->>'productId') as "spaceId",
|
|
|
COUNT(*) as "fileCount",
|
|
|
- SUM(CASE WHEN
|
|
|
- d."fileType" = 'delivery_white_model' OR
|
|
|
- d."data"->>'deliveryType' IN ('white_model', 'delivery_white_model')
|
|
|
+ SUM(CASE WHEN pf."fileType" = 'delivery_white_model'
|
|
|
+ OR pf."data"->>'deliveryType' IN ('white_model', 'delivery_white_model')
|
|
|
THEN 1 ELSE 0 END) as "whiteModelCount",
|
|
|
- SUM(CASE WHEN
|
|
|
- d."fileType" = 'delivery_soft_decor' OR
|
|
|
- d."data"->>'deliveryType' IN ('soft_decor', 'delivery_soft_decor')
|
|
|
+ SUM(CASE WHEN pf."fileType" = 'delivery_soft_decor'
|
|
|
+ OR pf."data"->>'deliveryType' IN ('soft_decor', 'delivery_soft_decor')
|
|
|
THEN 1 ELSE 0 END) as "softDecorCount",
|
|
|
- SUM(CASE WHEN
|
|
|
- d."fileType" = 'delivery_rendering' OR
|
|
|
- d."data"->>'deliveryType' IN ('rendering', 'delivery_rendering')
|
|
|
+ SUM(CASE WHEN pf."fileType" = 'delivery_rendering'
|
|
|
+ OR pf."data"->>'deliveryType' IN ('rendering', 'delivery_rendering')
|
|
|
THEN 1 ELSE 0 END) as "renderingCount",
|
|
|
- SUM(CASE WHEN
|
|
|
- d."fileType" = 'delivery_post_process' OR
|
|
|
- d."data"->>'deliveryType' IN ('post_process', 'delivery_post_process')
|
|
|
+ SUM(CASE WHEN pf."fileType" = 'delivery_post_process'
|
|
|
+ OR pf."data"->>'deliveryType' IN ('post_process', 'delivery_post_process')
|
|
|
THEN 1 ELSE 0 END) as "postProcessCount"
|
|
|
- FROM "ProjectFile" d
|
|
|
- WHERE d."project" IN (SELECT "objectId" FROM ActiveProjects)
|
|
|
- AND (d."isDeleted" IS NULL OR d."isDeleted" = false)
|
|
|
- AND (
|
|
|
- d."fileType" LIKE 'delivery_%' OR
|
|
|
- d."data"->>'uploadStage' = 'delivery'
|
|
|
- )
|
|
|
- GROUP BY COALESCE(d."data"->>'spaceId', d."data"->>'productId')
|
|
|
+ FROM "ProjectFile" pf
|
|
|
+ WHERE pf."project" IN (SELECT "objectId" FROM ActiveProjectIds)
|
|
|
+ AND (pf."isDeleted" IS NULL OR pf."isDeleted" = false)
|
|
|
+ AND (pf."fileType" LIKE 'delivery_%' OR pf."data"->>'uploadStage' = 'delivery')
|
|
|
+ GROUP BY COALESCE(pf."data"->>'spaceId', pf."data"->>'productId')
|
|
|
)
|
|
|
SELECT
|
|
|
ps."projectId",
|
|
|
@@ -212,16 +216,26 @@ async function handler(request, response) {
|
|
|
LIMIT 50
|
|
|
`;
|
|
|
|
|
|
- // --- 执行 SQL ---
|
|
|
+ // --- 执行 SQL (并行优化) ---
|
|
|
const queryParams = [companyId, rangeStart, rangeEnd];
|
|
|
|
|
|
- const [workloadResult, projectsResult, spaceStatsResult, issuesResult] = await Promise.all([
|
|
|
+ const [workloadResult, projectsResult, projectTeamsResult, spaceStatsResult, issuesResult] = await Promise.all([
|
|
|
Psql.query(workloadSql, [companyId]),
|
|
|
Psql.query(projectsSql, queryParams),
|
|
|
+ Psql.query(projectTeamsSql, queryParams),
|
|
|
Psql.query(spaceStatsSql, queryParams),
|
|
|
Psql.query(issuesSql, queryParams)
|
|
|
]);
|
|
|
|
|
|
+ // 构建项目团队成员映射
|
|
|
+ const projectTeamsMap = {};
|
|
|
+ projectTeamsResult.forEach(row => {
|
|
|
+ projectTeamsMap[row.projectId] = {
|
|
|
+ designerName: row.designerName || '待分配',
|
|
|
+ designerIds: row.designerIds || []
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
// --- 格式化数据 ---
|
|
|
|
|
|
// 1. Workload
|
|
|
@@ -248,6 +262,9 @@ async function handler(request, response) {
|
|
|
// 2. Projects
|
|
|
const spaceAssigneeMap = {};
|
|
|
const projects = projectsResult.map(p => {
|
|
|
+ // 从映射中获取设计师信息
|
|
|
+ const teamInfo = projectTeamsMap[p.id] || { designerName: '待分配', designerIds: [] };
|
|
|
+
|
|
|
// 解析设计师分配信息
|
|
|
if (p.projectDate && p.projectDate.designerAssignmentStats) {
|
|
|
const stats = p.projectDate.designerAssignmentStats;
|
|
|
@@ -292,8 +309,8 @@ async function handler(request, response) {
|
|
|
daysLeft: Math.ceil(days),
|
|
|
isOverdue: days < 0,
|
|
|
statusStr,
|
|
|
- designerName: p.designerName || '待分配',
|
|
|
- designerIds: p.designerIds || [],
|
|
|
+ designerName: teamInfo.designerName,
|
|
|
+ designerIds: teamInfo.designerIds,
|
|
|
// 扩展字段
|
|
|
data: data
|
|
|
};
|