DESIGNER_ASSIGNMENT_SYNC_ISSUE_ANALYSIS.md 11 KB

设计师分配弹窗数据同步问题分析

问题描述

总管理员端的项目管理中,点击"分配设计师"按钮后,设计师的繁忙情况接单情况没有显示,与其他端看到的数据不一致。


数据来源分析

1️⃣ 设计师基础数据来源

Department 表 (项目组信息)

  • name - 项目组名称
  • leader - 组长 (Pointer → Profile)
  • type - 类型 ('project' = 项目组)
  • company - 所属公司
  • data.description - 项目组描述

Profile 表 (设计师信息)

  • name - 设计师姓名
  • department - 所属项目组 (String, departmentId)
  • data.avatar - 头像
  • data.skills - 技能列表
  • data.status - 初始状态 (通常为空,由项目数据计算)
  • data.workload - 初始工作量 (通常为空,由项目数据计算)
  • data.currentProjects - 初始项目数 (通常为空,由项目数据计算)

2️⃣ 设计师项目数据来源 (关键!)

设计师分配弹窗在 enrichMembersWithProjectAssignments() 方法中动态加载项目数据:

查询方案1:ProjectTeam 表 (优先)

// 查询 ProjectTeam 表
const teamQuery = new Parse.Query('ProjectTeam');
teamQuery.matchesQuery('project', companyProjectQuery);
teamQuery.include('project');
teamQuery.include('profile');

字段说明:

  • project - 关联的项目 (Pointer → Project)
  • profile - 关联的设计师 (Pointer → Profile)
  • role - 项目角色
  • isDeleted - 是否删除

聚合逻辑:

  • 遍历 ProjectTeam 记录
  • profile.id 分组统计项目数量
  • 不过滤项目状态,统计所有项目

查询方案2:Project 表 (降级方案)

如果 ProjectTeam 表为空,使用 Project 表的 assignee 字段:

const projectQuery = new Parse.Query('Project');
projectQuery.equalTo('company', companyId);
projectQuery.include('assignee');

字段说明:

  • assignee - 项目负责人 (Pointer → Profile)
  • status - 项目状态
  • currentStage / stage - 项目阶段
  • createdAt - 创建时间
  • title / name - 项目名称

3️⃣ 状态计算逻辑

设计师的状态根据项目数量自动计算:

// 第808-818行
if (member.currentProjects === 0) {
  member.status = 'idle';        // 🟢 绿色 - 空闲
} else if (member.currentProjects >= 1 && member.currentProjects <= 5) {
  member.status = 'reviewing';   // 🟠 橙色 - 有项目
} else {
  member.status = 'stagnant';    // 🔴 红色 - 繁忙 (超过5个项目)
}

关键指标:

  • currentProjects - 当前项目总数(所有状态的项目)
  • workload - 工作量 = Math.min(100, currentProjects * 20)
  • idleDays - 闲置天数(如果有项目则为0)
  • recentOrders - 近30天接单数
  • lastOrderDate - 最后接单日期
  • status - 状态 (idle/reviewing/stagnant)

核心问题定位

❌ 问题根源:项目管理端缺少关键输入

查看 project-management.ts 第322-340行:

openTeamAssignmentModal(project: Project): void {
  this.selectedProject = project;
  
  // ❌ 使用的是模拟数据
  this.projectTeams = this.mockProjectTeams;
  
  // ❌ 没有传递 loadRealData 参数
  // ❌ 没有传递 projectId 参数
  
  this.showTeamAssignmentModal = true;
}

查看 project-management.html 第268-278行:

<app-designer-team-assignment-modal
  [visible]="showTeamAssignmentModal"
  [projectTeams]="projectTeams"
  [selectedTeamId]="currentTeamAssignment.primaryTeamId"
  [selectedDesigners]="currentTeamAssignment.quotationAssignments"
  [crossTeamCollaborators]="currentTeamAssignment.crossTeamCollaborators"
  [quotationItems]="currentQuotationItems"
  [calendarViewMode]="'month'"
  
  ❌ 缺少:[loadRealData]="true"
  ❌ 缺少:[projectId]="selectedProject.id"
  
  (close)="closeTeamAssignmentModal()"
  (confirm)="confirmTeamAssignment($event)"
></app-designer-team-assignment-modal>

对比其他端的调用方式

✅ 正确的调用方式(参考设计师端/组长端)

<app-designer-team-assignment-modal
  [visible]="showTeamAssignmentModal"
  [loadRealData]="true"          ← ✅ 启用真实数据加载
  [projectId]="currentProjectId" ← ✅ 传递项目ID
  [loadRealSpaces]="true"        ← ✅ 加载真实空间数据
  [enableSpaceAssignment]="true" ← ✅ 启用空间分配
  (close)="closeTeamAssignmentModal()"
  (confirm)="confirmTeamAssignment($event)"
></app-designer-team-assignment-modal>

🔑 关键参数说明

参数 类型 默认值 作用
loadRealData boolean true 是否从数据库加载真实的项目组和成员数据
projectId string '' 项目ID,用于查询项目相关数据
loadRealSpaces boolean true 是否自动加载项目的空间数据
enableSpaceAssignment boolean false 是否启用空间分配功能

数据加载流程对比

❌ 当前项目管理端(错误流程)

打开弹窗
    ↓
loadRealData = undefined (默认 true)
    ↓
调用 loadRealProjectTeams()
    ↓
✅ 从 Department/Profile 表加载设计师基础信息
    ↓
❌ projectId 未传递 (空字符串)
    ↓
❌ enrichMembersWithProjectAssignments() 方法中
   查询 ProjectTeam 时没有项目上下文
    ↓
❌ 无法统计设计师的项目数量
    ↓
❌ currentProjects = 0 (默认值)
    ↓
❌ status = 'idle' (默认值)
    ↓
❌ workload = 0 (默认值)
    ↓
显示:所有设计师都是"空闲"状态 ❌

✅ 其他端(正确流程)

打开弹窗
    ↓
[loadRealData]="true"
[projectId]="currentProjectId"
    ↓
调用 loadRealProjectTeams()
    ↓
✅ 从 Department/Profile 表加载设计师基础信息
    ↓
✅ enrichMembersWithProjectAssignments()
    ↓
✅ 查询 ProjectTeam 表(或 Project 表)
    按公司ID查询所有项目
    ↓
✅ 为每个设计师统计项目数量
    currentProjects = 实际项目数
    ↓
✅ 根据项目数量计算状态
    0个项目 → idle (绿色)
    1-5个项目 → reviewing (橙色)
    >5个项目 → stagnant (红色)
    ↓
✅ 计算工作量
    workload = Math.min(100, currentProjects * 20)
    ↓
✅ 计算闲置天数
    有项目 → idleDays = 0
    无项目 → 根据 lastOrderDate 计算
    ↓
显示:设计师真实的繁忙情况 ✅

修复方案

方案1:修改 HTML 模板(推荐)

<!-- project-management.html -->
<app-designer-team-assignment-modal
  [visible]="showTeamAssignmentModal"
  [loadRealData]="true"                     ← ✅ 添加
  [projectId]="selectedProject?.id || ''"   ← ✅ 添加
  [loadRealSpaces]="true"                   ← ✅ 添加(可选)
  [enableSpaceAssignment]="false"           ← ✅ 添加(可选)
  [projectTeams]="projectTeams"
  [selectedTeamId]="currentTeamAssignment.primaryTeamId"
  [selectedDesigners]="currentTeamAssignment.quotationAssignments"
  [crossTeamCollaborators]="currentTeamAssignment.crossTeamCollaborators"
  [quotationItems]="currentQuotationItems"
  [calendarViewMode]="'month'"
  (close)="closeTeamAssignmentModal()"
  (confirm)="confirmTeamAssignment($event)"
></app-designer-team-assignment-modal>

方案2:修改 TypeScript 逻辑(可选)

// project-management.ts

openTeamAssignmentModal(project: Project): void {
  this.selectedProject = project;
  
  // ✅ 不再使用模拟数据,让弹窗组件自动加载
  // ❌ this.projectTeams = this.mockProjectTeams;
  this.projectTeams = [];  // 让弹窗组件自动加载真实数据
  
  // 初始化当前团队分配信息
  this.currentTeamAssignment = {
    primaryTeamId: project.assigneeId || null,
    quotationAssignments: [],
    crossTeamCollaborators: []
  };
  
  // TODO: 从Parse Server加载报价项数据
  this.currentQuotationItems = [];
  
  this.showTeamAssignmentModal = true;
  console.log('✅ 打开团队分配弹窗:', project.id, project.title);
}

验证步骤

1️⃣ 修改后重新打开弹窗

控制台应该输出:

🚀 [设计师分配弹窗] 初始化,loadRealData: true
🔄 [设计师分配弹窗] 弹窗打开,重新加载项目数据...
✅ [项目数据加载] ProjectTeam 查询成功!
📊 [项目数据加载] 查询结果: X 条记录
🔍 [张佳乐] Member ID: designer-1
   从 profileIdToProjects 获取到 3 个项目
   ✅ 已设置 currentProjects(所有项目) = 3
🟠 [张佳乐] 3个项目 - 有项目 (工作量:60%, 闲置:0天)

2️⃣ 检查设计师卡片显示

  • 状态指示器

    • 🟢 绿色 = 0个项目(空闲)
    • 🟠 橙色 = 1-5个项目(有项目)
    • 🔴 红色 = >5个项目(繁忙)
  • 项目数量:显示实际项目数,例如 "当前项目: 3个"

  • 工作量进度条:根据项目数量计算,例如 60%

  • 闲置天数:有项目时为0天,无项目时显示实际天数


潜在风险

⚠️ 如果 ProjectTeam 表为空

系统会自动启用降级方案,从 Project.assignee 字段查询:

// 降级方案:查询 Project 表
const projectQuery = new Parse.Query('Project');
projectQuery.equalTo('company', companyId);
projectQuery.include('assignee');

const allProjects = await projectQuery.find();
const projects = allProjects.filter(p => {
  const assignee = p.get('assignee');
  return assignee && profileIds.has(assignee.id);
});

注意事项:

  1. 确保 Project.assignee 字段已正确设置
  2. 只统计角色为"组员"的项目
  3. 项目必须属于当前公司

数据持久化位置

✅ Profile 表(设计师基础信息)

  • 位置:Parse Server → Profile 表
  • 字段:name, department, data.avatar, data.skills

✅ ProjectTeam 表(项目-设计师关联)

  • 位置:Parse Server → ProjectTeam 表
  • 字段:project (Pointer), profile (Pointer), role

✅ Project 表(项目信息)

  • 位置:Parse Server → Project 表
  • 字段:assignee (Pointer), status, currentStage, title

❌ 动态计算(不持久化)

  • currentProjects - 每次打开弹窗时动态查询
  • workload - 根据项目数量计算
  • status - 根据项目数量判断
  • idleDays - 根据最后接单日期计算
  • recentOrders - 统计近30天项目

总结

问题根源

项目管理端调用设计师分配弹窗时,没有传递 projectId 参数,导致弹窗无法查询设计师的项目数据,所有设计师都显示为默认的空闲状态。

解决方案

project-management.html 中添加 [projectId]="selectedProject?.id || ''" 输入属性,让弹窗组件能够查询真实的项目数据。

关键参数

  • loadRealData="true" - 启用真实数据加载(默认已是true)
  • projectId="项目ID" - 关键! 必须传递,用于查询项目数据

数据流程

传递 projectId
    ↓
查询 ProjectTeam/Project 表
    ↓
统计每个设计师的项目数量
    ↓
计算状态 (idle/reviewing/stagnant)
    ↓
显示繁忙情况和接单情况