# userId 格式检查指南

## 🎯 核心问题

**userId 格式必须完全一致！**

如果格式不一致，会导致：
- ❌ 提示"对方不在线"（错误代码 60011）
- ❌ 无法接听来电
- ❌ 无法发起通话

## 📋 正确的格式

### 医生端（Web）
```
登录 userId: doctor_1
呼叫目标: patient_2
```

### 患者端（小程序）
```
登录 userId: patient_2
呼叫目标: doctor_1
```

## 🔍 检查步骤

### 步骤 1：检查后端接口

#### 医生端获取签名接口
**接口：** `POST /api/tcm/getCallSignature`

**请求：**
```json
{
  "diagnosis_id": 1,
  "patient_id": 2,
  "admin_id": 1
}
```

**期望响应：**
```json
{
  "code": 1,
  "data": {
    "userId": "doctor_1",           // 医生自己的 userId（字符串）
    "patientUserId": "patient_2",   // 要呼叫的患者 userId（字符串）
    "sdkAppId": "1400xxxxxx",       // 字符串或数字都可以
    "userSig": "eJw1..."
  }
}
```

**关键检查点：**
- [ ] `userId` 是字符串 `"doctor_1"`，不是数字 `1`
- [ ] `patientUserId` 是字符串 `"patient_2"`，不是数字 `2`
- [ ] 格式统一：都是 `prefix_number` 格式

#### 患者端获取签名接口
**接口：** `GET /api/tcm/getPatientSignature?patient_id=2`

**期望响应：**
```json
{
  "code": 1,
  "data": {
    "userId": "patient_2",          // 患者自己的 userId（字符串）
    "sdkAppId": "1400xxxxxx",
    "userSig": "eJw1..."
  }
}
```

**关键检查点：**
- [ ] `userId` 是字符串 `"patient_2"`，不是数字 `2`
- [ ] 格式与医生端一致：`prefix_number`

### 步骤 2：检查 Web 端日志

**登录时：**
```javascript
console.log('=== 发起一对一通话 ===')
console.log('后端返回的 patientUserId:', res.patientUserId)  // 应该是 "patient_2"
console.log('最终使用的 targetUserId:', targetUserId)        // 应该是 "patient_2"
```

**关键检查点：**
- [ ] `patientUserId` 是字符串 `"patient_2"`
- [ ] `targetUserId` 是字符串 `"patient_2"`
- [ ] 没有多余的空格或特殊字符

### 步骤 3：检查小程序端日志

**登录时：**
```javascript
console.log('=== 小程序端登录信息 ===')
console.log('userId:', userId)  // 应该是 "patient_2"
```

**关键检查点：**
- [ ] `userId` 是字符串 `"patient_2"`
- [ ] 格式与 Web 端一致

### 步骤 4：对比验证

| 端 | 登录 userId | 呼叫目标 | 格式 |
|----|------------|---------|------|
| Web（医生） | `doctor_1` | `patient_2` | ✅ 一致 |
| 小程序（患者） | `patient_2` | `doctor_1` | ✅ 一致 |

**必须满足：**
- Web 端呼叫的 `patient_2` === 小程序端登录的 `patient_2`
- 小程序端呼叫的 `doctor_1` === Web 端登录的 `doctor_1`

## ⚠️ 常见错误

### 错误 1：数字 vs 字符串

**错误示例：**
```javascript
// ❌ 错误：后端返回数字
{
  "userId": 1,              // 数字
  "patientUserId": 2        // 数字
}

// ✅ 正确：应该返回字符串
{
  "userId": "doctor_1",     // 字符串
  "patientUserId": "patient_2"  // 字符串
}
```

**后果：**
- Web 端使用 `2` 呼叫
- 小程序端登录为 `"patient_2"`
- 格式不匹配，无法通话

### 错误 2：前缀不一致

**错误示例：**
```javascript
// ❌ 错误：前缀不一致
Web 端: "doctor_1"
小程序端: "patient2"  // 缺少下划线

// ✅ 正确：前缀一致
Web 端: "doctor_1"
小程序端: "patient_2"  // 都使用下划线
```

### 错误 3：大小写不一致

**错误示例：**
```javascript
// ❌ 错误：大小写不一致
Web 端: "Doctor_1"
小程序端: "doctor_1"

// ✅ 正确：大小写一致
Web 端: "doctor_1"
小程序端: "doctor_1"
```

## 🛠️ 后端代码检查

### PHP 后端示例

**医生端获取签名：**
```php
public static function getCallSignature(array $params)
{
    $adminId = $params['admin_id'] ?? 0;
    $patientId = $params['patient_id'] ?? 0;
    
    // ✅ 正确：使用字符串格式
    $doctorUserId = 'doctor_' . $adminId;      // "doctor_1"
    $patientUserId = 'patient_' . $patientId;  // "patient_2"
    
    // ❌ 错误：不要这样做
    // $doctorUserId = $adminId;                // 1
    // $patientUserId = $patientId;             // 2
    
    return [
        'sdkAppId' => (int)$config['sdkAppId'],
        'userId' => $doctorUserId,              // "doctor_1"
        'userSig' => $userSig,
        'patientUserId' => $patientUserId,      // "patient_2"
    ];
}
```

**患者端获取签名：**
```php
public static function getPatientSignature(int $patientId)
{
    // ✅ 正确：使用字符串格式
    $patientUserId = 'patient_' . $patientId;  // "patient_2"
    
    // ❌ 错误：不要这样做
    // $patientUserId = $patientId;             // 2
    
    return [
        'sdkAppId' => (int)$config['sdkAppId'],
        'userId' => $patientUserId,             // "patient_2"
        'userSig' => $userSig,
    ];
}
```

## 🧪 测试验证

### 测试 1：打印 userId

**Web 端（浏览器控制台）：**
```javascript
// 在发起通话前
console.log('=== userId 格式检查 ===');
console.log('类型:', typeof res.patientUserId);  // 应该是 "string"
console.log('值:', res.patientUserId);           // 应该是 "patient_2"
console.log('长度:', res.patientUserId.length);  // 应该是 9
```

**小程序端（微信开发者工具控制台）：**
```javascript
// 在登录后
console.log('=== userId 格式检查 ===');
console.log('类型:', typeof userId);  // 应该是 "string"
console.log('值:', userId);           // 应该是 "patient_2"
console.log('长度:', userId.length);  // 应该是 9
```

### 测试 2：对比 userId

**在 Web 端发起呼叫时：**
```javascript
console.log('=== 对比检查 ===');
console.log('Web 端呼叫目标:', targetUserId);        // "patient_2"
console.log('小程序端应该登录为:', targetUserId);    // "patient_2"
console.log('是否完全相等:', targetUserId === "patient_2");  // true
```

### 测试 3：手动验证

1. **Web 端控制台执行：**
   ```javascript
   console.log(JSON.stringify({
     type: typeof res.patientUserId,
     value: res.patientUserId,
     length: res.patientUserId.length
   }));
   ```

2. **小程序端控制台执行：**
   ```javascript
   console.log(JSON.stringify({
     type: typeof userId,
     value: userId,
     length: userId.length
   }));
   ```

3. **对比结果：**
   - 类型必须都是 `"string"`
   - 值必须完全相同
   - 长度必须相同

## 📊 诊断表

| 检查项 | Web 端 | 小程序端 | 状态 |
|--------|--------|----------|------|
| userId 类型 | string | string | ✅ |
| userId 值 | doctor_1 | patient_2 | ✅ |
| 呼叫目标类型 | string | string | ✅ |
| 呼叫目标值 | patient_2 | doctor_1 | ✅ |
| 格式一致性 | prefix_number | prefix_number | ✅ |

## 🔧 修复建议

### 如果后端返回的是数字

**修改后端代码：**
```php
// 在返回前确保转换为字符串
return [
    'userId' => (string)$doctorUserId,      // 强制转换为字符串
    'patientUserId' => (string)$patientUserId,
    // ...
];
```

### 如果前端收到的是数字

**修改前端代码：**
```javascript
// Web 端
const targetUserId = String(res.patientUserId);  // 确保是字符串

// 小程序端
const userId = String(signatureData.userId);     // 确保是字符串
```

## ✅ 验证清单

在测试前，请确认：

- [ ] 后端返回的 userId 是字符串格式
- [ ] 后端返回的 patientUserId 是字符串格式
- [ ] Web 端日志显示正确的 userId 格式
- [ ] 小程序端日志显示正确的 userId 格式
- [ ] Web 端呼叫目标与小程序端登录 userId 完全一致
- [ ] 小程序端呼叫目标与 Web 端登录 userId 完全一致
- [ ] 没有多余的空格或特殊字符
- [ ] 大小写完全一致

## 📞 如果仍然有问题

请提供以下信息：

1. **后端接口响应**（隐藏 userSig）：
   ```json
   {
     "userId": "???",
     "patientUserId": "???",
     "sdkAppId": "???"
   }
   ```

2. **Web 端日志**：
   ```
   类型: ???
   值: ???
   长度: ???
   ```

3. **小程序端日志**：
   ```
   类型: ???
   值: ???
   长度: ???
   ```

4. **具体错误信息**：
   - 错误代码
   - 错误提示

---

**最后更新：** 2024-03-04
**版本：** 1.0
