2025102217-wxwork-sdk-init-fix.md 9.0 KB

企业微信SDK初始化问题修复

日期: 2025-10-24
问题: 访问项目详情页面时报错 TypeError: Cannot set properties of null (setting 'cid')


🔴 问题分析

错误现场

  • URL: http://localhost:4200/admin/project-detail/iKvYck89zE/order
  • 错误: TypeError: Cannot set properties of null (setting 'cid')
  • 错误位置:
    • wxwork.auth.mjs:8:1666
    • wxwork.sdk.mjs:8:4611
    • project-detail.component.ts:236

根本原因

1. ProjectService 数据源问题 (已解决)

问题

  • 项目管理页面使用 admin/services/ProjectService,从Parse数据库查询真实数据
  • 项目ID是真实的Parse objectId:7EFEsEd0ht, iKvYck89zE
  • 项目详情页面使用通用的 services/ProjectService,只有模拟数据
  • 模拟数据ID:proj-001, 2, 3
  • 结果:查询返回 undefined,导致后续操作失败

解决方案: 修改 src/app/services/project.service.tsgetProjectById 方法,优先从Parse查询真实数据:

getProjectById(id: string): Observable<Project | undefined> {
  return new Observable(observer => {
    this.getProjectFromParse(id).then(project => {
      if (project) {
        observer.next(project);
      } else {
        // 降级:使用模拟数据
        observer.next(this.projects.find(project => project.id === id));
      }
      observer.complete();
    }).catch(error => {
      console.error('查询项目失败,使用模拟数据:', error);
      observer.next(this.projects.find(project => project.id === id));
      observer.complete();
    });
  });
}

2. 企业微信SDK初始化缺少cid参数 (本次修复重点)

问题: 管理员端项目详情页 (modules/project/pages/project-detail/project-detail.component.ts) 在初始化企微SDK时:

// ❌ 旧代码
async initWxworkAuth() {
  let cid = this.cid || localStorage.getItem("company") || "";
  this.wxAuth = new WxworkAuth({ cid: cid});      // cid为空时会报错
  this.wxwork = new WxworkSDK({ cid: cid, appId: 'crm' });
  this.wecorp = new WxworkCorp(cid);
}

原因

  1. 管理员端路由是 /admin/project-detail/:projectId,没有 :cid 参数
  2. this.cid 从路由获取为空
  3. 如果 localStorage.getItem("company") 也为空,cid 就是空字符串
  4. 传入空字符串到 WxworkSDK 导致内部初始化失败:Cannot set properties of null (setting 'cid')

错误链

ngOnInit() 
  → initWxworkAuth() 
  → new WxworkSDK({ cid: "" }) 
  → 内部尝试设置 null.cid 
  → TypeError

✅ 解决方案

1. 修复 modules/project 的项目详情组件

文件: src/modules/project/pages/project-detail/project-detail.component.ts

修改点

A. 添加cid空值检查

async initWxworkAuth() {
  try {
    let cid = this.cid || localStorage.getItem("company") || "";
    
    // ✅ 如果没有cid,记录警告但不抛出错误
    if (!cid) {
      console.warn('⚠️ 未找到company ID (cid),企微功能将不可用');
      return;  // 不初始化SDK,避免错误
    }
    
    this.wxAuth = new WxworkAuth({ cid: cid });
    this.wxwork = new WxworkSDK({ cid: cid, appId: 'crm' });
    this.wecorp = new WxworkCorp(cid);
    
    console.log('✅ 企微SDK初始化成功,cid:', cid);
  } catch (error) {
    console.error('❌ 企微SDK初始化失败:', error);
    // 不阻塞页面加载
  }
}

B. 添加获取用户Profile的错误处理

// 2. 获取当前用户(优先从全局服务获取)
if (!this.currentUser?.id && this.wxAuth) {
  try {
    this.currentUser = await this.wxAuth.currentProfile();
  } catch (error) {
    console.warn('⚠️ 获取当前用户Profile失败:', error);
  }
}

2. 修复 designer 的项目详情组件

文件: src/app/pages/designer/project-detail/project-detail.ts

修改点:在 constructor 中添加企微认证初始化

constructor(
  private route: ActivatedRoute,
  private projectService: ProjectService,
  private router: Router,
  private fb: FormBuilder,
  private cdr: ChangeDetectorRef,
  private paymentVoucherService: PaymentVoucherRecognitionService,
  private projectReviewService: ProjectReviewService,
  private colorAnalysisService: ColorAnalysisService
) {
  // 初始化企业微信认证
  this.loadProfile();
}

// 当前用户信息
currentUser: any = {};

/**
 * 加载当前用户Profile
 */
async loadProfile() {
  try {
    const cid = localStorage.getItem("company");
    if (cid) {
      const { WxworkAuth } = await import('fmode-ng/core');
      const wwAuth = new WxworkAuth({ cid: cid });
      const profile = await wwAuth.currentProfile();
      
      if (profile) {
        this.currentUser = {
          name: profile.get("name") || profile.get("mobile"),
          avatar: profile.get("avatar"),
          roleName: profile.get("roleName")
        };
        console.log('✅ 用户Profile加载成功:', this.currentUser);
      }
    } else {
      console.warn('⚠️ localStorage中未找到company(cid)');
    }
  } catch (error) {
    console.error('❌ 加载用户Profile失败:', error);
  }
}

📋 涉及文件

修改的文件

  1. src/app/services/project.service.ts

    • 修改 getProjectById() 方法
    • 添加 getProjectFromParse() 私有方法
  2. src/modules/project/pages/project-detail/project-detail.component.ts

    • 修改 initWxworkAuth() 方法,添加cid空值检查
    • 修改 loadData() 方法,添加获取用户错误处理
  3. src/app/pages/designer/project-detail/project-detail.ts

    • 修改 constructor,添加企微认证初始化
    • 添加 loadProfile() 方法
    • 添加 currentUser 属性

🎯 核心原则

企业微信SDK初始化最佳实践

根据 rules/wxwork/auth.mdrules/wxwork/guard-wxwork.md

1. 必须提供cid参数

// ✅ 正确
const cid = localStorage.getItem("company");
if (cid) {
  const wxAuth = new WxworkAuth({ cid: cid });
}

// ❌ 错误:可能传入空字符串
const cid = this.cid || "";  // cid可能为空
const wxAuth = new WxworkAuth({ cid: cid });  // 会报错

2. 参考客服端和管理员端的正确写法

客服端 (customer-service/dashboard/dashboard.ts):

async loadProfile() {
  let cid = localStorage.getItem("company");
  if (cid) {
    let wwAuth = new WxworkAuth({cid:cid})
    let profile = await wwAuth.currentProfile();
    this.currentUser = {
      name: profile?.get("name") || profile?.get("mobile"),
      avatar: profile?.get("avatar"),
      roleName: profile?.get("roleName")
    }
  }
}

管理员端 (admin/dashboard/dashboard.ts):

import { WxworkAuth } from 'fmode-ng/core';

// 使用时先检查cid

3. 错误处理原则

// ✅ 推荐:try-catch包裹,不阻塞页面
try {
  const cid = localStorage.getItem("company");
  if (!cid) {
    console.warn('未找到cid,企微功能不可用');
    return;  // 提前返回,不初始化
  }
  
  const wxAuth = new WxworkAuth({ cid });
  // ... 其他初始化
} catch (error) {
  console.error('初始化失败:', error);
  // 页面仍然可以继续加载
}

🧪 测试验证

测试步骤

  1. 清除localStorage中的company

    localStorage.removeItem("company");
    
  2. 访问管理员项目详情页

    http://localhost:4200/admin/project-detail/iKvYck89zE/order
    
  3. 预期结果

    • ✅ 页面正常加载,不报错
    • ✅ 控制台显示警告:⚠️ 未找到company ID (cid),企微功能将不可用
    • ✅ 项目数据正常显示(从Parse查询)
    • ⚠️ 企微相关功能不可用(用户头像、企微聊天等)
  4. 设置cid后重新测试

    localStorage.setItem("company", "cDL6R1hgSi");  // 使用真实的公司ID
    
  5. 刷新页面

    • ✅ 企微SDK初始化成功
    • ✅ 用户Profile加载成功
    • ✅ 所有功能正常

📖 相关文档

  • rules/wxwork/guard-wxwork.md - 企业微信路由守卫使用指南
  • rules/wxwork/auth.md - 企业微信认证方法文档
  • docs/task/2025102216-team-leader-database-integration.md - Parse数据库集成文档

🎉 总结

修复前

  • ❌ 访问项目详情页报错 TypeError: Cannot set properties of null
  • ❌ 页面无法正常加载
  • ❌ 项目数据查询失败(数据源不一致)

修复后

  • ✅ 页面正常加载,不再报错
  • ✅ 企微SDK初始化有完善的错误处理
  • ✅ cid缺失时给出友好提示,不阻塞页面
  • ✅ 项目数据从Parse数据库正确查询
  • ✅ 用户Profile加载有错误处理机制

核心改进

  1. 防御性编程: 所有企微SDK初始化都添加了空值检查
  2. 优雅降级: cid缺失时不初始化SDK,但不影响页面基本功能
  3. 错误隔离: 企微相关错误不会影响项目数据加载
  4. 统一模式: 所有页面遵循相同的初始化模式

修复完成!