Files
gochat/docs/requirements/M7-reporting-and-csat.md
T
2026-06-04 15:44:48 +08:00

398 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# M7 报告与CSAT — Chatwoot 功能梳理文档
> 基于 Chatwoot 源码深度阅读 + CodeGraph 上下文梳理
> 生成日期:2026-05-22
> 参照仓库:chatwoot-reference
> 模块覆盖:CSAT Survey · Campaign · Reporting Event + Rollup · Dashboard Stats · Inbox CSAT模板 · Report Metrics
---
## 1. CSAT Survey(客户满意度调查)
### CSAT 调查发送
- 功能描述:当会话被标记为已解决时,系统自动向客户发送满意度调查(CSAT Survey)。支持网站Widget内嵌消息、WhatsApp/Twilio模板消息三种投递通道,同时支持基于标签的 Survey Rules 精准定向。
- 用户操作流程:
1. 客服解决会话 → 系统监听 `conversation_status_changed` 事件
2. `CsatSurveyListener` 触发 `CsatSurveyService`
3. 服务判断 inbox 是否开启 CSAT、是否已发过 CSAT、是否符合 Survey Rules
4. WhatsApp 通道:使用 WhatsApp CSAT 模板投递;Twilio 通道:使用 Twilio CSAT 模板投递;Website 通道:在消息窗口内创建 `input_csat` 类型消息
5. 客户看到评分界面(1-5分 + 文本反馈框)
- 涉及的API端点:
- `GET /public/api/v1/csat_survey/:id` — 客户端获取 CSAT 调查页面(通过 conversation UUID)
- `PUT /public/api/v1/csat_survey/:id` — 客户提交/更新 CSAT 评分与反馈(14天内可修改)
- `GET /api/v1/accounts/:account_id/csat_survey_responses` — 列表查询(支持分页、排序、日期/agent/inbox/team 过滤)
- `GET /api/v1/accounts/:account_id/csat_survey_responses/metrics` — 统计汇总(总评分数、各评分等级分布、发送总数)
- `GET /api/v1/accounts/:account_id/csat_survey_responses/download` — CSV 导出下载
- 涉及的数据模型+关键字段:
- **CsatSurveyResponse**(`csat_survey_responses` 表):
- `rating` (integer, 1-5) — 评分等级
- `feedback_message` (text) — 文本反馈
- `csat_review_notes` (text) — 内部审阅备注
- `message_id` (bigint, UNIQUE) — 关联消息(一条消息唯一对应一条 CSAT 响应)
- `account_id`, `conversation_id`, `contact_id`, `assigned_agent_id` — 多维度关联
- `review_notes_updated_by_id`, `review_notes_updated_at` — 审阅追踪
- 涉及的业务逻辑:
- **CsatSurveyListener**(`app/listeners/csat_survey_listener.rb`):监听会话状态变化,触发 CSAT 发送;监听消息更新,构建 CSAT 响应
- **CsatSurveyService**(`app/services/csat_survey_service.rb`):发送前多重校验(`conversation_allows_csat?` / `csat_enabled?` / `csat_already_sent?` / `csat_allowed_by_survey_rules?`);根据通道类型选择发送方式
- **CsatSurveys::ResponseBuilder**(`app/builders/csat_surveys/response_builder.rb`):解析消息 `submitted_values` 中的 rating 和 feedback,创建/更新 CsatSurveyResponse 记录
- **MessageTemplates::Template::CsatSurvey**(`app/services/message_templates/template/csat_survey.rb`):创建 `content_type: input_csat` 的消息,支持自定义消息内容和 display_type(emoji / 文本)
- 涉及的自动化/规则/事件:
- 会话状态变更 → `CsatSurveyListener.conversation_status_changed` → 自动发送
- CSAT 消息提交 → `CsatSurveyListener.message_updated` → 自动构建 Response
- **Survey Rules**:基于会话标签的定向规则(`contains` / `does_not_contain` 操作符),配置在 inbox `csat_config` 中
- **14天锁定**:CSAT 提交后14天内可更新,超期锁定(`Public::CsatSurveyController.check_csat_locked`)
- Chatwoot原实现代码路径:
- `app/models/csat_survey_response.rb`
- `app/listeners/csat_survey_listener.rb`
- `app/services/csat_survey_service.rb`
- `app/services/message_templates/template/csat_survey.rb`
- `app/builders/csat_surveys/response_builder.rb`
- `app/controllers/public/api/v1/csat_survey_controller.rb`
- `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb`
- 版本标注:v1(CSAT 基础)+ Enterprise(CSAT Response update 端点)
---
## 2. Campaign(营销活动/主动推送)
### Campaign CRUD + 触发
- 功能描述:Campaign 是面向 Inbox 中联系人进行主动消息推送的功能。支持两种类型:**ongoing**(持续性,当新对话进入时自动触发)和 **one_off**(一次性,向指定 audience 批量发送)。仅支持 Website、Twilio SMS、SMS、WhatsApp Inbox。
- 用户操作流程:
1. 用户在 Inbox 设置中创建 Campaign(选择类型、填写标题/消息/触发规则/受众)
2. ongoing 类型:当新对话匹配 trigger_rules 时,自动发送 Campaign 消息
3. one_off 类型:设置 scheduled_at,系统通过 Campaigns::TriggerOneoffCampaignJob 定时触发
4. 触发时根据 Inbox 类型调用对应 Service(Twilio SMS / SMS / WhatsApp)
- 涉及的API端点:
- `POST /api/v1/accounts/:account_id/campaigns` — 创建
- `GET /api/v1/accounts/:account_id/campaigns` — 列表
- `GET /api/v1/accounts/:account_id/campaigns/:id` — 详情
- `PUT /api/v1/accounts/:account_id/campaigns/:id` — 更新
- `DELETE /api/v1/accounts/:account_id/campaigns/:id` — 删除
- `GET /api/v1/widget/campaigns` — Widget端获取进行中的 Campaign
- 涉及的数据模型+关键字段:
- **Campaign**(`campaigns` 表):
- `campaign_type` (enum: ongoing=0, one_off=1) — 活动类型
- `campaign_status` (enum: active=0, completed=1) — 活动状态
- `title` (string, required) — 活动标题
- `message` (text, required) — 活动消息内容
- `enabled` (boolean, default true) — 是否启用
- `trigger_only_during_business_hours` (boolean) — 是否仅在营业时间触发
- `trigger_rules` (jsonb) — 触发规则(ongoing 类型使用)
- `audience` (jsonb) — 受众筛选条件(one_off 类型使用)
- `template_params` (jsonb) — 模板参数(WhatsApp Campaign)
- `scheduled_at` (datetime) — 定时发送时间(one_off 类型)
- `inbox_id` (bigint, required) — 关联 Inbox
- `sender_id` (integer) — 发送者 ID
- `display_id` (integer) — 展示 ID(自增)
- 涉及的业务逻辑:
- **Campaign.trigger!**:one_off 活动触发执行,根据 inbox.inbox_type 选择对应 Service
- **Twilio::OneoffSmsCampaignService** / **Sms::OneoffSmsCampaignService** / **Whatsapp::OneoffCampaignService**:各通道的具体发送实现
- **Liquid::CampaignTemplateService**:支持 Liquid 模板语法(`{{contact.name}}` 等)的消息变量替换
- 创建前校验:`validate_campaign_inbox`(仅支持 Website/Twilio SMS/SMS/WhatsApp)、`sender_must_belong_to_account`、`inbox_must_belong_to_account`
- 已完成的活动不可更新(`prevent_completed_campaign_from_update`)
- 涉及的自动化/规则/事件:
- **Campaigns::TriggerOneoffCampaignJob**:one_off 活动定时触发(scheduled_at 时间到达后执行)
- ongoing 活动:在会话创建时由前端 / Conversation 创建流程中判断 trigger_rules 匹配并发送
- 活动完成后状态自动标记为 `completed`
- Chatwoot原实现代码路径:
- `app/models/campaign.rb`
- `app/controllers/api/v1/accounts/campaigns_controller.rb`
- `app/controllers/api/v1/widget/campaigns_controller.rb`
- `app/jobs/campaigns/trigger_oneoff_campaign_job.rb`
- `app/services/twilio/oneoff_sms_campaign_service.rb`
- `app/services/sms/oneoff_sms_campaign_service.rb`
- `app/services/whatsapp/oneoff_campaign_service.rb`
- `app/services/liquid/campaign_template_service.rb`
- 版本标注:v1 基础;WhatsApp Campaign 需要 feature flag `whatsapp_campaign`
---
## 3. Reporting Event + Rollup(报告事件与数据聚合)
### Reporting Event 采集
- 功能描述:ReportingEvent 是 Chatwoot 报告系统的原始数据层,通过事件监听器在关键业务节点自动采集指标事件。涵盖首次响应时间、回复时间、会话解决时间、Bot 解决/移交等核心指标。每条事件包含时间值、营业时间值、多维度关联(account/inbox/user/conversation)。
- 用户操作流程:
1. 客服首次回复 → `ReportingEventListener.first_reply_created` → 写入 `first_response` 事件
2. 客服后续回复 → `reply_time_created` → 写入 `reply_time` 事件
3. 会话被解决 → `conversation_resolved` → 写入 `conversation_resolved` + `resolution_time` 事件
4. Bot 解决 → `conversation_bot_resolved` → 写入 Bot 解决事件
5. Bot 移交人类 → `conversation_bot_handoff` → 写入 Bot 移交事件
6. 每条事件写入后 → `ReportingEvents::RollupService` 异步聚合到 Rollup 表
- 涉及的API端点:
- `GET /api/v1/accounts/:account_id/conversations/:id/reporting_events` — 会话级事件列表(Enterprise)
- `GET /api/v1/accounts/:account_id/reporting_events` — 账户级事件列表(Enterprise)
- 涉及的数据模型+关键字段:
- **ReportingEvent**(`reporting_events` 表):
- `name` (string, required) — 事件名(first_response / reply_time / conversation_resolved / conversation_bot_resolved / conversation_bot_handoff / conversation_opened)
- `value` (float, required) — 时间值(秒)
- `value_in_business_hours` (float) — 营业时间内的时间值
- `event_start_time` / `event_end_time` (datetime) — 事件起止时间
- `account_id`, `inbox_id`, `user_id`, `conversation_id` — 多维度关联
- **ReportingEventsRollup**(`reporting_events_rollups` 表):
- `metric` (enum: resolutions_count / first_response / resolution_time / reply_time / bot_resolutions_count / bot_handoffs_count) — 指标名
- `dimension_type` (enum: account / agent / inbox / team) — 维度类型
- `dimension_id` (bigint) — 维度值
- `date` (date) — 聚合日期
- `count` (bigint) — 事件计数
- `sum_value` (float) — 时间值总和
- `sum_value_business_hours` (float) — 营业时间值总和
- 涉及的业务逻辑:
- **ReportingEventListener**(`app/listeners/reporting_event_listener.rb`):监听6种事件,计算时间差并写入 ReportingEvent
- `first_reply_created`:计算首次响应时间 = message.created_at - last_non_human_activity
- `conversation_resolved`:计算解决时间 = event_end_time - conversation.created_at
- `reply_time_created`:计算回复时间差
- `conversation_bot_resolved` / `conversation_bot_handoff`:Bot 相关指标
- **ReportingEventHelper.last_non_human_activity**:计算首次响应基线时间,优先取 bot_handoff / conversation_opened 事件时间,回退到 bot_resolved 或 conversation.created_at
- **ReportingEventHelper.business_hours**:利用 `working_hours` gem 计算 inbox 营业时间内的有效时长
- **ReportingEvents::RollupService**:每条 ReportingEvent 写入后触发,按 account/agent/inbox 三维度聚合到 Rollup 表(upsert + count/sum 累加)
- **ReportingEvents::EventMetricRegistry**:定义事件 → Rollup 指标的映射关系
- **ReportingEvents::BackfillService**:历史数据回填,批量聚合指定日期范围内的原始事件
- 涉及的自动化/规则/事件:
- 事件写入 → `safe_rollup(reporting_event)` 自动触发 Rollup 聚合
- Rollup 仅在账户设置了 `reporting_timezone` 时启用(软开关)
- Rollup 读取路径通过 feature flag `reporting_events_rollup` 控制
- 首次响应时间基线考虑 Bot 移交 / 重开等场景(`last_non_human_activity`)
- Chatwoot原实现代码路径:
- `app/models/reporting_event.rb`
- `app/models/reporting_events_rollup.rb`
- `app/listeners/reporting_event_listener.rb`
- `app/helpers/reporting_event_helper.rb`
- `app/services/reporting_events/rollup_service.rb`
- `app/services/reporting_events/event_metric_registry.rb`
- `app/services/reporting_events/metric_registry.rb`
- `app/services/reporting_events/backfill_service.rb`
- `db/migrate/20260211145813_create_reporting_events_rollup.rb`
- 版本标注:v1 ReportingEvent 基础;Rollup 为后续增强(2026年新增 Rollup 表 + 聚合机制)
---
## 4. Dashboard Stats(仪表盘实时统计)
### Live Reports(实时会话指标)
- 功能描述:LiveReports 提供会话维度的实时统计,包括打开数、未关注数、未分配数、等待数。支持按 team_id / assignee_id 分组聚合,用于仪表盘实时展示。
- 用户操作流程:
1. 用户打开仪表盘首页 → 前端请求 live report API
2. 系统查询当前账户的会话实时状态
3. 返回 open/unattended/unassigned/pending 计数
4. 如请求 grouped 接口 → 按 team 或 agent 分组返回各组计数
- 涉及的API端点:
- `GET /api/v2/accounts/:account_id/live_reports/conversation_metrics` — 全局实时指标(需 `report:view` 权限)
- `GET /api/v2/accounts/:account_id/live_reports/grouped_conversation_metrics?group_by=team_id/assignee_id` — 分组实时指标
- 参数:`team_id`, `inbox_id`, `user_ids[]` — 过滤条件
- 涉及的数据模型+关键字段:
- 基于 **Conversation** 模型的 scope 查询:
- `open` — 状态为 open 的会话
- `unattended` — open + 无人工回复的会话
- `unassigned` — open + 无 assignee 的会话
- `pending` — 状态为 pending 的会话
- 涉及的业务逻辑:
- **LiveReportsController** 直接用 Conversation scope 做 count/group 查询
- 分组模式:`group(@group_scope).count` 按 team_id 或 assignee_id 分组统计
- 权限控制:`authorize :report, :view?`
- 涉及的自动化/规则/事件:无(纯查询接口)
- Chatwoot原实现代码路径:
- `app/controllers/api/v2/accounts/live_reports_controller.rb`
- 版本标注:v2
### Dashboard Apps(自定义仪表盘应用)
- 功能描述:DashboardApp 是嵌入在 Chatwoot 主仪表盘中的自定义 iframe 应用模块。用户可配置标题和内容(URL + type=frame),将外部报表/工具嵌入到客服工作界面。
- 用户操作流程:
1. 管理员在设置中创建 Dashboard App(配置标题和 iframe URL)
2. 前端在仪表盘侧边栏或主区域渲染 iframe
3. 外部应用通过 iframe 嵌入展示
- 涉及的API端点:
- `POST /api/v1/accounts/:account_id/dashboard_apps` — 创建
- `GET /api/v1/accounts/:account_id/dashboard_apps` — 列表
- `GET /api/v1/accounts/:account_id/dashboard_apps/:id` — 详情
- `PUT /api/v1/accounts/:account_id/dashboard_apps/:id` — 更新
- `DELETE /api/v1/accounts/:account_id/dashboard_apps/:id` — 删除
- 涉及的数据模型+关键字段:
- **DashboardApp**(`dashboard_apps` 表):
- `title` (string, required) — 应用标题
- `content` (jsonb, required) — 内容配置,结构为 `[ { url: "https://...", type: "frame" } ]`
- `account_id`, `user_id` — 关联
- 涉及的业务逻辑:
- JSON Schema 校验 content 格式(必须为 array,每项必须有 url 和 type=frame,url 必须为 http/https)
- 空内容自动置为 `[]`
- 涉及的自动化/规则/事件:无
- Chatwoot原实现代码路径:
- `app/models/dashboard_app.rb`
- `app/controllers/api/v1/accounts/dashboard_apps_controller.rb`
- 版本标注:v1
---
## 5. Inbox CSAT 模板(WhatsApp/Twilio CSAT 模板管理)
### CSAT 模板创建与管理
- 功能描述:针对 WhatsApp 和 Twilio WhatsApp 通道,CSAT 调查需要通过官方模板(Template)发送。此功能提供 CSAT 模板的状态查询、创建、效用分析(Captain AI 评分)三部分能力。模板创建后自动更新 Inbox 的 `csat_config` 配置。
- 用户操作流程:
1. 管理员进入 Inbox 设置 → CSAT 模板管理
2. 查看当前模板状态(是否已创建、是否已审批通过)
3. 创建新模板(填写消息文本、按钮文本、语言)→ 系统调用 WhatsApp/Twilio API 创建
4. 可选使用 AI 分析功能(analyze)对模板文本质量进行评分
5. 模板审批通过后,CsatSurveyService 在 WhatsApp 通道优先使用模板发送
- 涉及的API端点:
- `GET /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template` — 查看模板状态
- `POST /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template` — 创建模板
- `POST /api/v1/accounts/:account_id/inboxes/:inbox_id/csat_template/analyze` — AI 分析模板效用(需 Captain feature flag)
- 涉及的数据模型+关键字段:
- **Inbox** 的 `csat_config` (jsonb):
- `csat_survey_enabled` (boolean) — 是否开启 CSAT
- `message` (string) — 自定义 CSAT 消息文本
- `display_type` (string) — 评分展示类型(emoji / text)
- `survey_rules` — 标签定向规则
- `template` — WhatsApp/Twilio 模板配置(template name、language、status 等)
- 涉及的业务逻辑:
- **CsatTemplateManagementService**:
- `template_status`:根据 Inbox 类型调用 WhatsApp/Twilio API 查询模板审批状态
- `create_template`:验证参数 → 删除旧模板 → 调用对应 Provider API 创建 → 更新 `csat_config`
- **Whatsapp::CsatTemplateService** / **Twilio::CsatTemplateService**:封装 WhatsApp Business API 和 Twilio API 的模板 CRUD
- **CsatTemplateUtilityAnalysisService**:调用 Captain AI 分析模板文本质量(按钮文字、语言适配性)
- **CsatTemplateNameService**:生成标准化的 CSAT 模板命名
- **CsatTemplateUtilityRubric**:定义 CSAT 模板质量评分标准
- 涉及的自动化/规则/事件:
- 模板创建 → 自动更新 Inbox `csat_config` 的 `template` 字段
- 模板审批状态变更 → 前端重新查询状态
- CsatSurveyService 发送时检查 `template_available_and_approved?`,优先使用模板
- Chatwoot原实现代码路径:
- `app/controllers/api/v1/accounts/inbox_csat_templates_controller.rb`
- `app/services/csat_template_management_service.rb`
- `app/services/csat_template_utility_analysis_service.rb`
- `app/services/csat_template_utility_rubric.rb`
- `app/services/csat_template_name_service.rb`
- `app/services/whatsapp/csat_template_service.rb`
- `app/services/twilio/csat_template_service.rb`
- `app/services/twilio/csat_template_api_client.rb`
- 版本标注:v1;AI analyze 需要 Captain feature flag
---
## 6. Report Metrics(报告指标与数据查询体系)
### 指标注册体系
- 功能描述:Chatwoot 报告系统采用双层指标注册机制。**ReportingEvents::EventMetricRegistry** 定义写入侧(原始事件 → Rollup 指标的映射),**Reports::ReportMetricRegistry** 定义读取侧(API 请求指标名 → 查询策略的映射)。两者保持指标名对齐,确保写入和读取路径一致。
- 核心指标清单:
- **conversations_count** — 会话数(count)
- **incoming_messages_count** — 收到消息数(count)
- **outgoing_messages_count** — 发出消息数(count)
- **avg_first_response_time** — 平均首次响应时间(average, raw_event: first_response, rollup: first_response)
- **avg_resolution_time** — 平均解决时间(average, raw_event: conversation_resolved, rollup: resolution_time)
- **avg_reply_time** — 平均回复时间(average, raw_event: reply_time, rollup: reply_time)
- **resolved_conversations_count** — 已解决会话数(count, raw_event: conversation_resolved)
- **bot_resolutions_count** — Bot 解决数(count)
- **bot_handoffs_count** — Bot 移交数(count)
### 时序报告(Timeseries Report)
- 功能描述:时序报告按时间维度(day/week/month/year/hour)聚合指标数据,支持按 account/inbox/agent/label/team 维度过滤,返回 `{ value, timestamp, count }` 结构的时序数据。数据源自动选择 RawDataSource(原始事件查询)或 RollupDataSource(聚合表查询,预留)。
- 用户操作流程:
1. 前端请求指定 metric + type + id + 时间范围的报告 API
2. ReportBuilder 根据 metric 从 ReportMetricRegistry 获取查询策略
3. DataSource.for() 创建数据源实例(当前默认 RawDataSource)
4. 执行 timeseries 或 aggregate 查询 → 返回时间序列数据
- 涉及的API端点:
- `GET /api/v2/accounts/:account_id/reports?metric=avg_first_response_time&type=account&id=1&since=...&until=...` — 时序数据
- `GET /api/v2/accounts/:account_id/reports/summary` — 全局汇总
- `GET /api/v2/accounts/:account_id/reports/bot_summary` — Bot 汇总
- 涉及的数据模型+关键字段:
- 基于 ReportingEvent + ReportingEventsRollup 的查询
- DataSource 参数:`account, metric, dimension_type, dimension_id, scope, range, group_by, timezone_offset, business_hours`
- 涉及的业务逻辑:
- **V2::Reports::Conversations::ReportBuilder** → **V2::Reports::Timeseries::ReportBuilder** → **Reports::DataSource.for()** → **Reports::RawDataSource**
- RawDataSource.timeseries:average 指标 → `group_by_period + average + count`;count 指标 → `group_by_period + count`
- RawDataSource.summary:按 dimension_type 分组查询各指标(conversations_count / resolved_count / avg_resolution_time / avg_first_response_time / avg_reply_time)
- 支持 business_hours 参数:当 `use_business_hours?` 为 true 时查询 `value_in_business_hours` 字段
- 涉及的自动化/规则/事件:
- Rollup 读取路径预留:DataSource.for() 中注释 `# TODO: Route to Reports::RollupDataSource when rollup reads are implemented`
- Chatwoot原实现代码路径:
- `app/services/reports/report_metric_registry.rb`
- `app/services/reports/data_source.rb`
- `app/services/reports/raw_data_source.rb`
- `app/builders/v2/reports/conversations/report_builder.rb`
- `app/builders/v2/reports/conversations/base_report_builder.rb`
- `app/builders/v2/reports/timeseries/report_builder.rb`
- `app/builders/v2/reports/timeseries/base_timeseries_builder.rb`
- `app/controllers/api/v2/accounts/reports_controller.rb`
- 版本标注:v2;Rollup 读取路径待实现
### Summary Reports(分组汇总报告)
- 功能描述:Summary Reports 按不同维度(Agent / Team / Inbox / Label / Channel)提供分组汇总数据,每组返回 conversations_count、resolved_conversations_count、avg_resolution_time、avg_first_response_time、avg_reply_time 五项指标。支持 business_hours 和日期范围过滤。Channel Summary 有额外日期范围长度限制(≤31天)。
- 用户操作流程:
1. 前端选择维度(Agent/Team/Inbox/Label/Channel)+ 日期范围 + 是否仅营业时间
2. 后端调用对应 SummaryBuilder → DataSource.summary → 聚合查询
3. Builder 将结果按维度分组整理返回
- 涉及的API端点:
- `GET /api/v2/accounts/:account_id/summary_reports/agent?since=...&until=...&business_hours=true`
- `GET /api/v2/accounts/:account_id/summary_reports/team`
- `GET /api/v2/accounts/:account_id/summary_reports/inbox`
- `GET /api/v2/accounts/:account_id/summary_reports/label`
- `GET /api/v2/accounts/:account_id/summary_reports/channel`(≤31天限制)
- 涉及的数据模型+关键字段:
- 通过 Reports::DataSource.summary 查询 ReportingEvent / ReportingEventsRollup
- SummaryBuilder 的 group_by_key:Agent → `user_id`,Team → `conversations.team_id`,Inbox → `inbox_id`
- 涉及的业务逻辑:
- **V2::Reports::AgentSummaryBuilder**:按 user_id 分组,遍历 account_users 构建每个 Agent 的指标字典
- **V2::Reports::TeamSummaryBuilder** / **InboxSummaryBuilder** / **LabelSummaryBuilder** / **ChannelSummaryBuilder**:类似模式
- **V2::Reports::BaseSummaryBuilder**:统一 load_data → prepare_report 流程,data_source 使用 summary_dimension_type 映射
- Channel Summary 特殊:日期范围 ≤31天校验
- 涉及的自动化/规则/事件:无(纯查询)
- Chatwoot原实现代码路径:
- `app/controllers/api/v2/accounts/summary_reports_controller.rb`
- `app/builders/v2/reports/base_summary_builder.rb`
- `app/builders/v2/reports/agent_summary_builder.rb`
- `app/builders/v2/reports/team_summary_builder.rb`
- `app/builders/v2/reports/inbox_summary_builder.rb`
- `app/builders/v2/reports/label_summary_builder.rb`
- `app/builders/v2/reports/channel_summary_builder.rb`
- 版本标注:v2
### CSV 导出报告
- 功能描述:Reports Controller 的 agents/inboxes/labels/teams/conversations_summary/conversation_traffic 端点同时支持 JSON 和 CSV 导出。前端请求时根据响应格式选择渲染方式。
- 涉及的API端点:
- `GET /api/v2/accounts/:account_id/reports/agents` — Agent 报告(JSON/CSV)
- `GET /api/v2/accounts/:account_id/reports/inboxes` — Inbox 报告(JSON/CSV)
- `GET /api/v2/accounts/:account_id/reports/labels` — Label 报告
- `GET /api/v2/accounts/:account_id/reports/teams` — Team 报告
- `GET /api/v2/accounts/:account_id/reports/conversations_summary` — 会话汇总
- `GET /api/v2/accounts/:account_id/reports/conversation_traffic` — 会话流量热力图
- `GET /api/v2/accounts/:account_id/reports/bot_metrics` — Bot 指标
- `GET /api/v2/accounts/:account_id/reports/first_response_time_distribution` — 首次响应时间分布
- 涉及的业务逻辑:
- **ReportsHelper**(`app/helpers/api/v2/accounts/reports_helper.rb`):调用各 SummaryBuilder 构建 JSON 数据,再转换为 CSV 格式
- **HeatmapHelper**:会话流量热力图数据,按小时 × 日期矩阵组织
- `generate_csv` 方法:根据格式参数渲染 CSV template 或 JSON
- Chatwoot原实现代码路径:
- `app/helpers/api/v2/accounts/reports_helper.rb`
- `app/helpers/api/v2/accounts/heatmap_helper.rb`
- `app/controllers/api/v2/accounts/reports_controller.rb`
- `app/views/api/v2/accounts/reports/` — CSV 模板
- 版本标注:v2
---
## 附录:模块间依赖关系
| 功能 | 依赖 |
|------|------|
| CSAT Survey | Inbox.csat_config, Conversation.resolved, Message.input_csat, WhatsApp/Twilio Template |
| Campaign | Inbox.inbox_type, Liquid Template, Contact/Audience |
| Reporting Event | Conversation lifecycle events, Inbox.working_hours, User assignments |
| Reporting Rollup | Reporting Event (写入触发), Account.reporting_timezone |
| Live Reports | Conversation.open/unattended/unassigned/pending scopes |
| Dashboard Apps | Account, User |
| Inbox CSAT Template | Inbox.channel (WhatsApp/Twilio), csat_config, Captain AI |
| Report Metrics | ReportingEvent + Rollup (数据源), ReportMetricRegistry (查询策略) |
| Summary Reports | DataSource.summary, Conversation group scopes |
| CSAT Survey → Reporting | CSAT 评分数据未直接写入 ReportingEvent(独立查询体系) |
---
## 附录:核心数据表一览
| 表名 | 主要字段 | 索引特点 |
|------|---------|---------|
| `csat_survey_responses` | rating(1-5), feedback_message, message_id(UNIQUE), assigned_agent_id, contact_id | message_id 唯一索引,多维度关联索引 |
| `campaigns` | campaign_type(ongoing/one_off), campaign_status(active/completed), trigger_rules(jsonb), audience(jsonb), scheduled_at | campaign_type/status/scheduled_at 索引 |
| `reporting_events` | name, value, value_in_business_hours, account_id, inbox_id, user_id, conversation_id, event_start/end_time | (account_id,name,created_at) 复合索引,response_distribution 复合索引 |
| `reporting_events_rollups` | metric, dimension_type, dimension_id, date, count, sum_value, sum_value_business_hours | (account_id,date,dimension_type,dimension_id,metric) UNIQUE,timeseries/summary 索引 |
| `dashboard_apps` | title, content(jsonb: [{url,type:"frame"}]), account_id, user_id | account_id, user_id 索引 |