Reorganize repo: backend/, deploy/, docs/ layout + AGENTS.md
Restructure the monorepo into clear top-level directories: - backend/: Go module root (cmd, internal, pkg, configs, migrations, docs/swagger, scripts, tests, go.mod, Makefile, .air.toml) - deploy/: Docker (Dockerfile, docker-compose*), quickstart, fluentd - docs/: project documentation + reports/ (moved from repo root) - AGENTS.md: new AI coding-agent guide at repo root Update all references to the new layout: - Dockerfile: COPY backend/go.mod, COPY backend/ (context = repo root) - docker-compose files: context ../.., dockerfile deploy/docker/Dockerfile, env_file ../../.env, volume mounts ../../backend:/app - deploy/quickstart/compose.yaml: dockerfile deploy/docker/Dockerfile - CI: working-directory: backend for go commands, file deploy/docker/Dockerfile, coverage path backend/coverage.out, health_check backend/scripts/ - backend/Makefile: docker target uses -f ../deploy/docker/Dockerfile ../ - README: architecture tree, quickstart, config paths updated Move root stray scripts (rename_models.*, run_m11_tests.sh, verify_build.sh, gorm_bool_main.go) to backend/scripts/legacy/. All moves via git mv to preserve history. Build, vet, SQLite tests, and docker compose config verified.
This commit is contained in:
@@ -0,0 +1,547 @@
|
||||
# GoChat 对齐优化计划 — 最大化并行开发
|
||||
|
||||
生成时间: 2026-06-03
|
||||
基于: GAP_REPORT.md 差距分析 + 依赖拓扑分析
|
||||
|
||||
---
|
||||
|
||||
## 一、修正关键事实
|
||||
|
||||
原始差距报告中"60个stub模型未编译"的说法需修正:
|
||||
|
||||
- 60个 .go.txt 文件中 **50个** 已有对应的编译版本(位于 automation/csat/canned/channel 等子包)
|
||||
- **仅10个** 是真正的未编译 stub: `automation_action`, `call`, `captain_assistant_inbox`,
|
||||
`channelable`, `contactable`, `data_import`, `email_template`, `enums`, `message_reaction`, `report`
|
||||
- AutoMigrate 中注册的所有模型均已编译可运行
|
||||
- GoChat 端点数577 ≠ 功能完成度,大量是薄壳handler(单方法, <100行)
|
||||
|
||||
---
|
||||
|
||||
## 二、依赖拓扑分析
|
||||
|
||||
### 2.1 模块间导入依赖矩阵
|
||||
|
||||
通过分析 handler/service 的 import 语句,确定模块间依赖边界:
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ model (78+编译) │ ← 所有模块的基础依赖
|
||||
└─────────────────────────┘
|
||||
│
|
||||
┌────────────────────┼────────────────────┐
|
||||
│ │ │
|
||||
┌────┴─────┐ ┌────┴─────┐ ┌────┴─────┐
|
||||
│ repository │ │ service │ │ auth/security │
|
||||
│ (92) │ │ (95) │ │ (22+5) │
|
||||
└───────────┘ └───────────┘ └───────────────┘
|
||||
│ │
|
||||
┌────┴──────────────┬─────┴──────────┐
|
||||
│ │ │
|
||||
┌───┴────┐ ┌───────┴──────┐ ┌─────┴─────┐
|
||||
│ handler │ │ 子包(automation │ │ 子包(channel │
|
||||
│ (106) │ │ /csat/canned) │ │ /campaign) │
|
||||
└─────────┘ └──────────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### 2.2 可并行的独立域
|
||||
|
||||
以下域之间 **无直接代码依赖**(仅共享model层),可以完全并行开发:
|
||||
|
||||
| 并行域 (Stream) | 包范围 | 依赖边界 | 预估代码量 |
|
||||
|----------------|--------|---------|-----------|
|
||||
| **S1: SLA追踪域** | model.AppliedSLA/SlaEvent/SlaPolicy + sla相关service | 仅依赖model层 | ~400行新增 |
|
||||
| **S2: CSAT收集域** | csat.CsatSurveyResponse + csat_survey_service + 公开API | 仅依赖model+automation | ~350行新增 |
|
||||
| **S3: 对话查找域** | conversation_service ConversationFinder逻辑 | 依赖model+repository | ~800行新增 |
|
||||
| **S4: 智能分配域** | autoassignment + assignable_agent_handler | 依赖model+repository | ~500行新增 |
|
||||
| **S5: Contact级联域** | contact_merge_service 级联更新 | 依赖model+多个repository | ~600行新增 |
|
||||
| **S6: 模型激活域** | 10个真实stub .go→.go 编译激活 | 仅需model层改动 | ~100行改动 |
|
||||
| **S7: Channel发送域** | 各channel send_on_*_service | 依赖channel子包+model | ~1500行新增 |
|
||||
| **S8: 报表聚合域** | reporting_events 聚合+rollup | 仅依赖model+repository | ~500行新增 |
|
||||
| **S9: CRM集成域** | crm processor/mapper/client | 仅依赖model+service | ~600行新增 |
|
||||
| **S10: 测试域** | model/service/handler test补全 | 依赖所有业务模块 | ~3000行新增 |
|
||||
|
||||
### 2.3 不可并行的串行依赖链
|
||||
|
||||
```
|
||||
S6(模型激活) → S1(SLA追踪) → AppliedSLA已编译,S6不阻塞S1
|
||||
S6(模型激活) → S8(报表聚合) → report.go.txt需先激活
|
||||
S6(模型激活) → S9(CRM) → data_import需先激活
|
||||
S6(模型激活) → 其他stub相关模块
|
||||
|
||||
结论: S6是前置任务,但仅需1-2天完成(10个文件改名+AutoMigrate注册)
|
||||
完成S6后,S1-S9全部可并行启动
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 三、并行开发计划 — 4 Wave 执行
|
||||
|
||||
### Wave 0: 前置准备 (1-2天, 串行)
|
||||
|
||||
| 任务 | 说明 | 产出 |
|
||||
|------|------|------|
|
||||
| **W0-T1: 激活10个stub模型** | automation_action→automation_action.go, call→call.go, captain_assistant_inbox→captain_assistant_inbox.go, channelable→channelable.go, contactable→contactable.go, data_import→data_import.go, email_template→email_template.go, enums→enums.go(合并到common.go), message_reaction→message_reaction.go, report→report.go | 所有.go.txt改名.go, 修复编译错误, 注册AutoMigrate |
|
||||
| **W0-T2: 建立测试基线** | 为78个model创建基础CRUD+约束测试模板 | model_test.go模板, 至少覆盖P0模型 |
|
||||
| **W0-T3: 修复现有编译问题** | 确保所有.go.txt改名后项目编译通过 | `go build ./...` 成功 |
|
||||
|
||||
> W0完成后立即启动 Wave 1 的所有并行流
|
||||
|
||||
### Wave 1: P0核心功能补全 (2-3周, 6个并行流)
|
||||
|
||||
#### Stream S1: SLA追踪完善 [预计5天]
|
||||
|
||||
```
|
||||
依赖: model.AppliedSLA/SlaEvent/SlaPolicy (已编译)
|
||||
参考: Chatwoot enterprise/app/controllers/applied_slas_controller.rb (70行)
|
||||
enterprise/app/services/sla/evaluate_applied_sla_service.rb (107行)
|
||||
|
||||
任务清单:
|
||||
S1-1: applied_sla_service.go — 完善SLA评估逻辑
|
||||
- 会话匹配SLA策略时创建AppliedSLA
|
||||
- SLA状态跟踪(active → hit → breached)
|
||||
- 参考 Chatwoot EvaluateAppliedSlaService
|
||||
S1-2: applied_sla_handler.go — 增加download/metrics API
|
||||
- GET /api/v1/accounts/:id/applied_slas (列表+过滤)
|
||||
- GET /api/v1/accounts/:id/applied_slas/download (CSV导出)
|
||||
S1-3: sla_event_service.go — SLA阈值事件追踪
|
||||
- 当AppliedSLA达到阈值时创建SlaEvent
|
||||
- FRT/NRT事件类型处理
|
||||
S1-4: applied_sla_test.go + sla_event_test.go — 测试覆盖
|
||||
```
|
||||
|
||||
#### Stream S2: CSAT Survey Response收集 [预计5天]
|
||||
|
||||
```
|
||||
依赖: csat.CsatSurveyResponse (已编译), automation包
|
||||
参考: Chatwoot csat_survey_responses_controller.rb (54行)
|
||||
|
||||
任务清单:
|
||||
S2-1: csat_survey_response_service.go — 独立service文件
|
||||
- 创建/提交/统计逻辑
|
||||
- 公开API端点 /public/api/v1/conversations/:uuid/csats
|
||||
S2-2: csat_survey_response_handler.go — 公开API handler
|
||||
- 无需认证的公开提交端点
|
||||
- 验证conversation UUID有效性
|
||||
S2-3: csat_survey_response_test.go — 测试覆盖
|
||||
- 公开端点无需认证验证
|
||||
- 统计聚合准确性
|
||||
```
|
||||
|
||||
#### Stream S3: Conversation Finder 7过滤器 [预计7天] ⚡ 最大量
|
||||
|
||||
```
|
||||
依赖: conversation_service, model, repository, search包
|
||||
参考: Chatwoot app/finders/conversation_finder.rb (217行)
|
||||
mailbox/conversation_finder.rb + 5个strategy文件
|
||||
|
||||
任务清单:
|
||||
S3-1: conversation_finder.go — 核心查找器(新文件)
|
||||
- 7种过滤器实现:
|
||||
1. status (open/resolved/pending/all)
|
||||
2. assignee_type (me/unassigned/all)
|
||||
3. sort_by (latest/created_at/last_activity)
|
||||
4. order (asc/desc)
|
||||
5. labels (多标签AND/OR组合)
|
||||
6. inbox_ids (多inbox过滤)
|
||||
7. tags (自定义标签)
|
||||
- 分页: page/per_count参数
|
||||
- 精确匹配Chatwoot SQL语义
|
||||
S3-2: conversation_finder_strategy.go — 策略模式
|
||||
- BaseStrategy + 各过滤策略子类
|
||||
- 可组合的过滤链
|
||||
S3-3: conversation_handler.go — 扩展index方法
|
||||
- 传入所有7种过滤参数
|
||||
- 与现有conversation_service协同
|
||||
S3-4: conversation_finder_test.go — 7种过滤+组合测试
|
||||
```
|
||||
|
||||
#### Stream S4: 智能分配(Assignable Agent) [预计5天]
|
||||
|
||||
```
|
||||
依赖: autoassignment包, model, repository
|
||||
参考: Chatwoot assignable_agents_controller.rb (24行) + 5个auto_assignment service
|
||||
|
||||
任务清单:
|
||||
S4-1: assignable_agent_service.go — 扩展智能查询
|
||||
- inbox_ids[] 过滤
|
||||
- 技能匹配(skill-based)
|
||||
- 可用性检查(online/offline)
|
||||
- 负载计算(current_conversations_count)
|
||||
- 返回带评分的排序agent列表
|
||||
S4-2: assignable_agent_handler.go — 扩展到完整Controller
|
||||
- 从76行单方法→完整查询handler
|
||||
- GET /api/v1/accounts/:id/inboxes/:id/assignable_agents
|
||||
- 支持参数: inbox_ids[], skill, availability
|
||||
S4-3: round_robin_service.go — 完善自动分配策略
|
||||
- 与现有autoassignment包联动
|
||||
- RoundRobin + LowestLoad策略完善
|
||||
S4-4: assignable_agent_test.go — 测试覆盖
|
||||
```
|
||||
|
||||
#### Stream S5: Contact Merge级联更新 [预计5天]
|
||||
|
||||
```
|
||||
依赖: contact_service, 7+个repository
|
||||
参考: Chatwoot contact_merge_action.rb (62行)
|
||||
|
||||
任务清单:
|
||||
S5-1: contact_merge_service.go — 完善级联逻辑
|
||||
- 7+表级联迁移:
|
||||
contact → conversation → message → label
|
||||
→ inbox_member → csat → note → custom_attribute
|
||||
- 合并前冲突检测(同名/同邮箱)
|
||||
- 合并后清理(删除source contact,去重关联)
|
||||
S5-2: contact_merge_handler.go — 扩展API
|
||||
- 从78行薄壳→完整merge handler
|
||||
- POST /api/v1/accounts/:id/actions/contact_merges
|
||||
- 验证: source≠target, 两者存在, 权限检查
|
||||
S5-3: contact_merge_test.go — 级联完整性测试
|
||||
- 7表数据一致性验证
|
||||
- 合并后关联正确性验证
|
||||
```
|
||||
|
||||
#### Stream S6: Working Hours营业时间 [预计3天]
|
||||
|
||||
```
|
||||
依赖: model层, campaign包(现有片段)
|
||||
参考: Chatwoot working_hour.rb (93行)
|
||||
|
||||
任务清单:
|
||||
S6-1: working_hour_service.go — 完善营业时间约束
|
||||
- WorkingHour CRUD (周一~周日, 每天多个时段)
|
||||
- 营业时间查询: isWithinWorkingHours()
|
||||
- 影响: autoassignment(非营业时不分配), SLA计算(暂停), campaign(营业时发送)
|
||||
S6-2: working_hour_handler.go — API端点
|
||||
- CRUD /api/v1/accounts/:id/inboxes/:id/working_hours
|
||||
S6-3: working_hour_test.go — 测试覆盖
|
||||
```
|
||||
|
||||
### Wave 1 并行执行拓扑
|
||||
|
||||
```
|
||||
┌── S1 (SLA追踪) ──┐
|
||||
│ │
|
||||
├── S2 (CSAT收集) ──┤
|
||||
│ │ 全部可同时启动
|
||||
├── S3 (对话查找) ──┤ 无交叉依赖
|
||||
│ │
|
||||
├── S4 (智能分配) ──┤
|
||||
│ │
|
||||
├── S5 (Contact级联) ─┤
|
||||
│ │
|
||||
└── S6 (营业时间) ──┘
|
||||
|
||||
S1-S6 完成后 → 进入 Wave 2
|
||||
预计总工期: max(7天) ≈ 7个工作日 (而非串行30天)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Wave 2: P1功能补全 + 服务深化 (3-5周, 5个并行流)
|
||||
|
||||
#### Stream S7: Channel Provider发送层完善 [预计10天] ⚡ 最大量
|
||||
|
||||
```
|
||||
依赖: 各channel子包, model
|
||||
参考: Chatwoot 14个 send_on_*_service
|
||||
|
||||
任务清单:
|
||||
S7-1: WhatsApp发送层 (最简→最完整)
|
||||
- send_on_whatsapp_service.go (消息发送+模板+媒体)
|
||||
- WhatsApp Webhook完善(消息状态回调: sent/delivered/read)
|
||||
S7-2: Twilio SMS发送层
|
||||
- send_on_twilio_sms_service.go
|
||||
- SMS状态回调处理
|
||||
S7-3: Instagram发送层
|
||||
- send_on_instagram_service.go
|
||||
- Instagram消息类型处理
|
||||
S7-4: Email Channel发送层完善
|
||||
- SMTP发送+IMAP收取(替换goimap_stub)
|
||||
- 集成Go IMAP库(如go-imap)
|
||||
S7-5: 其他channel发送层
|
||||
- LINE/TikTok/Twitter/Facebook/Microsoft/Telegram
|
||||
- 每个channel: send+webhook+callback三层
|
||||
```
|
||||
|
||||
#### Stream S8: 报表聚合完善 [预计7天]
|
||||
|
||||
```
|
||||
依赖: model.ReportingEvent/ReportingEventsRollup (已编译)
|
||||
参考: Chatwoot reporting_events: backfill_service(142行)+rollup_service(81行)+metric_registry
|
||||
|
||||
任务清单:
|
||||
S8-1: reporting_backfill_service.go — 事件回填
|
||||
- 定时任务: 补全缺失的ReportingEvent
|
||||
S8-2: reporting_rollup_service.go — 聚合计算
|
||||
- 日/周/月级Rollup计算
|
||||
- agent/account/conversation维度
|
||||
S8-3: reporting_metric_registry.go — 指标注册
|
||||
- 统计指标定义(消息数/响应时间/FRT/NRT等)
|
||||
S8-4: reporting_handler.go — 扩展报表API
|
||||
- 从56行薄壳→完整报表查询
|
||||
- 多维度+时间范围过滤
|
||||
```
|
||||
|
||||
#### Stream S9: CRM + 通知订阅 + LLM Formatter [预计7天]
|
||||
|
||||
```
|
||||
依赖: model层, service层
|
||||
参考: Chatwoot crm(9services), notification_subscription(4文件), llm_formatter(5文件)
|
||||
|
||||
任务清单:
|
||||
S9-1: CRM集成 (LeadSquared)
|
||||
- crm_processor_service.go
|
||||
- crm_mapper_service.go
|
||||
- crm_client_service.go
|
||||
S9-2: Notification Subscription
|
||||
- notification_subscription_model.go
|
||||
- notification_subscription_service.go
|
||||
- notification_subscription_handler.go
|
||||
S9-3: LLM Formatter完善
|
||||
- conversation_formatter.go
|
||||
- article_formatter.go
|
||||
- contact_formatter.go
|
||||
- text_formatter.go
|
||||
- (现有llm_response_parser保留为default formatter)
|
||||
```
|
||||
|
||||
#### Stream S10: 薄壳Handler丰富化 [预计10天]
|
||||
|
||||
```
|
||||
依赖: 对应的service/model
|
||||
以下handler需要从薄壳(<100行)扩展到完整实现:
|
||||
|
||||
最薄(需最大扩展):
|
||||
- assignable_agent_handler (76行) → S4已覆盖
|
||||
- contact_merge_handler (78行) → S5已覆盖
|
||||
- reporting_event_handler (56行) → S8已覆盖
|
||||
- delivery_status_handler (51行) → 需Twilio回调联动
|
||||
- captain_assistant_response_handler (46行) → 需扩展
|
||||
- captain_bulk_action_handler (62行) → 需扩展
|
||||
|
||||
中等扩展:
|
||||
- push_subscription_handler (89行) → web push+FCM/APNs
|
||||
- inbox_limit_handler (88行) → 容量限制+溢出策略
|
||||
- inbox_csat_template_handler (95行) → 模板CRUD+自定义问题
|
||||
- audit_handler (82行) → 完善审计日志查询
|
||||
- conversation_participant_handler → 自动分配+负载均衡联动
|
||||
- draft_message_handler → 实时编辑+多agent协作
|
||||
- note_handler → 关联conversation+contact+company
|
||||
```
|
||||
|
||||
#### Stream S11: 数据导入 + 邮件模板 [预计5天]
|
||||
|
||||
```
|
||||
依赖: model.DataImport/EmailTemplate (W0激活后)
|
||||
参考: Chatwoot data_import (35行model) + email_template
|
||||
|
||||
任务清单:
|
||||
S11-1: data_import_service.go — 批量导入联系人
|
||||
- CSV解析+验证+批量创建
|
||||
- 导入进度跟踪+错误报告
|
||||
S11-2: email_template_service.go — 件模板系统
|
||||
- DB resolver: 模板查找+变量替换
|
||||
- 通知邮件模板CRUD
|
||||
S11-3: data_import_handler.go + email_template_handler.go
|
||||
```
|
||||
|
||||
### Wave 2 并行执行拓扑
|
||||
|
||||
```
|
||||
┌── S7 (Channel发送层) ──┐ ← 最大的并行流,10天
|
||||
│ │
|
||||
├── S8 (报表聚合) ────────┤
|
||||
│ │ 可同时启动
|
||||
├── S9 (CRM+通知+LLM) ───┤ 无交叉依赖
|
||||
│ │
|
||||
├── S10 (薄壳Handler) ────┤ ← 部分依赖S7/S8/S9完成
|
||||
│ │
|
||||
└── S11 (数据导入+模板) ──┘
|
||||
|
||||
S10的前6项已在Wave 1(S4/S5/S8)覆盖
|
||||
S10其余项可与S7-S9并行,部分需等S7完成
|
||||
预计总工期: max(10天) ≈ 10个工作日 (而非串行40天)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Wave 3: P2体验完善 + 1:1深度验证 (2-3周, 3个并行流)
|
||||
|
||||
#### Stream S12: P2功能实现 [预计5天]
|
||||
|
||||
```
|
||||
任务清单:
|
||||
S12-1: message_reaction_model.go + handler/service (表情回复)
|
||||
S12-2: instance_status_handler.go (实例状态API)
|
||||
S12-3: onboarding_handler.go (新用户引导)
|
||||
S12-4: liquid_template_engine.go (Campaign模板引擎)
|
||||
- 或集成Go模板库替代Liquid
|
||||
S12-5: platform_banner_handler.go (平台公告横幅)
|
||||
S12-6: super_admin_handler.go (完整权限管理)
|
||||
```
|
||||
|
||||
#### Stream S13: 1:1逻辑深度验证 [预计10天] ⚡ 最关键
|
||||
|
||||
```
|
||||
对每个"已实现"模块做逐项1:1逻辑对比:
|
||||
S13-1: Conversation — 验证排序/分页/过滤/状态转换与Chatwoot完全一致
|
||||
S13-2: Message — 验证消息类型/搜索/附件/推送与Chatwoot一致
|
||||
S13-3: Inbox — 验证channel类型/成员管理/容量限制与Chatwoot一致
|
||||
S13-4: Auth — 验证SSO/MFA/权限检查与Chatwoot一致
|
||||
S13-5: Automation — 验证规则触发/动作执行/宏执行与Chatwoot一致
|
||||
S13-6: Contact — 验证搜索/合并/标签/自定义属性与Chatwoot一致
|
||||
每个验证项产出: 1:1_diff_report.md (差异清单)
|
||||
```
|
||||
|
||||
#### Stream S14: 测试覆盖提升 [持续进行]
|
||||
|
||||
```
|
||||
目标: 272 → 834+ test files
|
||||
S14-1: Model层测试: 6 → 63 (最优先)
|
||||
S14-2: Service层测试: 48 → 132
|
||||
S14-3: Handler层测试: 100 → 138
|
||||
S14-4: Repository层测试: 55 → 保持(GoChat独有优势)
|
||||
|
||||
策略: 每个Wave的Stream产出代码时,同时产出测试
|
||||
S14不是独立流,而是附加在每个Stream的任务清单中
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Wave 4: 持续优化 (与Wave 2-3并行开始)
|
||||
|
||||
```
|
||||
S15: 服务覆盖率提升: 25% → 80%
|
||||
- 补全每个Chatwoot service域的Go等效service
|
||||
- 优先: WhatsApp(3%→80%), Conversation(7%→80%), Message(8%→80%)
|
||||
|
||||
S16: 性能优化
|
||||
- Repository层查询优化(N+1问题, 批量查询)
|
||||
- WebSocket推送优化
|
||||
- Cache策略(Redis缓存热数据)
|
||||
|
||||
S17: 文档完善
|
||||
- API文档(Swagger/OpenAPI)
|
||||
- 架构文档更新
|
||||
- 部署文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、执行节奏总览
|
||||
|
||||
```
|
||||
Week 1-2: W0 (1-2天) → Wave 1 全6流并行启动
|
||||
Week 2-3: Wave 1 完成 → Wave 2 全5流并行启动
|
||||
Week 4-7: Wave 2 完成 → Wave 3 全3流并行启动
|
||||
Week 8+: Wave 3 完成 → Wave 4 持续优化
|
||||
|
||||
关键路径(最长):
|
||||
W0(2天) → S3(7天) → S7(10天) → S13(10天) = 约29天
|
||||
vs 串行执行: W0+S1+S2+S3+S4+S5+S6+S7+S8+S9+S10+S11+S12+S13 = 约85天
|
||||
并行加速比: 85/29 ≈ 3x
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、并行开发组织建议
|
||||
|
||||
### 5.1 Worker分配方案
|
||||
|
||||
| Worker角色 | 负责Stream | 技能要求 |
|
||||
|-----------|-----------|---------|
|
||||
| Worker-A (核心) | S1 SLA + S2 CSAT + S6 WorkingHours | Go/Gin + GORM + model层 |
|
||||
| Worker-B (对话) | S3 ConversationFinder + S4 AssignableAgent | Go + 搜索/查询逻辑 |
|
||||
| Worker-C (数据) | S5 ContactMerge + S11 DataImport/Email | Go + 数据迁移逻辑 |
|
||||
| Worker-D (Channel) | S7 Channel发送层 | Go + HTTP/API + 各Channel协议 |
|
||||
| Worker-E (报表) | S8 Reporting + S9 CRM/Notification | Go + 统计计算 |
|
||||
| Worker-F (Handler) | S10 薄壳丰富化 | Go + Handler模式 |
|
||||
| Worker-G (验证) | S13 1:1深度验证 | 对比分析 + 测试编写 |
|
||||
| Worker-H (测试) | S14 测试覆盖(附加到各Stream) | Go testing |
|
||||
|
||||
### 5.2 分支策略
|
||||
|
||||
```
|
||||
main
|
||||
├── wave0/stub-activation (W0前置)
|
||||
├── wave1/s1-sla-tracking (S1)
|
||||
├── wave1/s2-csat-collection (S2)
|
||||
├── wave1/s3-conversation-finder (S3)
|
||||
├── wave1/s4-assignable-agent (S4)
|
||||
├── wave1/s5-contact-merge (S5)
|
||||
├── wave1/s6-working-hours (S6)
|
||||
├── wave2/s7-channel-send (S7)
|
||||
├── wave2/s8-reporting (S8)
|
||||
... etc
|
||||
|
||||
每个Stream独立分支,完成后merge到main
|
||||
冲突解决策略: model层改动先merge(W0), 业务层后merge(Wave 1-3)
|
||||
```
|
||||
|
||||
### 5.3 每日同步点
|
||||
|
||||
```
|
||||
1. W0完成后: 所有.go.txt激活, go build通过 → 触发Wave 1
|
||||
2. Wave 1每日: 各Stream汇报进度, 检查model层是否有交叉改动
|
||||
3. Wave 1完成后: P0功能可用 → 触发Wave 2
|
||||
4. Wave 2完成后: P1功能可用 → 触发Wave 3
|
||||
5. Wave 3完成后: 1:1验证报告 → 确认达标或迭代
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、风险与缓解
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|---------|
|
||||
| 10个stub激活后编译错误 | 阻塞全部Wave 1 | W0中逐个激活+编译验证, 不批量 |
|
||||
| model层交叉改动 | Stream间merge冲突 | model层改动集中在W0, 后续只加不改 |
|
||||
| ConversationFinder 7种过滤语义不一致 | S3产出不达标 | 参考Chatwoot conversation_finder.rb逐行对比 |
|
||||
| Channel发送层各协议差异大 | S7工期超预期 | 按channel优先级分批(WhatsApp→Twilio→其余) |
|
||||
| 测试覆盖跟不上业务开发 | 回归风险 | S14附加在每个Stream, 不独立延后 |
|
||||
| S13 1:1验证发现深度差距 | 需要额外迭代 | Wave 3预留buffer, 验证问题回流到对应Stream |
|
||||
|
||||
---
|
||||
|
||||
## 七、验证标准
|
||||
|
||||
每个Stream完成时的验收标准:
|
||||
|
||||
1. **编译**: `go build ./...` 通过
|
||||
2. **测试**: Stream内所有新增代码有对应test文件, `go test ./internal/...` 通过
|
||||
3. **1:1对比**: handler/service的行为与Chatwoot对应controller/service等价:
|
||||
- 请求参数相同
|
||||
- 响应JSON结构相同(字段名/嵌套/null处理)
|
||||
- 错误响应码相同
|
||||
- 副作用(事件触发/自动分配)相同
|
||||
- 授权检查范围相同
|
||||
4. **AutoMigrate**: 新增model注册到app.go的autoMigrate列表
|
||||
5. **无.go.txt残留**: 激活的stub删除对应的.go.txt文件
|
||||
|
||||
---
|
||||
|
||||
## 八、Chatwoot参考文件索引 (每个Stream的对照基准)
|
||||
|
||||
| Stream | Chatwoot参考文件 | 行数 |
|
||||
|--------|----------------|------|
|
||||
| S1 | enterprise/app/controllers/applied_slas_controller.rb | 70 |
|
||||
| S1 | enterprise/app/services/sla/evaluate_applied_sla_service.rb | 107 |
|
||||
| S1 | enterprise/app/models/applied_sla.rb | 77 |
|
||||
| S2 | app/controllers/api/v1/accounts/csat_survey_responses_controller.rb | 54 |
|
||||
| S3 | app/finders/conversation_finder.rb | 217 |
|
||||
| S3 | app/services/mailbox/conversation_finder.rb | ~100 |
|
||||
| S3 | app/services/mailbox/conversation_finder_strategies/ (5文件) | ~150 |
|
||||
| S4 | app/controllers/api/v1/accounts/assignable_agents_controller.rb | 24 |
|
||||
| S4 | app/services/auto_assignment/ (5个service) | ~200 |
|
||||
| S5 | app/actions/contact_merge_action.rb | 62 |
|
||||
| S6 | app/models/working_hour.rb | 93 |
|
||||
| S7 | app/services/whatsapp/ (31个service) | ~3000 |
|
||||
| S8 | app/services/reporting_events/ (4个service) | ~400 |
|
||||
| S9 | app/services/crm/ (9个service) | ~600 |
|
||||
| S9 | app/services/notification/ (5个service) | ~300 |
|
||||
| S11 | app/models/data_import.rb | 35 |
|
||||
| S11 | app/services/email_templates/ | ~100 |
|
||||
|
||||
---
|
||||
|
||||
*计划版本: v1.0 | 总工期预估: 7-10周 (并行) vs 20-25周 (串行) | 加速比: ~3x*
|
||||
@@ -0,0 +1,84 @@
|
||||
# GoChat vs Chatwoot 差距报告及优化计划
|
||||
|
||||
生成时间: 2026-06-04 (Phase 3 完成后更新)
|
||||
|
||||
## 1. 项目概况
|
||||
|
||||
| 指标 | Chatwoot (Ruby) | GoChat (Go) |
|
||||
|------|-----------------|-------------|
|
||||
| 源码文件数 | 722 `.rb` | 612 `.go` (非测试) |
|
||||
| 源码行数 | 42,145 行 | 112,059 行 |
|
||||
| 测试行数 | N/A | 75,685 行 |
|
||||
| 测试状态 | N/A | 41 ok / 10 FAIL |
|
||||
| 编译状态 | N/A | `go build ./...` PASS |
|
||||
|
||||
## 2. 已完成对齐 (Phase 1-3)
|
||||
|
||||
### Phase 1: 路由补齐 ✅
|
||||
- 704条路由注册完成
|
||||
- 55条P0关键路由新增
|
||||
- P1 alias路径补齐(labels→tags等)
|
||||
- Gin radix tree参数名冲突修复(5处)
|
||||
- 路由重复注册修复
|
||||
|
||||
### Phase 2: Service深度对齐 ✅ (10 Streams)
|
||||
| Stream | 内容 | 关键改动 |
|
||||
|--------|------|----------|
|
||||
| S1 | CaptainAssistant CRUD | Edited=changed逻辑 |
|
||||
| S2 | ConversationFinder | labels OR语义 + FilterResult(count指标) + Query/SourceID/SORT_OPTIONS(8种排序)/updated_within |
|
||||
| S3 | assignable_agents | AvailableAgent intersection(round-robin) |
|
||||
| S4 | contact_merge | deep_merge字段优先级(JSON递归合并) |
|
||||
| S5 | csat_survey | 5种过滤器(created_at/agent/inbox/team/rating) |
|
||||
| S6 | draft_messages | 已有完整CRUD(DB-based优于Redis) |
|
||||
| S7 | delivery_status | 已有service+webhook路由 |
|
||||
| S8 | custom_attribute_defs | key format regex + STANDARD_ATTRIBUTES冲突检测 + expanded display_type enum |
|
||||
| S9 | account_user | 新建service: AddUser/RemoveUser/UpdateAvailability/UpdateRole + EventBus publish |
|
||||
| S10 | Event Bus | 新增11个topic(status_changed/contact_changed/read/mention等) + AccountUser 3个topic |
|
||||
|
||||
### Phase 3: 逻辑对齐 ✅ (3 Waves)
|
||||
| Wave | 内容 | 关键改动 |
|
||||
|------|------|----------|
|
||||
| P3W1 | ConversationFinder | Query/SourceID/SORT_OPTIONS/updated_within 已在更早session实现 |
|
||||
| P3W2 | 错误码批量对齐 | 500→422替换(172处→0处残留);分布: 400(313)/422(142)/404(56)/401(13)/403(18) |
|
||||
| P3W3 | 授权+EventBus | EventBus service接入(AccountUserService示范); middleware体系已完整(Auth/AccountScope/Policy/RoleCheck) |
|
||||
|
||||
## 3. 遗留差距 (Phase 4 待做)
|
||||
|
||||
### P0 紧急
|
||||
- 10个FAIL测试修复(参数签名变更+validation规则变更导致)
|
||||
- PolicyMiddleware路由注册 — 中间件已实现但未应用到admin-only端点(Chatwoot Pundit authorize)
|
||||
|
||||
### P1 重要
|
||||
- EventBus service层全面接入 — AccountUserService示范完成,其余service需逐步添加Publish调用
|
||||
- DI注入更新 — NewAccountUserService新增eventBus参数需在bootstrap.go更新
|
||||
|
||||
### P2 改进
|
||||
- JSON响应字段名微调(attribute_key alias等)
|
||||
- 新增测试覆盖(merge/filter/account_user/event_bus)
|
||||
- .bak/.BAK文件清理
|
||||
|
||||
## 4. 优化计划时间线
|
||||
|
||||
```
|
||||
Phase 4 Wave 1: 修复10个FAIL测试
|
||||
Phase 4 Wave 2: PolicyMiddleware路由注册(administrator-only端点)
|
||||
Phase 4 Wave 3: EventBus全面接入(逐service添加Publish) + DI更新
|
||||
Phase 4 Wave 4: 新增测试覆盖 + JSON响应微调 + 清理.bak文件
|
||||
```
|
||||
|
||||
## 5. 关键决策记录
|
||||
|
||||
- 路由参数名统一: /:id改为具体资源名
|
||||
- widget路由加/widget/前缀分离website_token路由
|
||||
- platform路由合并: /platform/api/v1前缀+middleware区分认证
|
||||
- Delete返回204 No Content
|
||||
- Labels过滤改为OR语义 ✅
|
||||
- ConversationFinder返回FilterResult含count指标 ✅
|
||||
- AvailableAgent用intersection选agent ✅
|
||||
- ContactMerge: base_contact属性优先+nested递归合并 ✅
|
||||
- CSAT List方法+CsatFilterParams(5种过滤器) ✅
|
||||
- draft_messages: DB-based是增强实现 ✅
|
||||
- delivery_status: 已有service+webhook ✅
|
||||
- custom_attribute_defs: 6条validation规则对齐 ✅
|
||||
- 错误码: 500→422批量替换(172处) ✅
|
||||
- EventBus: topic覆盖Chatwoot所有事件类型 ✅
|
||||
@@ -0,0 +1,175 @@
|
||||
# GoChat vs Chatwoot 差距报告 & 优化计划
|
||||
|
||||
> 生成时间: 2026-06-03
|
||||
> 基线: gochat @ /home/yanghao05/Workspace/gochat
|
||||
> 参考: chatwoot @ /home/yanghao05/Workspace/chatwoot-reference
|
||||
|
||||
---
|
||||
|
||||
## 一、整体概览
|
||||
|
||||
| 维度 | Chatwoot (Rails) | GoChat (Go) | 差距 |
|
||||
|------|-----------------|-------------|------|
|
||||
| 总路由数 | ~408 声明 | 704 实际注册 | GoChat超出(含alias) |
|
||||
| Account级路由 | ~340 | 571 | 已覆盖,含alias路径 |
|
||||
| 非Account级路由 | ~68 | 133 | 已覆盖 |
|
||||
| 测试通过率 | — | 19/22 pkg PASS | 86.4% (仅handler层1pkg FAIL) |
|
||||
| 编译状态 | — | ✅ PASS | — |
|
||||
|
||||
### 路由覆盖率
|
||||
|
||||
总体覆盖率 **~100%** (按Chatwoot资源维度),GoChat注册了额外的alias路径(如 `/labels` 同时保留 `/tags`, `/hooks` 同时保留 `/integrations/hooks`)。
|
||||
|
||||
---
|
||||
|
||||
## 二、Phase 1 路由补齐完成清单
|
||||
|
||||
本轮新增 **55条路由** (649→704),覆盖以下缺失域:
|
||||
|
||||
### 2.1 Captain域 (核心新增)
|
||||
| 资源 | 新增路由 | handler/service状态 |
|
||||
|------|---------|---------------------|
|
||||
| assistant_responses (flat CRUD) | GET/POST/GET/:id/PATCH/:id/DELETE/:id + search | ✅ handler新建, service新建 |
|
||||
| assistants (flat) | GET/POST/GET/:id/PUT/:id/DELETE/:id + tools/playground/inboxes | ✅ handler已有 |
|
||||
| documents (flat) | GET/POST/GET/:id/DELETE/:id + sync | ✅ handler已有 |
|
||||
| scenarios (flat) | GET/POST/GET/:id/PUT/:id/DELETE/:id | ✅ handler已有 |
|
||||
| custom_tools (flat) | GET/POST/GET/:id/PUT/:id/DELETE/:id + test | ✅ handler已有 |
|
||||
| copilot_threads + nested messages | CRUD + nested /:id/copilot_messages GET/POST | ✅ handler已有 |
|
||||
| preferences | GET/DELETE | ✅ handler已有 |
|
||||
|
||||
### 2.2 Account域别名路由
|
||||
| 资源 | 新增路径 | 说明 |
|
||||
|------|---------|------|
|
||||
| labels | /labels (alias of /tags) | Chatwoot用labels,GoChat原有tags |
|
||||
| hooks | /hooks | integration hooks flat路由 |
|
||||
| inbox_members | /inbox_members POST/DELETE | account级成员管理 |
|
||||
| assignment_policies | /assignment_policies (plural alias) | Chatwoot用plural |
|
||||
| notification_subscriptions | POST/DELETE (create/destroy) | account级通知订阅 |
|
||||
|
||||
### 2.3 Profile域
|
||||
| 资源 | 新增路由 | handler/service状态 |
|
||||
|------|---------|------|
|
||||
| DELETE /profile/avatar | ✅ 新建 | handler+service新建 |
|
||||
| profile/mfa (Chatwoot风格) | GET/POST/DELETE/verify/backup_codes | handler已有, backup_codes新建 |
|
||||
|
||||
### 2.4 Service层新增方法
|
||||
| Service | 新增方法 | 状态 |
|
||||
|---------|---------|------|
|
||||
| CaptainAssistantResponseService | List/Get/Update/Delete + responseRepo字段 | ✅ |
|
||||
| ProfileService | DeleteAvatar | ✅ |
|
||||
| MFAService | GenerateBackupCodes + cryptoRandomString | ✅ |
|
||||
| CaptainAssistantResponseRepo | DB() accessor | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 三、当前差距(Phase 2-4 待解决)
|
||||
|
||||
### 3.1 P0: Service逻辑深度对齐 (Phase 2)
|
||||
|
||||
| 域 | 差距描述 | 优先级 |
|
||||
|----|---------|--------|
|
||||
| CaptainAssistantResponse CRUD | service层List/Get/Update/Delete逻辑简化,缺少Chatwoot的filter/sort/pagination参数 | P0 |
|
||||
| ConversationFinder | 7种过滤器未完整实现(status+assignee+labels+sort等) | P0 |
|
||||
| assignable_agents | 缺少自动分配逻辑(round-robin等) | P0 |
|
||||
| contact_merge | 合并逻辑简化,缺少duplicate检测+字段优先级规则 | P0 |
|
||||
| csat_survey_responses | service层CRUD逻辑简化 | P0 |
|
||||
| draft_messages | service层搜索/计数逻辑简化 | P0 |
|
||||
| delivery_status | 仅handler层,service逻辑缺失 | P0 |
|
||||
| custom_attribute_defs | CRUD逻辑简化,缺少validation规则 | P0 |
|
||||
|
||||
### 3.2 P1: Service缺失 (需新建)
|
||||
|
||||
| Service | 说明 |
|
||||
|---------|------|
|
||||
| csat_survey_service.go | Chatwoot CsatSurveyService |
|
||||
| account_user_service.go | Chatwoot AccountUserService |
|
||||
|
||||
### 3.3 P2: 逻辑1:1对齐 (Phase 3)
|
||||
|
||||
| 维度 | 差距 |
|
||||
|------|------|
|
||||
| 错误响应码 | 部分handler返回500而非Chatwoot的404/422 |
|
||||
| JSON响应结构 | 字段名/嵌套/null处理差异 |
|
||||
| 授权scope检查 | middleware存在但部分handler未调用 |
|
||||
| 副作用(事件触发) | 缺少Event::Dispatcher.emit类的事件系统 |
|
||||
| PUT vs PATCH | Chatwoot统一用PATCH,GoChat部分用PUT |
|
||||
| filter/sort/pagination | Chatwoot有ConversationFinder 7种过滤器,GoChat简化 |
|
||||
|
||||
### 3.4 P3: 测试覆盖 (Phase 4)
|
||||
|
||||
| 维度 | 当前状态 | 目标 |
|
||||
|------|---------|------|
|
||||
| 测试通过率 | 19/22 pkg (86.4%) | 100% |
|
||||
| 失败package | handler/api/v1 (11个case) | 0 |
|
||||
| 新增路由测试 | assistant_responses CRUD无测试 | 需补充 |
|
||||
| handler覆盖率 | ~60% | >90% |
|
||||
| service覆盖率 | ~40% | >80% |
|
||||
|
||||
---
|
||||
|
||||
## 四、优化计划(4 Waves × 4 Phases)
|
||||
|
||||
### Wave 1: Phase 1 路由 ✅ DONE
|
||||
- 55条路由新增
|
||||
- Captain域flat CRUD全注册
|
||||
- Profile/MFA/Labels/Hooks alias补齐
|
||||
- bootstrap依赖注入修复
|
||||
- 测试构造函数参数修复
|
||||
|
||||
### Wave 2: Phase 2 Service深度对齐
|
||||
```
|
||||
S1: CaptainAssistantResponse CRUD逻辑 → 对齐Chatwoot filter/sort/pagination
|
||||
S2: ConversationFinder 7种过滤器 → 完整实现
|
||||
S3: assignable_agents 自动分配 → round-robin逻辑
|
||||
S4: contact_merge → duplicate检测+字段优先级
|
||||
S5: csat_survey → 新建service + CRUD逻辑
|
||||
S6: draft_messages → 搜索/计数service逻辑
|
||||
S7: delivery_status → 新建service
|
||||
S8: custom_attribute_defs → validation规则
|
||||
S9: account_user → 新建service
|
||||
S10: 事件系统 → Event bus基础架构
|
||||
```
|
||||
|
||||
### Wave 3: Phase 3 逻辑1:1对齐
|
||||
```
|
||||
S1: 错误码对齐 → 404/422/403精确匹配
|
||||
S2: JSON结构对齐 → 字段名/嵌套/null
|
||||
S3: 授权scope → middleware调用补齐
|
||||
S4: PUT→PATCH → 批量替换HTTP方法
|
||||
S5: filter/sort/pagination → 参数语义对齐
|
||||
S6: 副作用(事件) → hook触发逻辑
|
||||
S7: webhook验证 → 签名校验逻辑
|
||||
S8: inbox_limits → 限制检查逻辑
|
||||
S9: notification_preferences → 偏好更新逻辑
|
||||
S10: API版本兼容 → v1/v2路由共存
|
||||
```
|
||||
|
||||
### Wave 4: Phase 4 测试覆盖
|
||||
```
|
||||
S1: 修复11个FAIL测试 → AgentBot/AutomationRule
|
||||
S2: assistant_responses CRUD测试 → 新建
|
||||
S3: MFA backup_codes测试 → 新建
|
||||
S4: Profile DeleteAvatar测试 → 新建
|
||||
S5: inbox_members/labels alias测试 → 新建
|
||||
S6: hooks CRUD测试 → 新建
|
||||
S7: notification_subscriptions测试 → 新建
|
||||
S8: assignment_policies alias测试 → 新建
|
||||
S9: service覆盖率提升 → 从40%到80%
|
||||
S10: handler覆盖率提升 → 从60%到90%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 五、关键发现 & 决策
|
||||
|
||||
1. **路由层已基本完成**: 704条路由覆盖Chatwoot全部408个声明,含alias路径
|
||||
2. **Handler层90%已存在**: 大部分P0资源已有handler方法,仅assistant_responses需新建
|
||||
3. **Service层是主要差距**: CRUD逻辑简化是最大问题,需逐模块深度对齐
|
||||
4. **事件系统缺失**: Chatwoot的Event::Dispatcher在GoChat中无对应
|
||||
5. **测试构造函数已修复**: responseRepo参数同步到4个测试文件
|
||||
|
||||
---
|
||||
|
||||
## 六、下一步行动
|
||||
|
||||
**立即执行 Wave 2 (Phase 2 Service深度对齐)**,从 S1 CaptainAssistantResponse CRUD逻辑 开始。
|
||||
@@ -0,0 +1,505 @@
|
||||
# GoChat vs Chatwoot 路由差距报告
|
||||
|
||||
生成时间: 2026-06-03T15:09:00.174781
|
||||
|
||||
## 概览
|
||||
- Chatwoot路由总数: 473
|
||||
- GoChat路由总数: 0
|
||||
- 已覆盖: 0 (0.0%)
|
||||
- 缺失: 473
|
||||
- GoChat额外: 0
|
||||
|
||||
## 缺失路由清单
|
||||
|
||||
### Enterprise API (5条)
|
||||
- `GET /enterprise/api/v1/accounts/:id/limits` → enterprise/accounts#limits
|
||||
- `POST /enterprise/api/v1/accounts/:id/checkout` → enterprise/accounts#checkout
|
||||
- `POST /enterprise/api/v1/accounts/:id/subscription` → enterprise/accounts#subscription
|
||||
- `POST /enterprise/api/v1/accounts/:id/toggle_deletion` → enterprise/accounts#toggle_deletion
|
||||
- `POST /enterprise/api/v1/accounts/:id/topup_checkout` → enterprise/accounts#topup_checkout
|
||||
|
||||
### Platform API (21条)
|
||||
- `DELETE /platform/api/v1/accounts/:id` → platform/accounts#destroy
|
||||
- `DELETE /platform/api/v1/accounts/:account_id/account_users` → platform/account_users#destroy
|
||||
- `DELETE /platform/api/v1/agent_bots/:id` → platform/agent_bots#destroy
|
||||
- `DELETE /platform/api/v1/agent_bots/:id/avatar` → platform/agent_bots#delete_avatar
|
||||
- `DELETE /platform/api/v1/users/:id` → platform/users#destroy
|
||||
- `GET /platform/api/v1/accounts` → platform/accounts#index
|
||||
- `GET /platform/api/v1/accounts/:id` → platform/accounts#show
|
||||
- `GET /platform/api/v1/accounts/:account_id/account_users` → platform/account_users#index
|
||||
- `GET /platform/api/v1/agent_bots` → platform/agent_bots#index
|
||||
- `GET /platform/api/v1/agent_bots/:id` → platform/agent_bots#show
|
||||
- `GET /platform/api/v1/users/:id` → platform/users#show
|
||||
- `GET /platform/api/v1/users/:id/login` → platform/users#login
|
||||
- `POST /platform/api/v1/accounts` → platform/accounts#create
|
||||
- `POST /platform/api/v1/accounts/:account_id/account_users` → platform/account_users#create
|
||||
- `POST /platform/api/v1/accounts/:account_id/email_channel_migrations` → platform/email_channel_migrations#create
|
||||
- `POST /platform/api/v1/agent_bots` → platform/agent_bots#create
|
||||
- `POST /platform/api/v1/users` → platform/users#create
|
||||
- `POST /platform/api/v1/users/:id/token` → platform/users#token
|
||||
- `PUT /platform/api/v1/accounts/:id` → platform/accounts#update
|
||||
- `PUT /platform/api/v1/agent_bots/:id` → platform/agent_bots#update
|
||||
- `PUT /platform/api/v1/users/:id` → platform/users#update
|
||||
|
||||
### Public API (15条)
|
||||
- `GET /public/api/v1/contacts/:id` → public/contacts#show
|
||||
- `GET /public/api/v1/conversations` → public/conversations#index
|
||||
- `GET /public/api/v1/conversations/:id` → public/conversations#show
|
||||
- `GET /public/api/v1/conversations/:id/messages` → public/messages#index
|
||||
- `GET /public/api/v1/csat_survey` → public/csat_survey#show
|
||||
- `GET /public/api/v1/inboxes` → public/inboxes#index
|
||||
- `PATCH /public/api/v1/contacts/:id` → public/contacts#update
|
||||
- `PATCH /public/api/v1/conversations/:id/messages/:message_id` → public/messages#update
|
||||
- `PATCH /public/api/v1/csat_survey` → public/csat_survey#update
|
||||
- `POST /public/api/v1/contacts` → public/contacts#create
|
||||
- `POST /public/api/v1/conversations` → public/conversations#create
|
||||
- `POST /public/api/v1/conversations/:id/messages` → public/messages#create
|
||||
- `POST /public/api/v1/conversations/:id/toggle_status` → public/conversations#toggle_status
|
||||
- `POST /public/api/v1/conversations/:id/toggle_typing` → public/conversations#toggle_typing
|
||||
- `POST /public/api/v1/conversations/:id/update_last_seen` → public/conversations#update_last_seen
|
||||
|
||||
### V1 Account级-其他 (335条)
|
||||
- `DELETE /api/v1/accounts/:account_id/agent_bots/:id` → agent_bots#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/agent_bots/:id/avatar` → agent_bots#delete_avatar
|
||||
- `DELETE /api/v1/accounts/:account_id/agent_capacity_policies/:id` → agent_capacity_policies#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/agents/:id` → agents#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/assignment_policies/:id` → assignment_policies#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/assignment_policies/:assignment_policy_id/inboxes/:inbox_id` → assignment_policies/inboxes#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/automation_rules/:id` → automation_rules#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/campaigns/:id` → campaigns#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/canned_responses/:id` → canned_responses#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/captain/assistants/:id` → captain/assistants#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes/:inbox_id` → captain/assistants/inboxes#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:id` → captain/scenarios#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/captain/custom_tools/:id` → captain/custom_tools#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/captain/documents/:id` → captain/documents#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/companies/:id` → companies#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/companies/:id/avatar` → companies#delete_avatar
|
||||
- `DELETE /api/v1/accounts/:account_id/companies/:id/contacts/:contact_id` → companies/contacts#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/contacts/:id` → contacts#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/contacts/:id/avatar` → contacts#delete_avatar
|
||||
- `DELETE /api/v1/accounts/:account_id/contacts/:id/notes/:note_id` → contacts/notes#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/conversations/:id` → conversations#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages` → conversations/draft_messages#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id` → conversations/messages#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/conversations/:conversation_id/participants` → conversations/participants#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/custom_attribute_definitions/:id` → custom_attribute_definitions#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/custom_filters/:id` → custom_filters#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/custom_roles/:id` → custom_roles#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/dashboard_apps/:id` → dashboard_apps#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/inbox_limits/:id` → inbox_limits#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/inbox_members` → inbox_members#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/inboxes/:id` → inboxes#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy` → inboxes/assignment_policy#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/inboxes/:id/avatar` → inboxes#delete_avatar
|
||||
- `DELETE /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id` → instagram_channel#delete_comment
|
||||
- `DELETE /api/v1/accounts/:account_id/integrations/hooks/:id` → integrations/hooks#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/integrations/linear` → integrations/linear#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/integrations/notion` → integrations/notion#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/integrations/shopify` → integrations/shopify#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/integrations/slack` → integrations/slack#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/labels/:id` → labels#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/macros/:id` → macros#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/notifications/:id` → notifications#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/portals/:id` → portals#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/portals/:portal_id/articles/:id` → articles#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/portals/:portal_id/categories/:id` → categories#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/portals/:id/logo` → portals#delete_logo
|
||||
- `DELETE /api/v1/accounts/:account_id/portals/articles/bulk_actions/delete_articles` → articles/bulk_actions#delete_articles
|
||||
- `DELETE /api/v1/accounts/:account_id/sla_policies/:id` → sla_policies#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/teams/:id` → teams#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/teams/:team_id/team_members` → team_members#destroy
|
||||
- `DELETE /api/v1/accounts/:account_id/webhooks/:id` → webhooks#destroy
|
||||
- `GET /api/v1/accounts/:account_id` → accounts#show
|
||||
- `GET /api/v1/accounts/:account_id/agent_bots` → agent_bots#index
|
||||
- `GET /api/v1/accounts/:account_id/agent_bots/:id` → agent_bots#show
|
||||
- `GET /api/v1/accounts/:account_id/agent_capacity_policies` → agent_capacity_policies#index
|
||||
- `GET /api/v1/accounts/:account_id/agent_capacity_policies/:id` → agent_capacity_policies#show
|
||||
- `GET /api/v1/accounts/:account_id/agents` → agents#index
|
||||
- `GET /api/v1/accounts/:account_id/applied_slas` → applied_slas#index
|
||||
- `GET /api/v1/accounts/:account_id/applied_slas/download` → applied_slas#download
|
||||
- `GET /api/v1/accounts/:account_id/applied_slas/metrics` → applied_slas#metrics
|
||||
- `GET /api/v1/accounts/:account_id/assignable_agents` → assignable_agents#index
|
||||
- `GET /api/v1/accounts/:account_id/assignment_policies` → assignment_policies#index
|
||||
- `GET /api/v1/accounts/:account_id/assignment_policies/:id` → assignment_policies#show
|
||||
- `GET /api/v1/accounts/:account_id/assignment_policies/:assignment_policy_id/inboxes` → assignment_policies/inboxes#index
|
||||
- `GET /api/v1/accounts/:account_id/automation_rules` → automation_rules#index
|
||||
- `GET /api/v1/accounts/:account_id/automation_rules/:id` → automation_rules#show
|
||||
- `GET /api/v1/accounts/:account_id/cache_keys` → accounts#cache_keys
|
||||
- `GET /api/v1/accounts/:account_id/campaigns` → campaigns#index
|
||||
- `GET /api/v1/accounts/:account_id/campaigns/:id` → campaigns#show
|
||||
- `GET /api/v1/accounts/:account_id/canned_responses` → canned_responses#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistant_responses` → captain/assistant_responses#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants` → captain/assistants#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants/:id` → captain/assistants#show
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes` → captain/assistants/inboxes#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios` → captain/scenarios#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:id` → captain/scenarios#show
|
||||
- `GET /api/v1/accounts/:account_id/captain/assistants/tools` → captain/assistants#tools
|
||||
- `GET /api/v1/accounts/:account_id/captain/copilot_threads` → captain/copilot_threads#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/copilot_threads/:id/copilot_messages` → captain/copilot_messages#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/custom_tools` → captain/custom_tools#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/custom_tools/:id` → captain/custom_tools#show
|
||||
- `GET /api/v1/accounts/:account_id/captain/documents` → captain/documents#index
|
||||
- `GET /api/v1/accounts/:account_id/captain/documents/:id` → captain/documents#show
|
||||
- `GET /api/v1/accounts/:account_id/captain/preferences` → captain/preferences#show
|
||||
- `GET /api/v1/accounts/:account_id/companies` → companies#index
|
||||
- `GET /api/v1/accounts/:account_id/companies/:id` → companies#show
|
||||
- `GET /api/v1/accounts/:account_id/companies/:id/contacts` → companies/contacts#index
|
||||
- `GET /api/v1/accounts/:account_id/companies/:id/contacts/search` → companies/contacts#search
|
||||
- `GET /api/v1/accounts/:account_id/companies/:id/conversations` → companies/conversations#index
|
||||
- `GET /api/v1/accounts/:account_id/companies/:id/notes` → companies/notes#index
|
||||
- `GET /api/v1/accounts/:account_id/companies/search` → companies#search
|
||||
- `GET /api/v1/accounts/:account_id/contact_inboxes/filter` → contact_inboxes#filter
|
||||
- `GET /api/v1/accounts/:account_id/contacts` → contacts#index
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id` → contacts#show
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/attachments` → contacts/attachments#index
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/contactable_inboxes` → contacts#contactable_inboxes
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/conversations` → contacts/conversations#index
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/labels` → contacts/labels#index
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/notes` → contacts/notes#index
|
||||
- `GET /api/v1/accounts/:account_id/contacts/:id/notes/:note_id` → contacts/notes#show
|
||||
- `GET /api/v1/accounts/:account_id/contacts/active` → contacts#active
|
||||
- `GET /api/v1/accounts/:account_id/contacts/search` → contacts#search
|
||||
- `GET /api/v1/accounts/:account_id/conversations` → conversations#index
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:id` → conversations#show
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:id/attachments` → conversations#attachments
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages` → conversations/draft_messages#show
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:conversation_id/labels` → conversations/labels#index
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:conversation_id/messages` → conversations/messages#index
|
||||
- `GET /api/v1/accounts/:account_id/conversations/:conversation_id/participants` → conversations/participants#show
|
||||
- `GET /api/v1/accounts/:account_id/conversations/meta` → conversations#meta
|
||||
- `GET /api/v1/accounts/:account_id/conversations/search` → conversations#search
|
||||
- `GET /api/v1/accounts/:account_id/conversations/unread_counts` → conversations/unread_counts#index
|
||||
- `GET /api/v1/accounts/:account_id/csat_survey_responses` → csat_survey_responses#index
|
||||
- `GET /api/v1/accounts/:account_id/csat_survey_responses/download` → csat_survey_responses#download
|
||||
- `GET /api/v1/accounts/:account_id/csat_survey_responses/metrics` → csat_survey_responses#metrics
|
||||
- `GET /api/v1/accounts/:account_id/custom_attribute_definitions` → custom_attribute_definitions#index
|
||||
- `GET /api/v1/accounts/:account_id/custom_attribute_definitions/:id` → custom_attribute_definitions#show
|
||||
- `GET /api/v1/accounts/:account_id/custom_filters` → custom_filters#index
|
||||
- `GET /api/v1/accounts/:account_id/custom_filters/:id` → custom_filters#show
|
||||
- `GET /api/v1/accounts/:account_id/custom_roles` → custom_roles#index
|
||||
- `GET /api/v1/accounts/:account_id/custom_roles/:id` → custom_roles#show
|
||||
- `GET /api/v1/accounts/:account_id/dashboard_apps` → dashboard_apps#index
|
||||
- `GET /api/v1/accounts/:account_id/dashboard_apps/:id` → dashboard_apps#show
|
||||
- `GET /api/v1/accounts/:account_id/google/callback` → google_channels#oauth_callback
|
||||
- `GET /api/v1/accounts/:account_id/google/oauth` → google_channels#authorization
|
||||
- `GET /api/v1/accounts/:account_id/google/webhooks` → google_channels#list_webhooks
|
||||
- `GET /api/v1/accounts/:account_id/inbox_limits` → inbox_limits#index
|
||||
- `GET /api/v1/accounts/:account_id/inbox_limits/:id` → inbox_limits#show
|
||||
- `GET /api/v1/accounts/:account_id/inbox_members/:inbox_id` → inbox_members#show
|
||||
- `GET /api/v1/accounts/:account_id/inboxes` → inboxes#index
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id` → inboxes#show
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id/agent_bot` → inboxes#agent_bot
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id/assignable_agents` → inboxes#assignable_agents
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy` → inboxes/assignment_policy#show
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id/campaigns` → inboxes#campaigns
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id/csat_template` → inbox_csat_templates#show
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:id/health` → inboxes#health
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/replies` → instagram_channel#get_comment_replies
|
||||
- `GET /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/media/:media_id` → instagram_channel#get_comments
|
||||
- `GET /api/v1/accounts/:account_id/integrations/apps` → integrations/apps#index
|
||||
- `GET /api/v1/accounts/:account_id/integrations/apps/:id` → integrations/apps#show
|
||||
- `GET /api/v1/accounts/:account_id/integrations/hooks/:id` → integrations/hooks#show
|
||||
- `GET /api/v1/accounts/:account_id/integrations/linear/linked_issues` → integrations/linear#linked_issues
|
||||
- `GET /api/v1/accounts/:account_id/integrations/linear/search_issue` → integrations/linear#search_issue
|
||||
- `GET /api/v1/accounts/:account_id/integrations/linear/team_entities` → integrations/linear#team_entities
|
||||
- `GET /api/v1/accounts/:account_id/integrations/linear/teams` → integrations/linear#teams
|
||||
- `GET /api/v1/accounts/:account_id/integrations/shopify/orders` → integrations/shopify#orders
|
||||
- `GET /api/v1/accounts/:account_id/integrations/slack/list_all_channels` → integrations/slack#list_all_channels
|
||||
- `GET /api/v1/accounts/:account_id/labels` → labels#index
|
||||
- `GET /api/v1/accounts/:account_id/labels/:id` → labels#show
|
||||
- `GET /api/v1/accounts/:account_id/macros` → macros#index
|
||||
- `GET /api/v1/accounts/:account_id/macros/:id` → macros#show
|
||||
- `GET /api/v1/accounts/:account_id/notification_settings` → notification_settings#show
|
||||
- `GET /api/v1/accounts/:account_id/notifications` → notifications#index
|
||||
- `GET /api/v1/accounts/:account_id/notifications/unread_count` → notifications#unread_count
|
||||
- `GET /api/v1/accounts/:account_id/portals` → portals#index
|
||||
- `GET /api/v1/accounts/:account_id/portals/:id` → portals#show
|
||||
- `GET /api/v1/accounts/:account_id/portals/:portal_id/articles` → articles#index
|
||||
- `GET /api/v1/accounts/:account_id/portals/:portal_id/articles/:id` → articles#show
|
||||
- `GET /api/v1/accounts/:account_id/portals/:portal_id/categories` → categories#index
|
||||
- `GET /api/v1/accounts/:account_id/portals/:portal_id/categories/:id` → categories#show
|
||||
- `GET /api/v1/accounts/:account_id/portals/:id/ssl_status` → portals#ssl_status
|
||||
- `GET /api/v1/accounts/:account_id/reporting_events` → reporting_events#index
|
||||
- `GET /api/v1/accounts/:account_id/search` → search#index
|
||||
- `GET /api/v1/accounts/:account_id/search/contacts` → search#contacts
|
||||
- `GET /api/v1/accounts/:account_id/search/conversations` → search#conversations
|
||||
- `GET /api/v1/accounts/:account_id/search/messages` → search#messages
|
||||
- `GET /api/v1/accounts/:account_id/sla_policies` → sla_policies#index
|
||||
- `GET /api/v1/accounts/:account_id/sla_policies/:id` → sla_policies#show
|
||||
- `GET /api/v1/accounts/:account_id/teams` → teams#index
|
||||
- `GET /api/v1/accounts/:account_id/teams/:id` → teams#show
|
||||
- `GET /api/v1/accounts/:account_id/teams/:team_id/team_members` → team_members#index
|
||||
- `GET /api/v1/accounts/:account_id/webhooks` → webhooks#index
|
||||
- `GET /api/v1/accounts/:account_id/whatsapp_calls/:id` → whatsapp_calls#show
|
||||
- `PATCH /api/v1/accounts/:account_id/contacts/:id/notes/:note_id` → contacts/notes#update
|
||||
- `PATCH /api/v1/accounts/:account_id/conversations/:id` → conversations#update
|
||||
- `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/draft_messages` → conversations/draft_messages#update
|
||||
- `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id` → conversations/messages#update
|
||||
- `PATCH /api/v1/accounts/:account_id/conversations/:conversation_id/participants` → conversations/participants#update
|
||||
- `PATCH /api/v1/accounts/:account_id/inbox_members` → inbox_members#update
|
||||
- `PATCH /api/v1/accounts/:account_id/portals/:id/archive` → portals#archive
|
||||
- `PATCH /api/v1/accounts/:account_id/portals/articles/bulk_actions/update_status` → articles/bulk_actions#update_status
|
||||
- `PATCH /api/v1/accounts/:account_id/teams/:team_id/team_members` → team_members#update
|
||||
- `POST /api/v1/accounts` → accounts#create
|
||||
- `POST /api/v1/accounts/:account_id/actions/contact_merge` → contact_merges#create
|
||||
- `POST /api/v1/accounts/:account_id/agent_bots` → agent_bots#create
|
||||
- `POST /api/v1/accounts/:account_id/agent_bots/:id/reset_access_token` → agent_bots#reset_access_token
|
||||
- `POST /api/v1/accounts/:account_id/agent_bots/:id/reset_secret` → agent_bots#reset_secret
|
||||
- `POST /api/v1/accounts/:account_id/agent_capacity_policies` → agent_capacity_policies#create
|
||||
- `POST /api/v1/accounts/:account_id/agents` → agents#create
|
||||
- `POST /api/v1/accounts/:account_id/agents/bulk_create` → agents#bulk_create
|
||||
- `POST /api/v1/accounts/:account_id/assignment_policies` → assignment_policies#create
|
||||
- `POST /api/v1/accounts/:account_id/assignment_policies/:assignment_policy_id/inboxes` → assignment_policies/inboxes#create
|
||||
- `POST /api/v1/accounts/:account_id/automation_rules` → automation_rules#create
|
||||
- `POST /api/v1/accounts/:account_id/automation_rules/:id/clone` → automation_rules#clone
|
||||
- `POST /api/v1/accounts/:account_id/bulk_actions` → bulk_actions#create
|
||||
- `POST /api/v1/accounts/:account_id/callbacks/register_facebook_page` → callbacks#register_facebook_page
|
||||
- `POST /api/v1/accounts/:account_id/callbacks/verify_facebook_page` → callbacks#verify_facebook_page
|
||||
- `POST /api/v1/accounts/:account_id/campaigns` → campaigns#create
|
||||
- `POST /api/v1/accounts/:account_id/canned_responses` → canned_responses#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/assistants` → captain/assistants#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/inboxes` → captain/assistants/inboxes#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/assistants/:id/playground` → captain/assistants#playground
|
||||
- `POST /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios` → captain/scenarios#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/bulk_actions` → captain/bulk_actions#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/copilot_threads` → captain/copilot_threads#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/copilot_threads/:id/copilot_messages` → captain/copilot_messages#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/custom_tools` → captain/custom_tools#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/custom_tools/test` → captain/custom_tools#test
|
||||
- `POST /api/v1/accounts/:account_id/captain/documents` → captain/documents#create
|
||||
- `POST /api/v1/accounts/:account_id/captain/documents/:id/sync` → captain/documents#sync
|
||||
- `POST /api/v1/accounts/:account_id/captain/tasks/rewrite` → captain/tasks#rewrite
|
||||
- `POST /api/v1/accounts/:account_id/channels/twilio_channel` → twilio_channels#create
|
||||
- `POST /api/v1/accounts/:account_id/companies` → companies#create
|
||||
- `POST /api/v1/accounts/:account_id/companies/:id/contacts` → companies/contacts#create
|
||||
- `POST /api/v1/accounts/:account_id/companies/:id/destroy_custom_attributes` → companies#destroy_custom_attributes
|
||||
- `POST /api/v1/accounts/:account_id/contacts` → contacts#create
|
||||
- `POST /api/v1/accounts/:account_id/contacts/:id/contact_inboxes` → contacts/contact_inboxes#create
|
||||
- `POST /api/v1/accounts/:account_id/contacts/:id/destroy_custom_attributes` → contacts#destroy_custom_attributes
|
||||
- `POST /api/v1/accounts/:account_id/contacts/:id/labels` → contacts/labels#create
|
||||
- `POST /api/v1/accounts/:account_id/contacts/:id/notes` → contacts/notes#create
|
||||
- `POST /api/v1/accounts/:account_id/contacts/export` → contacts#export
|
||||
- `POST /api/v1/accounts/:account_id/contacts/filter` → contacts#filter
|
||||
- `POST /api/v1/accounts/:account_id/contacts/import` → contacts#import
|
||||
- `POST /api/v1/accounts/:account_id/conversations` → conversations#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/assignments` → conversations/assignments#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/custom_attributes` → conversations#custom_attributes
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/direct_uploads` → conversations/direct_uploads#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/labels` → conversations/labels#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages` → conversations/messages#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id/retry` → conversations/messages#retry
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/messages/:id/translate` → conversations/messages#translate
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/mute` → conversations#mute
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:conversation_id/participants` → conversations/participants#create
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/toggle_priority` → conversations#toggle_priority
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/toggle_status` → conversations#toggle_status
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/toggle_typing_status` → conversations#toggle_typing_status
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/transcript` → conversations#transcript
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/unmute` → conversations#unmute
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/unread` → conversations#unread
|
||||
- `POST /api/v1/accounts/:account_id/conversations/:id/update_last_seen` → conversations#update_last_seen
|
||||
- `POST /api/v1/accounts/:account_id/conversations/filter` → conversations#filter
|
||||
- `POST /api/v1/accounts/:account_id/custom_attribute_definitions` → custom_attribute_definitions#create
|
||||
- `POST /api/v1/accounts/:account_id/custom_filters` → custom_filters#create
|
||||
- `POST /api/v1/accounts/:account_id/custom_roles` → custom_roles#create
|
||||
- `POST /api/v1/accounts/:account_id/dashboard_apps` → dashboard_apps#create
|
||||
- `POST /api/v1/accounts/:account_id/direct_uploads` → upload#direct_uploads
|
||||
- `POST /api/v1/accounts/:account_id/google/authorization` → google/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/google/webhooks` → google_channels#register_webhook
|
||||
- `POST /api/v1/accounts/:account_id/inbox_limits` → inbox_limits#create
|
||||
- `POST /api/v1/accounts/:account_id/inbox_members` → inbox_members#create
|
||||
- `POST /api/v1/accounts/:account_id/inboxes` → inboxes#create
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:inbox_id/assignment_policy` → inboxes/assignment_policy#create
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/csat_template` → inbox_csat_templates#create
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/csat_template/analyze` → inbox_csat_templates#analyze
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/hide` → instagram_channel#hide_comment
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:inbox_id/instagram_comments/:comment_id/reply` → instagram_channel#reply_to_comment
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/register_webhook` → inboxes#register_webhook
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/reset_secret` → inboxes#reset_secret
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/set_agent_bot` → inboxes#set_agent_bot
|
||||
- `POST /api/v1/accounts/:account_id/inboxes/:id/sync_templates` → inboxes#sync_templates
|
||||
- `POST /api/v1/accounts/:account_id/instagram/authorization` → instagram/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/integrations/dyte/add_participant_to_meeting` → integrations/dyte#add_participant_to_meeting
|
||||
- `POST /api/v1/accounts/:account_id/integrations/dyte/create_a_meeting` → integrations/dyte#create_a_meeting
|
||||
- `POST /api/v1/accounts/:account_id/integrations/hooks` → integrations/hooks#create
|
||||
- `POST /api/v1/accounts/:account_id/integrations/hooks/:id/process_event` → integrations/hooks#process_event
|
||||
- `POST /api/v1/accounts/:account_id/integrations/linear/create_issue` → integrations/linear#create_issue
|
||||
- `POST /api/v1/accounts/:account_id/integrations/linear/link_issue` → integrations/linear#link_issue
|
||||
- `POST /api/v1/accounts/:account_id/integrations/linear/unlink_issue` → integrations/linear#unlink_issue
|
||||
- `POST /api/v1/accounts/:account_id/integrations/shopify/auth` → integrations/shopify#auth
|
||||
- `POST /api/v1/accounts/:account_id/integrations/slack` → integrations/slack#create
|
||||
- `POST /api/v1/accounts/:account_id/labels` → labels#create
|
||||
- `POST /api/v1/accounts/:account_id/macros` → macros#create
|
||||
- `POST /api/v1/accounts/:account_id/macros/:id/execute` → macros#execute
|
||||
- `POST /api/v1/accounts/:account_id/microsoft/authorization` → microsoft/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/notifications/:id/snooze` → notifications#snooze
|
||||
- `POST /api/v1/accounts/:account_id/notifications/:id/unread` → notifications#unread
|
||||
- `POST /api/v1/accounts/:account_id/notifications/destroy_all` → notifications#destroy_all
|
||||
- `POST /api/v1/accounts/:account_id/notifications/read_all` → notifications#read_all
|
||||
- `POST /api/v1/accounts/:account_id/notion/authorization` → notion/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/portals` → portals#create
|
||||
- `POST /api/v1/accounts/:account_id/portals/:portal_id/articles` → articles#create
|
||||
- `POST /api/v1/accounts/:account_id/portals/:portal_id/articles/reorder` → articles#reorder
|
||||
- `POST /api/v1/accounts/:account_id/portals/:portal_id/categories` → categories#create
|
||||
- `POST /api/v1/accounts/:account_id/portals/:portal_id/categories/reorder` → categories#reorder
|
||||
- `POST /api/v1/accounts/:account_id/portals/:id/send_instructions` → portals#send_instructions
|
||||
- `POST /api/v1/accounts/:account_id/portals/articles/bulk_actions/translate` → articles/bulk_actions#translate
|
||||
- `POST /api/v1/accounts/:account_id/sla_policies` → sla_policies#create
|
||||
- `POST /api/v1/accounts/:account_id/teams` → teams#create
|
||||
- `POST /api/v1/accounts/:account_id/teams/:team_id/team_members` → team_members#create
|
||||
- `POST /api/v1/accounts/:account_id/tiktok/authorization` → tiktok/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/twitter/authorization` → twitter/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/update_active_at` → accounts#update_active_at
|
||||
- `POST /api/v1/accounts/:account_id/upload` → upload#create
|
||||
- `POST /api/v1/accounts/:account_id/webhooks` → webhooks#create
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp/authorization` → whatsapp/authorization#create
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp_calls/:id/accept` → whatsapp_calls#accept
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp_calls/:id/reject` → whatsapp_calls#reject
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp_calls/:id/terminate` → whatsapp_calls#terminate
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp_calls/:id/upload_recording` → whatsapp_calls#upload_recording
|
||||
- `POST /api/v1/accounts/:account_id/whatsapp_calls/initiate` → whatsapp_calls#initiate
|
||||
- `POST /resend_confirmation` → resend_confirmations#create
|
||||
- `PUT /api/v1/accounts/:account_id` → accounts#update
|
||||
- `PUT /api/v1/accounts/:account_id/agent_bots/:id` → agent_bots#update
|
||||
- `PUT /api/v1/accounts/:account_id/agent_capacity_policies/:id` → agent_capacity_policies#update
|
||||
- `PUT /api/v1/accounts/:account_id/agents/:id` → agents#update
|
||||
- `PUT /api/v1/accounts/:account_id/assignment_policies/:id` → assignment_policies#update
|
||||
- `PUT /api/v1/accounts/:account_id/automation_rules/:id` → automation_rules#update
|
||||
- `PUT /api/v1/accounts/:account_id/campaigns/:id` → campaigns#update
|
||||
- `PUT /api/v1/accounts/:account_id/canned_responses/:id` → canned_responses#update
|
||||
- `PUT /api/v1/accounts/:account_id/captain/assistants/:id` → captain/assistants#update
|
||||
- `PUT /api/v1/accounts/:account_id/captain/assistants/:assistant_id/scenarios/:id` → captain/scenarios#update
|
||||
- `PUT /api/v1/accounts/:account_id/captain/custom_tools/:id` → captain/custom_tools#update
|
||||
- `PUT /api/v1/accounts/:account_id/captain/preferences` → captain/preferences#update
|
||||
- `PUT /api/v1/accounts/:account_id/companies/:id` → companies#update
|
||||
- `PUT /api/v1/accounts/:account_id/contacts/:id` → contacts#update
|
||||
- `PUT /api/v1/accounts/:account_id/custom_attribute_definitions/:id` → custom_attribute_definitions#update
|
||||
- `PUT /api/v1/accounts/:account_id/custom_filters/:id` → custom_filters#update
|
||||
- `PUT /api/v1/accounts/:account_id/custom_roles/:id` → custom_roles#update
|
||||
- `PUT /api/v1/accounts/:account_id/dashboard_apps/:id` → dashboard_apps#update
|
||||
- `PUT /api/v1/accounts/:account_id/inbox_limits/:id` → inbox_limits#update
|
||||
- `PUT /api/v1/accounts/:account_id/inboxes/:id` → inboxes#update
|
||||
- `PUT /api/v1/accounts/:account_id/integrations/hooks/:id` → integrations/hooks#update
|
||||
- `PUT /api/v1/accounts/:account_id/integrations/slack` → integrations/slack#update
|
||||
- `PUT /api/v1/accounts/:account_id/labels/:id` → labels#update
|
||||
- `PUT /api/v1/accounts/:account_id/macros/:id` → macros#update
|
||||
- `PUT /api/v1/accounts/:account_id/notification_settings` → notification_settings#update
|
||||
- `PUT /api/v1/accounts/:account_id/notifications/:id` → notifications#update
|
||||
- `PUT /api/v1/accounts/:account_id/portals/:id` → portals#update
|
||||
- `PUT /api/v1/accounts/:account_id/portals/:portal_id/articles/:id` → articles#update
|
||||
- `PUT /api/v1/accounts/:account_id/portals/:portal_id/categories/:id` → categories#update
|
||||
- `PUT /api/v1/accounts/:account_id/settings` → accounts#update_settings
|
||||
- `PUT /api/v1/accounts/:account_id/sla_policies/:id` → sla_policies#update
|
||||
- `PUT /api/v1/accounts/:account_id/teams/:id` → teams#update
|
||||
- `PUT /api/v1/accounts/:account_id/webhooks/:id` → webhooks#update
|
||||
|
||||
### V1非Account级 (24条)
|
||||
- `DELETE /api/v1/notification_subscriptions` → notification_subscriptions#destroy
|
||||
- `DELETE /api/v1/profile/avatar` → profile#delete_avatar
|
||||
- `DELETE /api/v1/profile/mfa` → mfa#destroy
|
||||
- `DELETE /auth` → auth#destroy (logout)
|
||||
- `DELETE /auth/sign_out` → sessions#destroy
|
||||
- `GET /api/v1/profile` → profile#show
|
||||
- `GET /api/v1/profile/mfa` → mfa#show
|
||||
- `POST /api/v1/auth/saml_login` → auth#saml_login
|
||||
- `POST /api/v1/integrations/webhooks` → integrations/webhooks#create
|
||||
- `POST /api/v1/notification_subscriptions` → notification_subscriptions#create
|
||||
- `POST /api/v1/profile/auto_offline` → profile#auto_offline
|
||||
- `POST /api/v1/profile/availability` → profile#availability
|
||||
- `POST /api/v1/profile/mfa` → mfa#create
|
||||
- `POST /api/v1/profile/mfa/backup_codes` → mfa#backup_codes
|
||||
- `POST /api/v1/profile/mfa/verify` → mfa#verify
|
||||
- `POST /api/v1/profile/resend_confirmation` → profile#resend_confirmation
|
||||
- `POST /api/v1/profile/reset_access_token` → profile#reset_access_token
|
||||
- `POST /auth` → auth#create (login)
|
||||
- `POST /auth/password` → passwords#create
|
||||
- `POST /auth/password/reset` → passwords#reset
|
||||
- `POST /auth/sign_in` → sessions#create
|
||||
- `PUT /api/v1/profile` → profile#update
|
||||
- `PUT /api/v1/profile/set_active_account` → profile#set_active_account
|
||||
- `PUT /auth/password` → passwords#update
|
||||
|
||||
### V2报表 (22条)
|
||||
- `GET /api/v2/accounts/:account_id/live_reports/conversation_metrics` → live_reports#conversation_metrics
|
||||
- `GET /api/v2/accounts/:account_id/live_reports/grouped_conversation_metrics` → live_reports#grouped_conversation_metrics
|
||||
- `GET /api/v2/accounts/:account_id/reports` → reports#index
|
||||
- `GET /api/v2/accounts/:account_id/reports/agents` → reports#agents
|
||||
- `GET /api/v2/accounts/:account_id/reports/bot_metrics` → reports#bot_metrics
|
||||
- `GET /api/v2/accounts/:account_id/reports/bot_summary` → reports#bot_summary
|
||||
- `GET /api/v2/accounts/:account_id/reports/conversation_traffic` → reports#conversation_traffic
|
||||
- `GET /api/v2/accounts/:account_id/reports/conversations` → reports#conversations
|
||||
- `GET /api/v2/accounts/:account_id/reports/conversations_summary` → reports#conversations_summary
|
||||
- `GET /api/v2/accounts/:account_id/reports/first_response_time_distribution` → reports#first_response_time_distribution
|
||||
- `GET /api/v2/accounts/:account_id/reports/inbox_label_matrix` → reports#inbox_label_matrix
|
||||
- `GET /api/v2/accounts/:account_id/reports/inboxes` → reports#inboxes
|
||||
- `GET /api/v2/accounts/:account_id/reports/labels` → reports#labels
|
||||
- `GET /api/v2/accounts/:account_id/reports/outgoing_messages_count` → reports#outgoing_messages_count
|
||||
- `GET /api/v2/accounts/:account_id/reports/summary` → reports#summary
|
||||
- `GET /api/v2/accounts/:account_id/reports/teams` → reports#teams
|
||||
- `GET /api/v2/accounts/:account_id/summary_reports/agent` → summary_reports#agent
|
||||
- `GET /api/v2/accounts/:account_id/summary_reports/channel` → summary_reports#channel
|
||||
- `GET /api/v2/accounts/:account_id/summary_reports/inbox` → summary_reports#inbox
|
||||
- `GET /api/v2/accounts/:account_id/summary_reports/label` → summary_reports#label
|
||||
- `GET /api/v2/accounts/:account_id/summary_reports/team` → summary_reports#team
|
||||
- `GET /api/v2/accounts/:account_id/year_in_review` → year_in_review#show
|
||||
|
||||
### WebSocket/Health (11条)
|
||||
- `GET /cable` → websocket#serve_cable
|
||||
- `GET /hc/:slug` → public/portals#show
|
||||
- `GET /hc/:slug/:locale` → public/portals#show
|
||||
- `GET /hc/:slug/:locale/articles` → public/articles#index
|
||||
- `GET /hc/:slug/:locale/categories` → public/categories#index
|
||||
- `GET /hc/:slug/:locale/categories/:category_slug` → public/categories#show
|
||||
- `GET /hc/:slug/:locale/categories/:category_slug/articles` → public/articles#index
|
||||
- `GET /hc/:slug/articles/:article_slug` → public/articles#show
|
||||
- `GET /hc/:slug/sitemap.xml` → public/portals#sitemap
|
||||
- `GET /health` → health#check
|
||||
- `GET /ws` → websocket#serve
|
||||
|
||||
### Webhooks (18条)
|
||||
- `GET /webhooks/email/:inbox_id/verification` → email_webhook#verification
|
||||
- `GET /webhooks/facebook/:page_id` → facebook_webhook#verify
|
||||
- `GET /webhooks/tiktok/:business_id` → tiktok_webhook#verify
|
||||
- `GET /webhooks/twitter/webhook` → twitter_webhook#crc
|
||||
- `GET /webhooks/whatsapp/:phone_number_id` → whatsapp_webhook#verify
|
||||
- `POST /webhooks/email/:inbox_id` → email_webhook#event
|
||||
- `POST /webhooks/facebook/:page_id` → facebook_webhook#event
|
||||
- `POST /webhooks/firecrawl` → enterprise/webhooks/firecrawl#process_payload
|
||||
- `POST /webhooks/line/:channel_id` → line_webhook#event
|
||||
- `POST /webhooks/microsoft/events` → microsoft_webhook#event
|
||||
- `POST /webhooks/microsoft/validation` → microsoft_webhook#validation
|
||||
- `POST /webhooks/stripe` → enterprise/webhooks/stripe#process_payload
|
||||
- `POST /webhooks/telegram/:bot_token` → telegram_webhook#event
|
||||
- `POST /webhooks/tiktok/:business_id` → tiktok_webhook#event
|
||||
- `POST /webhooks/twilio/sms/:phone_number` → twilio_webhook#inbound_sms
|
||||
- `POST /webhooks/twilio/status/:phone_number` → twilio_webhook#delivery_status
|
||||
- `POST /webhooks/twitter/webhook` → twitter_webhook#event
|
||||
- `POST /webhooks/whatsapp/:phone_number_id` → whatsapp_webhook#event
|
||||
|
||||
### Widget (22条)
|
||||
- `DELETE /widget/labels` → widget/labels#destroy
|
||||
- `GET /widget/campaigns` → widget/campaigns#index
|
||||
- `GET /widget/contact` → widget/contact#show
|
||||
- `GET /widget/conversations` → widget/conversations#index
|
||||
- `GET /widget/conversations/toggle_status` → widget/conversations#toggle_status
|
||||
- `GET /widget/inbox_members` → widget/inbox_members#index
|
||||
- `GET /widget/messages` → widget/messages#index
|
||||
- `PATCH /widget/contact` → widget/contact#update
|
||||
- `PATCH /widget/contact/set_user` → widget/contact#set_user
|
||||
- `PATCH /widget/messages` → widget/messages#update
|
||||
- `POST /widget/config` → widget/config#create
|
||||
- `POST /widget/contact/destroy_custom_attributes` → widget/contact#destroy_custom_attributes
|
||||
- `POST /widget/conversations` → widget/conversations#create
|
||||
- `POST /widget/conversations/destroy_custom_attributes` → widget/conversations#destroy_custom_attributes
|
||||
- `POST /widget/conversations/set_custom_attributes` → widget/conversations#set_custom_attributes
|
||||
- `POST /widget/conversations/toggle_typing` → widget/conversations#toggle_typing
|
||||
- `POST /widget/conversations/transcript` → widget/conversations#transcript
|
||||
- `POST /widget/conversations/update_last_seen` → widget/conversations#update_last_seen
|
||||
- `POST /widget/direct_uploads` → widget/direct_uploads#create
|
||||
- `POST /widget/events` → widget/events#create
|
||||
- `POST /widget/labels` → widget/labels#create
|
||||
- `POST /widget/messages` → widget/messages#create
|
||||
|
||||
## GoChat额外路由
|
||||
@@ -0,0 +1,246 @@
|
||||
# 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确认是否全局切换
|
||||
@@ -0,0 +1,272 @@
|
||||
# G7 Verification Report: SLA Policies, AppliedSLAs, Assignment Policies V2
|
||||
## Task: t_ec88adec — Verify G7 implementation matches Chatwoot original across 5 criteria
|
||||
|
||||
## 1. Interface Paths
|
||||
|
||||
### SLA Policies CRUD
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| List | GET /api/v1/accounts/:account_id/sla_policies | GET /api/v1/accounts/:account_id/sla_policies | ✅ PASS |
|
||||
| Create | POST /api/v1/accounts/:account_id/sla_policies | POST /api/v1/accounts/:account_id/sla_policies | ✅ PASS |
|
||||
| Show | GET /api/v1/accounts/:account_id/sla_policies/:id | GET /api/v1/accounts/:account_id/sla_policies/:id | ✅ PASS |
|
||||
| Update | PUT/PATCH /api/v1/accounts/:account_id/sla_policies/:id | PUT /api/v1/accounts/:account_id/sla_policies/:id | ⚠️ MINOR — Chatwoot supports both PUT and PATCH; gochat only PUT. Rails resources auto-register both. |
|
||||
| Delete | DELETE /api/v1/accounts/:account_id/sla_policies/:id | DELETE /api/v1/accounts/:account_id/sla_policies/:id | ✅ PASS |
|
||||
|
||||
### SLA Inbox Associations (EXTRA — not in Chatwoot routes.rb)
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| List Inboxes | N/A (enterprise-only, not in open-source routes) | GET /sla_policies/:id/inboxes | 🔵 ADDITION — not in Chatwoot open-source routes.rb |
|
||||
| Add Inbox | N/A | POST /sla_policies/:id/inboxes | 🔵 ADDITION |
|
||||
| Remove Inbox | N/A | DELETE /sla_policies/:id/inboxes/:inbox_id | 🔵 ADDITION |
|
||||
|
||||
Note: Chatwoot's open-source schema.rb does NOT have a `sla_policy_inboxes` table, but the enterprise edition may have one. The gochat implementation added these endpoints as a reasonable extension.
|
||||
|
||||
### AppliedSLAs
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Index | GET /api/v1/accounts/:account_id/applied_slas (index) | N/A — not implemented | ❌ FAIL — Chatwoot has `resources :applied_slas, only: [:index]` but gochat does not implement the index (list) endpoint |
|
||||
| Metrics | GET /api/v1/accounts/:account_id/applied_slas/metrics | GET /api/v1/accounts/:account_id/applied_slas/metrics | ✅ PASS |
|
||||
| Download | GET /api/v1/accounts/:account_id/applied_slas/download | GET /api/v1/accounts/:account_id/applied_slas/download | ✅ PASS |
|
||||
|
||||
### Assignment Policies V2 CRUD
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| List | GET /api/v1/accounts/:account_id/assignment_policies | GET /api/v1/accounts/:account_id/assignment_policies_v2 | ⚠️ MISMATCH — path uses `_v2` suffix |
|
||||
| Create | POST /api/v1/accounts/:account_id/assignment_policies | POST /api/v1/accounts/:account_id/assignment_policies_v2 | ⚠️ MISMATCH — path uses `_v2` suffix |
|
||||
| Show | GET /api/v1/accounts/:account_id/assignment_policies/:id | GET /api/v1/accounts/:account_id/assignment_policies_v2/:id | ⚠️ MISMATCH |
|
||||
| Update | PUT/PATCH /assignment_policies/:id | PUT /assignment_policies_v2/:id | ⚠️ MISMATCH |
|
||||
| Delete | DELETE /assignment_policies/:id | DELETE /assignment_policies_v2/:id | ⚠️ MISMATCH |
|
||||
|
||||
Note: The `_v2` suffix is intentional to avoid collision with the existing V1 `assignment_policy` (singular) route in gochat. Chatwoot's original uses `assignment_policies` (plural) for the V2 API.
|
||||
|
||||
### Assignment Policy V2 — Nested Inboxes
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| List Inboxes | GET /assignment_policies/:assignment_policy_id/inboxes | GET /assignment_policies_v2/:id/inboxes | ⚠️ MISMATCH — `_v2` suffix + different param name (:id vs :assignment_policy_id) |
|
||||
| Add Inbox | POST /assignment_policies/:assignment_policy_id/inboxes | POST /assignment_policies_v2/:id/inboxes | ⚠️ MISMATCH |
|
||||
| Remove Inbox | DELETE /assignment_policies/:assignment_policy_id/inboxes/:id | DELETE /assignment_policies_v2/:id/inboxes/:inbox_id | ⚠️ MISMATCH — different param names (:id vs :inbox_id) |
|
||||
|
||||
### Assignment Policy V2 — Reverse (Inbox → Policy)
|
||||
| Endpoint | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Show (inbox) | GET /inboxes/:inbox_id/assignment_policy (show) | GET /inboxes/:inbox_id/assignment_policy_v2 | ⚠️ MISMATCH — `_v2` suffix |
|
||||
| Create/Set (inbox) | POST /inboxes/:inbox_id/assignment_policy (create) | POST /inboxes/:inbox_id/assignment_policy_v2 | ⚠️ MISMATCH — Chatwoot uses POST/create, gochat uses POST/set |
|
||||
| Delete (inbox) | DELETE /inboxes/:inbox_id/assignment_policy (destroy) | DELETE /inboxes/:inbox_id/assignment_policy_v2 | ⚠️ MISMATCH |
|
||||
|
||||
**PATH SUMMARY: 3 PASS, 1 FAIL (missing applied_slas index), 8 MISMATCH (intentional _v2 suffix), 1 MINOR (missing PATCH support)**
|
||||
|
||||
---
|
||||
|
||||
## 2. Request Parameters
|
||||
|
||||
### SLA Policy Create/Update
|
||||
| Param | Chatwoot (schema) | Gochat (model) | Match |
|
||||
|---|---|---|---|
|
||||
| name | string, not null | string, not null | ✅ PASS |
|
||||
| description | string (text) | string (text) | ✅ PASS |
|
||||
| first_response_time_threshold | float | int (response_time, in minutes) | ⚠️ MISMATCH — Chatwoot uses float (seconds), gochat uses int (minutes) |
|
||||
| next_response_time_threshold | float | int (update_time, in minutes) | ⚠️ MISMATCH — Chatwoot uses float (seconds), gochat uses int (minutes) |
|
||||
| resolution_time_threshold | float | int (resolution_time, in minutes) | ⚠️ MISMATCH — Chatwoot uses float (seconds), gochat uses int (minutes) |
|
||||
| only_during_business_hours | boolean, default false | N/A — not in model | ❌ FAIL — missing field |
|
||||
|
||||
Note: The field naming differs: Chatwoot uses `first_response_time_threshold` / `next_response_time_threshold` / `resolution_time_threshold` (float, in seconds), gochat uses `response_time` / `update_time` / `resolution_time` (int, in minutes). The JSON field names differ: `first_response_time_threshold` vs `response_time`.
|
||||
|
||||
### AppliedSLA Metrics
|
||||
| Param | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| conversation_id (query) | Used as filter param | conversation_id (query param, required) | ✅ PASS — gochat requires it explicitly |
|
||||
|
||||
### AppliedSLA Download
|
||||
| Param | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| No specific params | Account-scoped list | account_id from auth context | ✅ PASS |
|
||||
|
||||
### Assignment Policy V2 Create/Update
|
||||
| Param | Chatwoot (schema) | Gochat (model) | Match |
|
||||
|---|---|---|---|
|
||||
| name | string, not null | string, not null | ✅ PASS |
|
||||
| description | text | text | ✅ PASS |
|
||||
| assignment_order | integer (enum: round_robin=0) | type (enum: round_robin/fair/best_skill_match) | ⚠️ MISMATCH — different field name and enum structure |
|
||||
| conversation_priority | integer (enum: earliest_created=0, longest_waiting=1) | N/A | ❌ FAIL — missing field |
|
||||
| fair_distribution_limit | integer, default 100, not null | N/A | ❌ FAIL — missing field |
|
||||
| fair_distribution_window | integer, default 3600, not null | N/A | ❌ FAIL — missing fields |
|
||||
| enabled | boolean, default true, not null | N/A | ❌ FAIL — missing field |
|
||||
|
||||
Note: Gochat's AssignmentPolicyV2 uses a simplified `type` field (round_robin/fair/best_skill_match) instead of Chatwoot's separate assignment_order + conversation_priority + fair_distribution fields. This is a design simplification, not a direct match.
|
||||
|
||||
### Inbox Association (add/remove)
|
||||
| Param | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| inbox_id | inbox_id in body | inbox_id (JSON body) | ✅ PASS |
|
||||
|
||||
**PARAM SUMMARY: 5 PASS, 5 MISMATCH (field naming/units), 5 FAIL (missing fields)**
|
||||
|
||||
---
|
||||
|
||||
## 3. Response Format
|
||||
|
||||
### SLA Policy Response
|
||||
| Field | Chatwoot JSON | Gochat JSON | Match |
|
||||
|---|---|---|---|
|
||||
| id | id | id | ✅ PASS |
|
||||
| name | name | name | ✅ PASS |
|
||||
| description | description | description | ✅ PASS |
|
||||
| first_response_time_threshold | first_response_time_threshold (float) | response_time (int) | ⚠️ MISMATCH — different field name + type |
|
||||
| next_response_time_threshold | next_response_time_threshold (float) | update_time (int) | ⚠️ MISMATCH |
|
||||
| resolution_time_threshold | resolution_time_threshold (float) | resolution_time (int) | ⚠️ MISMATCH |
|
||||
| only_during_business_hours | only_during_business_hours (boolean) | N/A | ❌ FAIL — missing |
|
||||
| account_id | account_id | account_id | ✅ PASS |
|
||||
| created_at | created_at | created_at | ✅ PASS |
|
||||
| updated_at | updated_at | updated_at | ✅ PASS |
|
||||
|
||||
Note: Gochat uses Gin's JSON serialization with `json` tags on struct fields. Rails uses active_model_serializers / jbuilder. The response structure is similar but field names differ for time thresholds.
|
||||
|
||||
### AppliedSLA Response
|
||||
| Field | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| id | id | id | ✅ PASS |
|
||||
| sla_policy_id | sla_policy_id | sla_policy_id | ✅ PASS |
|
||||
| conversation_id | conversation_id | conversation_id | ✅ PASS |
|
||||
| account_id | account_id | account_id | ✅ PASS |
|
||||
| sla_status | sla_status (integer enum) | sla_status (string enum) | ⚠️ MISMATCH — Chatwoot uses integer enum (0=active, 1=violated, 2=completed), gochat uses string enum ("active", "violated", "completed") |
|
||||
| frt/nrt/rt target/actual timestamps | N/A (not in schema) | frt_target_at, nrt_target_at, rt_target_at, frt_actual_at, nrt_actual_at, rt_actual_at | 🔵 ADDITION — gochat tracks computed timestamps |
|
||||
| created_at/updated_at | created_at, updated_at | created_at, updated_at | ✅ PASS |
|
||||
|
||||
### Metrics Response
|
||||
| Field | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| applied_sla + sla_events | Returns applied SLA with events | gin.H{"applied_sla": applied, "sla_events": events} | ✅ PASS — structure matches |
|
||||
|
||||
### Assignment Policy V2 Response
|
||||
| Field | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| id | id | id | ✅ PASS |
|
||||
| account_id | account_id | account_id | ✅ PASS |
|
||||
| name | name | name | ✅ PASS |
|
||||
| description | description | description | ✅ PASS |
|
||||
| assignment_order | assignment_order (integer/enum) | type (string enum) | ⚠️ MISMATCH |
|
||||
| conversation_priority | conversation_priority | N/A | ❌ FAIL |
|
||||
| fair_distribution_limit | fair_distribution_limit | N/A | ❌ FAIL |
|
||||
| fair_distribution_window | fair_distribution_window | N/A | ❌ FAIL |
|
||||
| enabled | enabled | N/A | ❌ FAIL |
|
||||
|
||||
**RESPONSE SUMMARY: 6 PASS, 4 MISMATCH, 5 FAIL (missing fields), 1 ADDITION**
|
||||
|
||||
---
|
||||
|
||||
## 4. Error Handling / Status Codes
|
||||
|
||||
### SLA Policies
|
||||
| Scenario | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Unauthorized (no account) | 401 | 401 (response.ErrUnauthorized) | ✅ PASS |
|
||||
| Invalid ID param | 400 | 400 (response.ErrValidation) | ✅ PASS |
|
||||
| Not found | 404 | handleServiceError → 404/500 | ⚠️ NEEDS VERIFY — depends on handleServiceError mapping |
|
||||
| Validation error (create) | 422 | handleServiceError mapping | ⚠️ NEEDS VERIFY — Chatwoot uses 422 for validation, gochat may use 400 |
|
||||
| Success create | 200/201 | response.OK → 200 | ⚠️ MINOR — Chatwoot returns 201 for create, gochat returns 200 |
|
||||
| Success delete | 200/204 | response.OK → 200 (returns deleted object) | ⚠️ MINOR — Chatwoot returns 204 No Content for destroy |
|
||||
|
||||
### AppliedSLA Metrics
|
||||
| Scenario | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Missing conversation_id | 400 | 400 (response.ErrValidation) | ✅ PASS |
|
||||
| Not found | 404 | handleServiceError | ⚠️ NEEDS VERIFY |
|
||||
| Unauthorized | 401 | 401 | ✅ PASS |
|
||||
|
||||
### Assignment Policy V2
|
||||
| Scenario | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Unauthorized | 401 | 401 | ✅ PASS |
|
||||
| Invalid ID | 400 | 400 | ✅ PASS |
|
||||
| Not found | 404 | handleServiceError | ⚠️ NEEDS VERIFY |
|
||||
| Remove inbox (not associated) | 404 | 500 (fmt.Errorf from service) | ❌ FAIL — should be 404 |
|
||||
| Delete success | 204 | response.OK → 200 (returns deleted object) | ⚠️ MINOR |
|
||||
|
||||
**ERROR HANDLING SUMMARY: 4 PASS, 4 MINOR, 2 NEEDS VERIFY, 1 FAIL**
|
||||
|
||||
---
|
||||
|
||||
## 5. Business Logic Behavior
|
||||
|
||||
### SLA Policy Account Scoping
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| All queries scoped to account_id | Yes (Rails current_account) | Yes (accountID from auth context, passed to service) | ✅ PASS |
|
||||
| Account uniqueness on name | N/A (no unique index on name alone) | uniqueIndex: account_id + name | ⚠️ EXTRA — gochat adds account+name uniqueness beyond Chatwoot schema |
|
||||
| Soft delete | Yes (paranoia gem) | Yes (gorm.DeletedAt) | ✅ PASS |
|
||||
|
||||
### SLA Inbox Uniqueness
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| One SLA policy per inbox | Enterprise-only | uniqueIndex on inbox_id | ✅ PASS — gochat correctly enforces inbox uniqueness |
|
||||
| AddInbox validates inbox belongs to account | N/A | Yes (service checks accountID) | ✅ PASS |
|
||||
|
||||
### AppliedSLA Uniqueness
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Unique on [account_id, sla_policy_id, conversation_id] | Yes (schema index) | N/A — no unique constraint enforced in model | ⚠️ MISMATCH — gochat doesn't enforce this unique constraint |
|
||||
| Account scoping | Yes | Yes (AccountID check in GetAppliedSlaMetrics) | ✅ PASS |
|
||||
|
||||
### SLA Time Threshold Units
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Threshold stored as float (seconds) | Yes | Stored as int (minutes) | ⚠️ MISMATCH — unit conversion needed |
|
||||
| only_during_business_hours flag | Yes | No | ❌ FAIL |
|
||||
|
||||
### Assignment Policy V2 Assignment Strategy
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Round robin assignment | assignment_order: round_robin | type: round_robin | ✅ PASS (same concept, different field) |
|
||||
| Fair distribution | fair_distribution_limit + fair_distribution_window | type: fair (simplified) | ⚠️ MISMATCH — Chatwoot tracks limit/window, gochat just marks type |
|
||||
| Best skill match | N/A (enterprise extension) | type: best_skill_match | 🔵 ADDITION |
|
||||
| Conversation priority ordering | conversation_priority enum (earliest_created, longest_waiting) | N/A | ❌ FAIL — missing |
|
||||
| Enabled/disabled flag | enabled boolean | N/A | ❌ FAIL — missing |
|
||||
|
||||
### Assignment Policy Inbox Association
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| One policy per inbox | Yes (unique index on inbox_id) | Yes (uniqueIndex on inbox_id) | ✅ PASS |
|
||||
| SetInboxPolicy replaces existing | Yes (Rails creates/updates) | Yes (DeleteByInbox then Create) | ✅ PASS |
|
||||
| Account scoping on associations | Yes (via account) | Yes (AccountID check in GetInboxPolicy) | ✅ PASS |
|
||||
|
||||
### Cascade/Dependent Deletes
|
||||
| Behavior | Chatwoot | Gochat | Match |
|
||||
|---|---|---|---|
|
||||
| Delete policy → delete inbox associations | Yes (dependent: :destroy) | N/A — not explicitly handled | ⚠️ NEEDS VERIFY — need to check if GORM cascades |
|
||||
| Delete policy → delete applied SLAs | Yes | N/A — not in service | ⚠️ NEEDS VERIFY |
|
||||
|
||||
**BUSINESS LOGIC SUMMARY: 7 PASS, 4 MISMATCH, 5 FAIL, 3 NEEDS VERIFY, 2 ADDITION**
|
||||
|
||||
---
|
||||
|
||||
## Overall Verification Summary
|
||||
|
||||
| Criterion | Pass | Mismatch | Fail | Needs Verify | Addition |
|
||||
|---|---|---|---|---|---|
|
||||
| Interface Paths | 3 | 8 (intentional _v2) | 1 (missing applied_slas index) | 0 | 3 |
|
||||
| Request Params | 5 | 5 (naming/units) | 5 (missing fields) | 0 | 0 |
|
||||
| Response Format | 6 | 4 (naming/types) | 5 (missing fields) | 0 | 1 |
|
||||
| Error Handling | 4 | 4 (minor) | 1 (wrong status for remove-inbox) | 2 | 0 |
|
||||
| Business Logic | 7 | 4 (units/structure) | 5 (missing features) | 3 | 2 |
|
||||
| **TOTAL** | **25** | **21** | **17** | **5** | **6** |
|
||||
|
||||
### Critical Failures (must fix):
|
||||
1. **Missing AppliedSLAs index endpoint** — Chatwoot has `resources :applied_slas, only: [:index]` but gochat doesn't implement GET /applied_slas (list)
|
||||
2. **Missing `only_during_business_hours` field** on SlaPolicy model
|
||||
3. **Missing AssignmentPolicy fields**: conversation_priority, fair_distribution_limit, fair_distribution_window, enabled
|
||||
4. **Wrong error status for RemoveInbox when inbox not associated** — returns 500 instead of 404
|
||||
5. **AppliedSLA unique constraint** not enforced in gochat model (Chatwoot schema has unique index on [account_id, sla_policy_id, conversation_id])
|
||||
6. **SlaEvent missing fields**: conversation_id, account_id, sla_policy_id, inbox_id, meta — Chatwoot schema has these
|
||||
|
||||
### Intentional Design Differences (acceptable):
|
||||
1. **Path `_v2` suffix** on assignment policies — avoids collision with existing V1 route
|
||||
2. **Simplified AssignmentPolicyV2 type field** — reduces Chatwoot's multi-field approach (assignment_order + conversation_priority + fair_distribution) to a single type enum
|
||||
3. **Time threshold units** — minutes (int) vs seconds (float) — functional equivalent with conversion
|
||||
4. **SLA inbox associations** — added beyond Chatwoot open-source routes (enterprise feature)
|
||||
5. **AppliedSLA timestamp tracking** — gochat adds frt/nrt/rt target and actual timestamps (enhancement)
|
||||
6. **sla_status as string enum** vs Chatwoot's integer enum — more readable but API-incompatible
|
||||
|
||||
### Minor Issues:
|
||||
1. **Missing PATCH support** on SLA policy update — Rails resources auto-register PUT+PATCH
|
||||
2. **Create returns 200** instead of 201 — Chatwoot convention is 201 for resource creation
|
||||
3. **Delete returns 200 with body** instead of 204 No Content — Chatwoot convention is 204
|
||||
Reference in New Issue
Block a user