# 设计师分配弹窗项目数量显示修复 ## 问题描述 设计师组分配弹窗(`@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()` 方法排除了大量状态和阶段: ```typescript const excludedStatuses = new Set(['已完成','已交付','已取消','已归档','归档','archived','cancelled','canceled','done','delivered']); const excludedStages = new Set(['delivery','已完成']); ``` 这导致很多实际存在的项目被过滤掉,显示项目数为 0。 ### 4. **降级查询中缺少角色过滤** 在使用 `Project.assignee` 作为降级方案时,修复前的代码没有检查 `assignee.roleName === '组员'`,可能统计了组长或其他角色的项目。 ## 解决方案 ### 核心修改:完全对齐团队组长端逻辑 #### 1. **移除 ProjectTeam 查询的 profile 限制(最关键)** ```typescript // ❌ 修复前:限制查询范围 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. **移除项目状态过滤** ```typescript // ❌ 修复前:使用 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. **在降级查询中添加角色过滤** ```typescript // ✅ 修复后:只统计组员角色的项目 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. **更新统计日志** ```typescript 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` 过滤: ```typescript // ❌ 修复前 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. ✅ **双查询路径**:`ProjectTeam` → `Project.assignee` 2. ✅ **不过滤项目状态**:统计所有项目,包括已完成、已交付等 3. ✅ **角色过滤**:降级查询时只统计 `roleName === '组员'` 的项目 4. ✅ **数据结构**:使用 `Map` 聚合数据 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秒缓存机制确保了良好的用户体验。