SURVEY-DATA-DEBUG-GUIDE.md 14 KB

能力问卷数据加载调试指南

🔍 问题描述

管理端员工信息面板中,复用的 @employee-detail-panel 组件无法显示能力问卷数据,显示"该员工尚未完成能力问卷",但实际上该员工已经完成了问卷。


📊 数据流转链路

1. employees.ts: viewEmployee()
   ↓ 调用 loadEmployeeSurvey()
   
2. employees.ts: loadEmployeeSurvey()
   ↓ 查询 Profile 和 SurveyLog 表
   ↓ 返回 { completed, data, profileId }
   
3. employees.ts: selectedEmployeeForPanel
   ↓ 包含 surveyCompleted 和 surveyData
   
4. employee-info-panel.component.html
   ↓ [employee]="selectedEmployeeForPanel"
   
5. employee-info-panel.component.ts: employeeDetailForTeamLeader getter
   ↓ 转换为 TeamLeaderEmployeeDetail 格式
   
6. <app-employee-detail-panel>
   ↓ [employeeDetail]="employeeDetailForTeamLeader"
   
7. employee-detail-panel.html
   ↓ @if (employeeDetail.surveyCompleted && employeeDetail.surveyData)

🐛 可能的问题点

问题点 1: Profile 查询失败

位置: employees.ts: loadEmployeeSurvey() (行 507-558)

检查方法:

// 打开浏览器控制台,查找以下日志
🔍 [loadEmployeeSurvey] 查找员工 徐福静 (employeeId),找到 X 个结果

预期结果: 找到 1 个结果 问题结果: 找到 0 个结果

原因:

  • Employee.id 与 Profile.objectId 不匹配
  • Employee.realname 与 Profile.realname/name 不匹配

解决方案:

// 需要确认数据库中的映射关系
// 方法1: 使用 Employee.userid (企微ID) 查询
const useridQuery = new Parse.Query('Profile');
useridQuery.equalTo('userid', emp.userid);

// 方法2: 使用 Employee.wxworkId 查询
const wxworkQuery = new Parse.Query('Profile');
wxworkQuery.equalTo('wxworkId', emp.wxworkId);

问题点 2: SurveyLog 查询失败

位置: employees.ts: loadEmployeeSurvey() (行 528-539)

检查方法:

// 控制台日志
📝 [loadEmployeeSurvey] 找到 X 条问卷记录

预期结果: 找到至少 1 条记录 问题结果: 找到 0 条记录

原因:

  • type = 'survey-profile' 不匹配(可能是其他 type 值)
  • profile Pointer 不匹配

解决方案:

// 先查询所有该用户的 SurveyLog
const allSurveyQuery = new Parse.Query('SurveyLog');
allSurveyQuery.equalTo('profile', profile.toPointer());
allSurveyQuery.descending('createdAt');
const allSurveys = await allSurveyQuery.find();
console.log('📝 所有问卷记录:', allSurveys.map(s => ({ 
  type: s.get('type'), 
  answers: s.get('answers')?.length 
})));

问题点 3: surveyCompleted 标记错误

位置: employees.ts: loadEmployeeSurvey() (行 522)

检查方法:

// 控制台日志
📋 [loadEmployeeSurvey] Profile ID: xxx, surveyCompleted: false

预期结果: surveyCompleted: true 问题结果: surveyCompleted: false

原因:

  • Profile 表中的 surveyCompleted 字段未正确更新
  • 字段名称不匹配(可能是 survey_completed 或其他)

解决方案:

// 检查 Profile 的所有字段
const profile = profileResults[0];
console.log('📋 Profile 所有字段:', profile.attributes);
console.log('📋 surveyCompleted 字段值:', profile.get('surveyCompleted'));
console.log('📋 survey_completed 字段值:', profile.get('survey_completed'));

问题点 4: 数据传递丢失

位置: employee-info-panel.component.ts: employeeDetailForTeamLeader (行 149-180)

检查方法:

// 控制台日志
✅ [employeeDetailForTeamLeader] 转换完成: {
  surveyCompleted: true/false,
  hasSurveyData: true/false
}

预期结果:

surveyCompleted: true
hasSurveyData: true
surveyData: { answers: [...], createdAt: Date }

问题结果:

surveyCompleted: false
hasSurveyData: false
surveyData: undefined

原因:

  • this.employee 中没有 surveyCompletedsurveyData 字段
  • 数据在 employees.tsemployee-info-panel 的传递中丢失

解决方案:

// 在 employees.ts 中添加更详细的日志
console.log('🎯 [Employees] selectedEmployeeForPanel 完整内容:', {
  ...this.selectedEmployeeForPanel,
  surveyCompleted: this.selectedEmployeeForPanel.surveyCompleted,
  surveyData: this.selectedEmployeeForPanel.surveyData
});

🔧 调试步骤

步骤 1: 打开浏览器控制台

F12 打开开发者工具,切换到 Console 标签。

步骤 2: 清空控制台

点击控制台左上角的 🚫 图标,清空所有日志。

步骤 3: 点击员工"徐福静"

在员工列表中点击"徐福静",打开员工信息面板。

步骤 4: 查看控制台输出

按照以下顺序查找日志:

日志 1: 员工数据加载开始

🚀 [Employees] 开始打开员工信息面板: 徐福静 (employeeId)

日志 2: 问卷数据查询

🔍 [loadEmployeeSurvey] 查找员工 徐福静 (employeeId),找到 X 个结果
  • ✅ 如果 X = 1,继续
  • ❌ 如果 X = 0,问题在 Profile 查询,跳到问题点 1

日志 3: Profile 信息

📋 [loadEmployeeSurvey] Profile ID: xxx, surveyCompleted: true/false
  • ✅ 如果 surveyCompleted: true,继续
  • ❌ 如果 surveyCompleted: false,问题在 Profile 标记,跳到问题点 3

日志 4: SurveyLog 查询

📝 [loadEmployeeSurvey] 找到 X 条问卷记录
  • ✅ 如果 X >= 1,继续
  • ❌ 如果 X = 0,问题在 SurveyLog 查询,跳到问题点 2

日志 5: 问卷数据加载成功

✅ [loadEmployeeSurvey] 问卷数据加载成功,共 X 道题
  • ✅ 如果 X > 0,继续
  • ❌ 如果 X = 0,问题在答案数据

日志 6: 完整数据准备

📝 [Employees] 问卷数据加载完成: {
  completed: true,
  answers: X
}

🎯 [Employees] 完整数据准备完成,打开面板: {
  surveyData: '✅'
}
  • ✅ 如果 surveyData: '✅',继续
  • ❌ 如果 surveyData: '❌',数据未正确传递

日志 7: 数据转换

✅ [employeeDetailForTeamLeader] 转换完成: {
  surveyCompleted: true,
  hasSurveyData: true
}
  • ✅ 如果两者都是 true,数据传递成功
  • ❌ 如果任一为 false,跳到问题点 4

日志 8: 完整数据结构

📦 [employeeDetailForTeamLeader] 完整数据结构: {
  employee: {...},
  result: {
    surveyCompleted: true,
    surveyData: {...}
  }
}
  • ✅ 展开查看 result.surveyData 是否包含 answers 数组

🛠️ 快速修复方案

方案 A: 增强日志(推荐)

employees.tsloadEmployeeSurvey 方法中添加更多日志:

// 在 line 507 附近添加
console.log('🔍 [loadEmployeeSurvey] 查询参数:', {
  employeeId,
  employeeName,
  useridQuery: emp.userid,
  wxworkId: emp.wxworkId
});

// 在 line 514 附近添加
if (profileResults.length > 0) {
  const profile = profileResults[0];
  console.log('📋 [loadEmployeeSurvey] Profile 完整信息:', {
    id: profile.id,
    realname: profile.get('realname'),
    name: profile.get('name'),
    surveyCompleted: profile.get('surveyCompleted'),
    allAttributes: profile.attributes
  });
}

// 在 line 528 附近添加
console.log('📝 [loadEmployeeSurvey] SurveyLog 查询参数:', {
  profileId: profile.id,
  profilePointer: profile.toPointer(),
  type: 'survey-profile'
});

// 在 line 531 附近添加
console.log('📝 [loadEmployeeSurvey] SurveyLog 查询结果:', {
  count: surveyResults.length,
  surveys: surveyResults.map(s => ({
    id: s.id,
    type: s.get('type'),
    answersCount: s.get('answers')?.length,
    createdAt: s.get('createdAt')
  }))
});

方案 B: 备用查询逻辑

如果 type = 'survey-profile' 查询不到,尝试其他 type:

// 在 loadEmployeeSurvey 方法中,line 528 附近
if (surveyCompleted) {
  // 先尝试 'survey-profile' type
  let surveyQuery = new Parse.Query('SurveyLog');
  surveyQuery.equalTo('profile', profile.toPointer());
  surveyQuery.equalTo('type', 'survey-profile');
  surveyQuery.descending('createdAt');
  surveyQuery.limit(1);
  
  let surveyResults = await surveyQuery.find();
  console.log(`📝 [loadEmployeeSurvey] 找到 'survey-profile' 类型: ${surveyResults.length} 条`);
  
  // 如果找不到,尝试不限制 type
  if (surveyResults.length === 0) {
    console.warn(`⚠️ [loadEmployeySurvey] 'survey-profile' 类型未找到,尝试查询所有类型`);
    surveyQuery = new Parse.Query('SurveyLog');
    surveyQuery.equalTo('profile', profile.toPointer());
    surveyQuery.descending('createdAt');
    surveyQuery.limit(1);
    surveyResults = await surveyQuery.find();
    console.log(`📝 [loadEmployeeSurvey] 找到所有类型: ${surveyResults.length} 条`);
  }
  
  if (surveyResults.length > 0) {
    const survey = surveyResults[0];
    const surveyData = {
      answers: survey.get('answers') || [],
      createdAt: survey.get('createdAt'),
      updatedAt: survey.get('updatedAt')
    };
    console.log(`✅ [loadEmployeeSurvey] 问卷数据加载成功,type: ${survey.get('type')}, 共 ${surveyData.answers.length} 道题`);
    
    return {
      completed: true,
      data: surveyData,
      profileId
    };
  }
}

方案 C: 直接使用组长端逻辑

组长端的 dashboard.ts 中已经有正确的查询逻辑(line 3184-3237),可以完全复用:

// 在 employees.ts 中,复制组长端的查询逻辑
private async loadEmployeeSurvey(employeeId: string, employeeName: string): Promise<{ completed: boolean; data: any; profileId: string }> {
  try {
    const Parse = await import('fmode-ng/parse').then(m => m.FmodeParse.with('nova'));
    
    // 💡 完全复用组长端的查询逻辑
    // 通过员工名字查找Profile(同时查询 realname 和 name 字段)
    const realnameQuery = new Parse.Query('Profile');
    realnameQuery.equalTo('realname', employeeName);
    
    const nameQuery = new Parse.Query('Profile');
    nameQuery.equalTo('name', employeeName);
    
    // 使用 or 查询
    const profileQuery = Parse.Query.or(realnameQuery, nameQuery);
    profileQuery.limit(1);
    
    const profileResults = await profileQuery.find();
    
    console.log(`🔍 查找员工 ${employeeName},找到 ${profileResults.length} 个结果`);
    
    if (profileResults.length > 0) {
      const profile = profileResults[0];
      const profileId = profile.id;
      const surveyCompleted = profile.get('surveyCompleted') || false;
      
      console.log(`📋 Profile ID: ${profileId}, surveyCompleted: ${surveyCompleted}`);
      
      // 如果已完成问卷,加载问卷答案
      if (surveyCompleted) {
        const surveyQuery = new Parse.Query('SurveyLog');
        surveyQuery.equalTo('profile', profile.toPointer());
        surveyQuery.equalTo('type', 'survey-profile');
        surveyQuery.descending('createdAt');
        surveyQuery.limit(1);
        
        const surveyResults = await surveyQuery.find();
        console.log(`📝 找到 ${surveyResults.length} 条问卷记录`);
        
        if (surveyResults.length > 0) {
          const survey = surveyResults[0];
          const surveyData = {
            answers: survey.get('answers') || [],
            createdAt: survey.get('createdAt'),
            updatedAt: survey.get('updatedAt')
          };
          console.log(`✅ 加载问卷数据成功,共 ${surveyData.answers.length} 道题`);
          
          return {
            completed: true,
            data: surveyData,
            profileId
          };
        }
      }
      
      return {
        completed: false,
        data: null,
        profileId
      };
    } else {
      console.warn(`⚠️ 未找到员工 ${employeeName} 的 Profile`);
      return {
        completed: false,
        data: null,
        profileId: ''
      };
    }
  } catch (error) {
    console.error(`❌ 加载员工 ${employeeName} 问卷数据失败:`, error);
    return {
      completed: false,
      data: null,
      profileId: ''
    };
  }
}

📋 检查清单

在提交问题或进行修复前,请完成以下检查:

  • 确认控制台中有 "🔍 [loadEmployeeSurvey] 查找员工..." 日志
  • 确认 Profile 查询结果 > 0
  • 确认 surveyCompleted 字段为 true
  • 确认 SurveyLog 查询结果 > 0
  • 确认 answers 数组有数据
  • 确认 selectedEmployeeForPanel.surveyData 有值
  • 确认 employeeDetailForTeamLeader.surveyData 有值
  • 确认 HTML 模板中的条件 @if (employeeDetail.surveyCompleted && employeeDetail.surveyData) 被满足

🎯 预期结果

修复后,在控制台应该看到完整的数据链路:

🚀 [Employees] 开始打开员工信息面板: 徐福静
🔄 [Employees] 预加载员工数据...
✅ [Employees] 项目数据加载完成: { currentProjects: 3, ... }
📅 [Employees] 日历数据生成完成: { days: 42, 有项目的天数: 15 }
🔍 [loadEmployeeSurvey] 查找员工 徐福静,找到 1 个结果
📋 [loadEmployeeSurvey] Profile ID: xxx, surveyCompleted: true
📝 [loadEmployeeSurvey] 找到 1 条问卷记录
✅ [loadEmployeeSurvey] 问卷数据加载成功,共 25 道题  // ✅ 关键
📝 [Employees] 问卷数据加载完成: { completed: true, answers: 25 }
🎯 [Employees] 完整数据准备完成,打开面板: { surveyData: '✅' }
✅ [Employees] 面板已显示
🔍 [employeeDetailForTeamLeader] 开始转换
✅ [employeeDetailForTeamLeader] 转换完成: {
  surveyCompleted: true,    // ✅ 关键
  hasSurveyData: true      // ✅ 关键
}

然后在页面上应该能看到:

  • ✅ "已完成问卷" 状态
  • ✅ 问卷完成时间
  • ✅ 能力画像摘要
  • ✅ "查看完整问卷(共 25 道题)" 按钮

文档版本: v1.0 创建日期: 2025-11-10 状态: 调试指南