Files
gochat/V4_companies_verification_report.md
T
2026-06-04 15:44:48 +08:00

12 KiB

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 缺失
参数 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):

{
  "success": true,
  "data": { ...company object... },
  "meta": { "page": 1, "per_page": 25, "total_count": 100 }
}

Chatwoot list/search envelope:

{
  "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 keydata 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式权限控制

中优先级缺失:

  1. Domain唯一性/格式验证缺失
  2. contacts_count counter cache缺失
  3. last_activity_at rollup缺失
  4. additional_attributes字段缺失

低优先级差异(可容忍):

  1. 分页默认值不同(25 vs 动态)
  2. Conversations/Notes分页(Chatwoot限20 vs GoChat分页)
  3. GoChat额外字段(website_url/favicon_url/account_id)
  4. Delete返回204而非200空body
  5. 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确认是否全局切换