DESIGNER-ASSIGNMENT-ALL-PROJECTS-FIX.md 9.6 KB

设计师分配弹窗项目数量显示修复

问题描述

设计师组分配弹窗(@designer-team-assignment-modal)中,所有成员显示的项目数量都是 0,即使这些设计师实际上手上有项目。

根本原因

通过详细的代码分析和对比团队组长端(@team-leader/dashboard)的实现,发现了以下几个关键问题:

1. ProjectTeam 查询范围过窄(最关键)

  • 修复前:使用 teamQuery.containedIn('profile', profilePointers) 限制查询范围
    • 只查询特定成员的 ProjectTeam 记录
    • 如果 profilePointers 中的 ID 与数据库中的 profile.id 不匹配,会查不到数据
  • 团队组长端
    • 不限制 profile 范围
    • 查询所有公司的 ProjectTeam 记录
    • 在代码中筛选目标成员的项目

2. 数据查询逻辑不一致

  • 团队组长端

    • 优先使用 ProjectTeam 表查询项目
    • 如果 ProjectTeam 表为空,降级到使用 Project.assignee 字段
    • 不过滤项目状态,统计所有项目
  • 设计师分配弹窗(修复前)

    • 也使用了双查询路径(ProjectTeam + Project.assignee
    • 但引入了 isActiveProject() 过滤器,过滤掉了很多项目
    • 过滤规则排除了:已完成已交付已取消已归档 等状态
    • 过滤规则排除了:delivery已完成 等阶段

3. 项目状态过滤过于严格

isActiveProject() 方法排除了大量状态和阶段:

const excludedStatuses = new Set(['已完成','已交付','已取消','已归档','归档','archived','cancelled','canceled','done','delivered']);
const excludedStages = new Set(['delivery','已完成']);

这导致很多实际存在的项目被过滤掉,显示项目数为 0。

4. 降级查询中缺少角色过滤

在使用 Project.assignee 作为降级方案时,修复前的代码没有检查 assignee.roleName === '组员',可能统计了组长或其他角色的项目。

解决方案

核心修改:完全对齐团队组长端逻辑

1. 移除 ProjectTeam 查询的 profile 限制(最关键)

// ❌ 修复前:限制查询范围
const Profile = Parse.Object.extend('Profile');
const profilePointers = allMembers.map(m => {
  const p = new Profile();
  p.id = m.id;
  return p;
});

teamQuery.containedIn('profile', profilePointers);  // ❌ 查询范围过窄

// ✅ 修复后:查询所有公司的 ProjectTeam(对齐组长端)
const teamQuery = new Parse.Query('ProjectTeam');
teamQuery.matchesQuery('project', companyProjectQuery);
// ✅ 不限制 profile 范围,后续在代码中筛选

2. 移除项目状态过滤

// ❌ 修复前:使用 isActiveProject 过滤
if (!this.isActiveProject(project)) {
  filteredOutCount++;
  console.log(`   ⏭️ 已过滤非活跃项目...`);
} else {
  // 添加到 profileIdToProjects
}

// ✅ 修复后:统计所有项目,不过滤
const arr = profileIdToProjects.get(profile.id) || [];
arr.push(project);
profileIdToProjects.set(profile.id, arr);

3. 在降级查询中添加角色过滤

// ✅ 修复后:只统计组员角色的项目
const assigneeRole = assignee.get('roleName');
if (assigneeRole === '组员') {
  const arr = profileIdToProjects.get(assignee.id) || [];
  arr.push(project);
  profileIdToProjects.set(assignee.id, arr);
} else {
  nonMemberRoleCount++;
  console.log(`   ⏭️ 跳过非组员角色的项目 (角色: ${assigneeRole || '未知'})`)
}

4. 保留双查询路径和缓存机制

  • 优先查询 ProjectTeam
  • 如果为空,降级到 Project.assignee
  • 使用 30 秒客户端缓存,提高后续打开速度

5. 更新统计日志

console.log('📊 [项目数据加载] 统计结果:', {
  '数据源': teamRecords.length > 0 ? 'ProjectTeam 表' : 'Project 表(降级)',
  '总记录数': teamRecords.length > 0 ? teamRecords.length : projects.length,
  '缺少assignee': noAssigneeCount,
  '非组员角色': nonMemberRoleCount,  // 新增
  '有效项目数': Array.from(profileIdToProjects.values()).reduce((sum, arr) => sum + arr.length, 0)
});

6. 同步修改员工详情弹窗逻辑

showDesignerEmployeeDetail() 方法中,也移除了 isActiveProject 过滤:

// ❌ 修复前
const projects = allProjects.filter(p => this.isActiveProject(p));

// ✅ 修复后
const projects = allProjects;

修改的文件

  • yss-project/src/app/pages/designer/project-detail/components/designer-team-assignment-modal/designer-team-assignment-modal.component.ts

关键代码位置

  1. enrichMembersWithProjectAssignments() 方法 (约 line 477-870)

    • 最关键修复:移除了 teamQuery.containedIn('profile', profilePointers) 限制(line 500-524)
    • 移除了 isActiveProject 过滤(line 665-672, 698-709)
    • 添加了角色过滤(line 698-709)
    • 更新了统计日志(line 713-719)
  2. showDesignerEmployeeDetail() 方法 (约 line 1290-1450)

    • 移除了 isActiveProject 过滤(line 1373-1376)

调试增强

保留了详细的彩色控制台日志:

  • 🔍 绿色:ProjectTeam 查询
  • ⚠️ 橙色:降级查询
  • 💾 紫色:缓存操作
  • 🔥 粉色:DEBUG 输出
  • 📊 蓝色:项目统计
  • ✅ 白色:成功操作

测试建议

1. 基本功能测试

测试步骤:
1. 打开订单分配阶段的任意项目
2. 点击"添加协作成员"按钮
3. 观察设计师列表中每个成员的项目数量
4. 查看控制台输出的详细日志

预期结果:
- 每个设计师显示的项目数应该是他们手上所有项目(不过滤状态)
- 项目数 = 0:显示绿色(空闲)
- 项目数 1-5:显示橙色(有项目)
- 项目数 > 5:显示红色(繁忙)

2. 数据源验证

测试场景 A:ProjectTeam 表有数据
- 预期:控制台显示"使用 ProjectTeam 表数据"
- 验证:项目数量与 ProjectTeam 表记录一致

测试场景 B:ProjectTeam 表为空
- 预期:控制台显示"ProjectTeam 表为空,使用 Project.assignee 作为降级方案"
- 验证:项目数量与 Project.assignee 字段一致(仅统计组员角色)

3. 缓存测试

测试步骤:
1. 第一次打开弹窗(冷加载)
2. 关闭弹窗
3. 30秒内再次打开弹窗(使用缓存)
4. 等待 30 秒后再次打开(缓存过期)

预期结果:
- 第一次:显示完整查询日志
- 30秒内:显示"⚡ [使用缓存]"日志
- 30秒后:再次显示完整查询日志

4. 员工详情面板测试

测试步骤:
1. 在设计师列表中点击任意设计师的名字
2. 查看右侧员工详情面板
3. 验证显示的项目数量和日历数据

预期结果:
- 项目数量应与列表中显示的一致
- 日历应显示所有项目的时间线
- 控制台应输出详细的项目列表

对齐的团队组长端行为

修复后的设计师分配弹窗现在完全对齐团队组长端(@team-leader/dashboard)的以下行为:

  1. 双查询路径ProjectTeamProject.assignee
  2. 不过滤项目状态:统计所有项目,包括已完成、已交付等
  3. 角色过滤:降级查询时只统计 roleName === '组员' 的项目
  4. 数据结构:使用 Map<profileId, projects[]> 聚合数据
  5. 日历生成:基于项目的完整生命周期(createdAt → deadline)

潜在影响

正面影响

  • ✅ 显示真实的项目数量,不会因为过滤而漏掉项目
  • ✅ 与团队组长端的显示逻辑保持一致
  • ✅ 更准确地反映设计师的工作负载

可能的疑问

  • 为什么要统计已完成的项目?

    • 答:这是团队组长端的现有逻辑,可能用于评估设计师的历史负载和能力
    • 如果只需要"进行中"的项目,需要在团队组长端也同步修改
  • 项目数量会不会太多?

    • 答:可以通过增加时间范围过滤(如只统计最近 3 个月的项目)来优化
    • 但这需要在团队组长端也同步修改,保持一致性

下一步建议

1. 产品层面

  • 与产品团队确认:项目数量统计的准确定义
    • 是否应该只统计"进行中"的项目?
    • 是否需要排除"已完成"、"已取消"的项目?
    • 是否需要增加时间范围限制?

2. 技术层面

  • 如果需要修改统计规则,应该同时修改:
    • 团队组长端的 loadDesignerWorkload() 方法
    • 设计师分配弹窗的 enrichMembersWithProjectAssignments() 方法
    • 确保两处逻辑完全一致

3. 性能优化

  • 考虑在后端提供一个聚合 API:

    GET /api/designer-workload?companyId=xxx&includeCompleted=false
    
    • 返回每个设计师的项目数量和状态分布
    • 减少前端查询和计算的开销

总结

本次修复通过完全对齐团队组长端的逻辑,解决了设计师分配弹窗项目数量显示为 0 的问题。

核心改动(按重要性排序):

  1. 移除 ProjectTeam 查询的 profile 限制(最关键)
    • 修复前使用 containedIn('profile', profilePointers) 导致查询范围过窄
    • 修复后查询所有公司的 ProjectTeam,与团队组长端一致
  2. 移除项目状态过滤
    • 不再使用 isActiveProject() 过滤项目
    • 统计所有项目,包括已完成、已交付等状态
  3. 在降级查询中添加角色过滤
    • 确保只统计 roleName === '组员' 的项目

修复后的代码保留了详细的调试日志,方便后续排查问题和验证数据来源。同时,30秒缓存机制确保了良好的用户体验。