# 企业微信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.ts` 的 `getProjectById` 方法，优先从Parse查询真实数据：

```typescript
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时：

```typescript
// ❌ 旧代码
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空值检查

```typescript
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的错误处理

```typescript
// 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 中添加企微认证初始化

```typescript
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.md` 和 `rules/wxwork/guard-wxwork.md`：

#### 1. **必须提供cid参数**

```typescript
// ✅ 正确
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`):
```typescript
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`):
```typescript
import { WxworkAuth } from 'fmode-ng/core';

// 使用时先检查cid
```

#### 3. **错误处理原则**

```typescript
// ✅ 推荐：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**
   ```javascript
   localStorage.removeItem("company");
   ```

2. **访问管理员项目详情页**
   ```
   http://localhost:4200/admin/project-detail/iKvYck89zE/order
   ```

3. **预期结果**
   - ✅ 页面正常加载，不报错
   - ✅ 控制台显示警告：`⚠️ 未找到company ID (cid)，企微功能将不可用`
   - ✅ 项目数据正常显示（从Parse查询）
   - ⚠️ 企微相关功能不可用（用户头像、企微聊天等）

4. **设置cid后重新测试**
   ```javascript
   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. **统一模式**: 所有页面遵循相同的初始化模式

---

**修复完成！** ✨

