246 lines
12 KiB
Markdown
246 lines
12 KiB
Markdown
# V4 验收报告 — Companies模块 (2026-06-02 更新版)
|
|
|
|
## 验收标准
|
|
1. 接口路径一致
|
|
2. 请求参数一致
|
|
3. 响应格式一致
|
|
4. 错误处理与状态码一致
|
|
5. 业务逻辑一致
|
|
|
|
---
|
|
|
|
## 1. 接口路径对比
|
|
|
|
| # | Chatwoot端点 | GoChat端点 | 状态 |
|
|
|---|---|---|---|
|
|
| 1 | GET /api/v1/accounts/:account_id/companies | GET /api/v1/accounts/:id/companies | ✅ 兼容 (path param命名差异不影响功能) |
|
|
| 2 | POST /api/v1/accounts/:account_id/companies | POST /api/v1/accounts/:id/companies | ✅ 兼容 |
|
|
| 3 | GET /api/v1/accounts/:account_id/companies/search | GET /api/v1/accounts/:id/companies/search | ✅ 兼容 |
|
|
| 4 | GET /api/v1/accounts/:account_id/companies/:id | GET /api/v1/accounts/:id/companies/:company_id | ⚠️ path param名不同 — 功能兼容 |
|
|
| 5 | PATCH/PUT /api/v1/accounts/:account_id/companies/:id | PUT /api/v1/accounts/:id/companies/:company_id | ⚠️ Chatwoot支持PATCH, GoChat仅PUT |
|
|
| 6 | DELETE /api/v1/accounts/:account_id/companies/:id | DELETE /api/v1/accounts/:id/companies/:company_id | ⚠️ path param命名差异 |
|
|
| 7 | POST /companies/:id/destroy_custom_attributes | — | ❌ **缺失** |
|
|
| 8 | DELETE /companies/:id/avatar | — | ❌ **缺失** |
|
|
| 9 | GET /companies/:company_id/contacts | GET /companies/:company_id/contacts | ✅ 兼容 |
|
|
| 10 | GET /companies/:company_id/contacts/search | — | ❌ **缺失** |
|
|
| 11 | POST /companies/:company_id/contacts | POST /companies/:company_id/contacts/:contact_id | ⚠️ **路径差异** — Chatwoot用body传contact_id, GoChat用URL param |
|
|
| 12 | DELETE /companies/:company_id/contacts/:id | DELETE /companies/:company_id/contacts/:contact_id | ✅ 兼容 (path param命名差异) |
|
|
| 13 | GET /companies/:company_id/conversations | GET /companies/:company_id/conversations | ✅ 兼容 |
|
|
| 14 | GET /companies/:company_id/notes | GET /companies/:company_id/notes | ✅ 兼容 |
|
|
|
|
### GoChat额外端点 (Chatwoot不存在):
|
|
| # | GoChat端点 | 说明 |
|
|
|---|---|---|
|
|
| 15 | POST /companies/:company_id/notes | Chatwoot notes只有:index,无:create — GoChat扩展 |
|
|
| 16 | DELETE /companies/:company_id/notes/:note_id | Chatwoot无delete note端点 — GoChat扩展 |
|
|
|
|
### 端点汇总:
|
|
- ✅ 兼容: 7个
|
|
- ⚠️ 路径差异但功能兼容: 4个 (path param命名, PATCH vs PUT)
|
|
- ❌ 缺失: 3个 (destroy_custom_attributes, avatar delete, contacts/search)
|
|
- ⚠️ 新增路径差异: 1个 (AddContact用URL param而非body)
|
|
|
|
**结论: 接口路径 ≈80%一致, 缺失3个端点, 1个端点路径结构不同**
|
|
|
|
---
|
|
|
|
## 2. 请求参数对比
|
|
|
|
### Create Company
|
|
| 字段 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| name | ✅ (required, permit) | ✅ (required, validate:min=1) | ✅ 兼容 |
|
|
| domain | ✅ (permit) | ✅ (json:domain) | ✅ 兼容 |
|
|
| description | ✅ (permit) | ✅ (json:description) | ✅ 兼容 |
|
|
| avatar | ✅ (permit — file upload) | ❌ **缺失** | |
|
|
| additional_attributes | ✅ (permit: {}) | ❌ **缺失** | |
|
|
| custom_attributes | ✅ (permit: {}) | ✅ (json.RawMessage) | ✅ 兼容 |
|
|
|
|
### Update Company
|
|
| 字段 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| name | ✅ | ✅ | ✅ 兼容 |
|
|
| domain | ✅ | ✅ | ✅ 兼容 |
|
|
| description | ✅ | ✅ | ✅ 兼容 |
|
|
| avatar | ✅ | ❌ **缺失** | |
|
|
| custom_attributes | ✅ (merge逻辑) | ⚠️ 全量替换 | ❌ **行为不兼容** |
|
|
| additional_attributes | ✅ | ❌ **缺失** | |
|
|
|
|
### Search
|
|
| 参数 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| q (query string) | ✅ (required — 422 if blank) | ✅ (optional — 空query回退到list) | ⚠️ **行为差异** |
|
|
| page | ✅ | ✅ | ✅ 兼容 |
|
|
| sort | ✅ (Sift gem: name/domain/created_at/last_activity_at/contacts_count) | ✅ (name/domain/created_at/last_activity_at) | ⚠️ GoChat缺少contacts_count排序 |
|
|
|
|
### List (index)
|
|
| 参数 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| page | ✅ | ✅ | ✅ 兼容 |
|
|
| sort | ✅ | ✅ (同上支持) | ⚠️ 缺contacts_count排序 |
|
|
| per_page | — (Chatwoot固定25) | ✅ (动态page_size) | ⚠️ Chatwoot固定25 |
|
|
|
|
### Notes (create)
|
|
| 参数 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| content | — (Chatwoot无create) | ✅ (required) | GoChat扩展功能 |
|
|
|
|
**结论: 请求参数 ≈75%一致, 缺失avatar/additional_attributes, custom_attributes更新行为不兼容**
|
|
|
|
---
|
|
|
|
## 3. 响应格式对比
|
|
|
|
### GoChat当前响应envelope (APIResponse struct):
|
|
```json
|
|
{
|
|
"success": true,
|
|
"data": { ...company object... },
|
|
"meta": { "page": 1, "per_page": 25, "total_count": 100 }
|
|
}
|
|
```
|
|
|
|
### Chatwoot list/search envelope:
|
|
```json
|
|
{
|
|
"meta": { "total_count": N, "page": P },
|
|
"payload": [ { ...company... } ]
|
|
}
|
|
```
|
|
|
|
### 响应envelope差异:
|
|
| 属性 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| 顶层key | payload (数组) / payload (单对象) | data | ⚠️ GoChat用统一`data`而非`payload` |
|
|
| total_count | ✅ | ✅ (total_count in meta) | ✅ 兼容 |
|
|
| page | ✅ | ✅ | ✅ 兼容 |
|
|
| success字段 | ❌ (Chatwoot无) | ✅ | ⚠️ GoChat额外字段 |
|
|
| per_page | ❌ (Chatwoot不输出) | ✅ | ⚠️ GoChat额外字段 |
|
|
|
|
**说明**: GoChat现在使用统一的APIResponse{success, data, meta} envelope。与Chatwoot的`payload` key不同,但使用`total_count`而非`count`。这是一个系统性设计选择(全项目统一),而非Companies模块独有。
|
|
|
|
### Company单对象字段对比:
|
|
| 字段 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| id | ✅ (integer) | ✅ (integer) | ✅ 兼容 |
|
|
| name | ✅ | ✅ | ✅ 兼容 |
|
|
| domain | ✅ | ✅ | ✅ 兼容 |
|
|
| description | ✅ | ✅ | ✅ 兼容 |
|
|
| custom_attributes | ✅ (jsonb) | ✅ (jsonb) | ✅ 兼容 |
|
|
| contacts_count | ✅ (counter cache列) | ❌ **缺失** | ❌ |
|
|
| avatar_url | ✅ | ❌ **缺失** | ❌ |
|
|
| additional_attributes | ✅ (schema有但view不渲染) | ❌ **缺失字段** | ⚠️ |
|
|
| account_id | ❌ (Chatwoot不输出) | ✅ | ⚠️ GoChat额外暴露 |
|
|
| website_url | ❌ (Chatwoot无此字段) | ✅ | ⚠️ GoChat额外字段 |
|
|
| favicon_url | ❌ (Chatwoot用avatar_url) | ✅ | ⚠️ GoChat额外字段 |
|
|
| last_activity_at | ✅ (Unix timestamp integer) | ⚠️ (RFC3339 string) | ❌ **格式不兼容** |
|
|
| created_at | ✅ (Unix timestamp integer) | ⚠️ (RFC3339 string) | ❌ **格式不兼容** |
|
|
| updated_at | ✅ (Unix timestamp integer) | ⚠️ (RFC3339 string) | ❌ **格式不兼容** |
|
|
|
|
### 嵌套资源envelope:
|
|
| 资源 | Chatwoot envelope | GoChat envelope | 状态 |
|
|
|---|---|---|---|
|
|
| contacts | payload[] + meta{total_count} | data[] + meta{total_count,page,per_page} | ⚠️ key不同 |
|
|
| conversations | payload[] (无分页meta) | data[] + meta{total_count,page,per_page} | ⚠️ key不同; GoChat多分页 |
|
|
| notes | payload[] (无分页meta) | data[] + meta{total_count,page,per_page} | ⚠️ key不同; GoChat多分页 |
|
|
|
|
**结论: 响应格式 ≈60%一致 — envelope key(data vs payload)、时间戳格式(RFC3339 vs Unix)、缺失字段(contacts_count/avatar_url)是三大不兼容项**
|
|
|
|
---
|
|
|
|
## 4. 错误处理与状态码对比
|
|
|
|
| 场景 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| Create成功 | 200 (implicit) | 201 | ⚠️ GoChat用201更符合REST,但与Chatwoot不一致 |
|
|
| Create验证失败 | — (Rails异常) | 400 (VALIDATION_ERROR) | ⚠️ 行为差异 |
|
|
| Update成功 | 200 | 200 | ✅ 兼容 |
|
|
| Delete成功 | 200 空body | 204 No Content | ⚠️ Chatwoot返回200空body, GoChat返回204 |
|
|
| Search空query | 422 {error:...} | 回退到list (200) | ❌ **不兼容** |
|
|
| Company不存在 | 404 | 404 (handleServiceError now detects "not found") | ✅ **已修复** |
|
|
| 权限不足 | 403 (Pundit) | — | ❌ **缺失** — GoChat无权限检查 |
|
|
| Companies未启用 | 403 (feature flag) | — | ❌ **缺失** |
|
|
|
|
**结论: 错误处理 ≈50%一致 — 404已修复是好消息, 但Search空query、权限检查、Delete状态码仍不兼容**
|
|
|
|
---
|
|
|
|
## 5. 业务逻辑对比
|
|
|
|
| 功能 | Chatwoot | GoChat | 状态 |
|
|
|---|---|---|---|
|
|
| 排序(Sift) | ✅ name/domain/created_at/last_activity_at/contacts_count | ✅ name/domain/created_at/last_activity_at | ⚠️ 缺contacts_count排序 |
|
|
| 分页 | ✅ 固定25/page | ✅ 动态per_page | ⚠️ 默认值不同 |
|
|
| 搜索ILIKE | ✅ name/domain | ✅ name/domain/description | ⚠️ GoChat多搜description |
|
|
| Contacts搜索 | ✅ name/email/phone/identifier | ❌ **缺失端点** | ❌ |
|
|
| Conversations分页 | ❌ (限20, 无分页) | ✅ (有分页) | ⚠️ 行为差异 |
|
|
| Notes分页 | ❌ (限20, 无分页) | ✅ (有分页) | ⚠️ 行为差异 |
|
|
| Domain唯一性 | ✅ (scoped to account_id) | ❌ **缺失验证** | ❌ |
|
|
| Domain格式验证 | ✅ (regex) | ❌ **缺失** | ❌ |
|
|
| Custom attributes更新 | ✅ merge逻辑 | ❌ 全量替换 | ❌ **行为不兼容** |
|
|
| Avatar处理 | ✅ (ActiveStorage + purge) | ❌ **缺失整个avatar功能** | ❌ |
|
|
| Favicon自动获取 | ✅ (AvatarFromFaviconJob) | ❌ **缺失** | ⚠️ |
|
|
| Contact关联模式 | ✅ 1:N (company_id FK) | ⚠️ M:N (company_contacts关联表) | ❌ **架构不兼容** |
|
|
| Last activity rollup | ✅ (5min interval) | ❌ **缺失** | ❌ |
|
|
| Contacts count cache | ✅ (contacts_count列) | ❌ **缺失** | ❌ |
|
|
|
|
**结论: 业务逻辑 ≈40%一致 — Contact关联架构、custom_attributes merge、domain验证、avatar、last activity是关键缺失**
|
|
|
|
---
|
|
|
|
## 6. 与旧报告(首次验收)对比 — 已修复项
|
|
|
|
| 项 | 旧报告状态 | 当前状态 | 变化 |
|
|
|---|---|---|---|
|
|
| AddContact端点 | ❌ 缺失 | ✅ 已实现 (POST /:company_id/contacts/:contact_id) | ✅ **修复** |
|
|
| RemoveContact端点 | ❌ 缺失 | ✅ 已实现 (DELETE /:company_id/contacts/:contact_id) | ✅ **修复** |
|
|
| Company不存在返回500 | ❌ 返回500 | ✅ 返回404 (handleServiceError检测"not found") | ✅ **修复** |
|
|
| 响应envelope key | ❌ companies/contacts/notes/conversations | ✅ 统一data (APIResponse) | ⚠️ **改善了结构性,但key名仍是data而非payload** |
|
|
| total_count key | ❌ count | ✅ total_count (APIResponse.MetaBody) | ✅ **修复** |
|
|
| Delete返回200+JSON | ❌ 200 + {"message":"company deleted"} | ✅ 204 No Content | ⚠️ **改善了但Chatwoot是200空body而非204** |
|
|
| 排序支持 | ❌ 固定created_at DESC | ✅ name/domain/created_at/last_activity_at | ✅ **修复** |
|
|
|
|
---
|
|
|
|
## 7. 验收结论
|
|
|
|
**验收结果: ❌ 不合格**
|
|
|
|
5项验收标准评分:
|
|
1. **接口路径一致**: ≈80% — 缺3个端点, 1个路径差异
|
|
2. **请求参数一致**: ≈75% — 缺avatar/additional_attributes, custom_attributes行为不同
|
|
3. **响应格式一致**: ≈60% — envelope key(data vs payload)、时间戳格式(RFC3339 vs Unix)、缺失字段
|
|
4. **错误处理与状态码一致**: ≈50% — Search空query、权限检查、Delete状态码
|
|
5. **业务逻辑一致**: ≈40% — Contact关联架构、custom_attributes merge、domain验证
|
|
|
|
**综合一致度: ≈61% (5项加权平均)**
|
|
|
|
### 高优先级不兼容项(影响客户端集成):
|
|
1. **响应envelope key** — `data` vs `payload` (全项目统一设计,改动影响全局)
|
|
2. **时间戳格式** — RFC3339 string vs Unix integer (created_at/updated_at/last_activity_at)
|
|
3. **Contact关联架构** — M:N vs Chatwoot的1:N (数据模型差异,影响所有嵌套查询)
|
|
4. **Custom attributes更新** — 全量替换 vs merge (破坏渐进更新语义)
|
|
5. **Search空query行为** — 回退到list vs 422 (客户端依赖422做验证)
|
|
6. **缺失contacts/search端点** — 无法搜索公司关联联系人
|
|
7. **缺失destroy_custom_attributes端点** — 无法删除指定custom_attributes键
|
|
8. **缺失avatar功能** — 无上传/删除头像
|
|
9. **缺失权限检查** — 无Pundit式权限控制
|
|
|
|
### 中优先级缺失:
|
|
10. Domain唯一性/格式验证缺失
|
|
11. contacts_count counter cache缺失
|
|
12. last_activity_at rollup缺失
|
|
13. additional_attributes字段缺失
|
|
|
|
### 低优先级差异(可容忍):
|
|
14. 分页默认值不同(25 vs 动态)
|
|
15. Conversations/Notes分页(Chatwoot限20 vs GoChat分页)
|
|
16. GoChat额外字段(website_url/favicon_url/account_id)
|
|
17. Delete返回204而非200空body
|
|
18. Create返回201而非200
|
|
|
|
**建议下一步**:
|
|
- 优先修复(影响客户端): 时间戳格式、envelope key、custom_attributes merge、Search空query行为
|
|
- 其次修复(功能完善): contacts/search端点、destroy_custom_attributes、domain验证、avatar功能
|
|
- 架构决策: Contact关联模式是否统一到Chatwoot的1:N (需要CTO评估)
|
|
- 全项目层面: envelope key(data vs payload)是统一设计,需要与CTO确认是否全局切换 |