Research Platform — EUJ 端到端用户旅程
DAG Workflow Campaign System | 更新时间: 2026-02-24
模块定位:Research Platform 是一个基于 DAG(有向无环图)的营销自动化和 A/B 测试平台。用户通过可视化画布设计 Campaign 工作流,配置受众定位、多渠道触达(SMS/Email/Voice)、事件监听和转化追踪。
TOUCHES (Pages — 8):
admin/marketing/research/page.tsx (列表 + 发送历史 2 tabs),
admin/marketing/research/new/page.tsx (旧创建表单),
admin/marketing/research/[id]/page.tsx (仪表板详情),
admin/marketing/research/[id]/workflow/page.tsx (DAG 编辑器),
admin/marketing/research/[id]/participants/page.tsx (参与者列表),
admin/marketing/research/[id]/outreach/page.tsx (触达历史),
admin/marketing/research/[id]/analytics/page.tsx (分析面板),
admin/marketing/research/new/workflow → id=new 模式 (新创建入口)
TOUCHES (Components — 20+):
WorkflowCanvas.tsx,
ResearchNode.tsx,
NodePalette.tsx,
ConfigPanel.tsx,
CustomerSelectorForm.tsx,
SplitGroupForm.tsx,
AiVoiceForm.tsx,
AiSmsForm.tsx,
AiEmailForm.tsx,
ManualTaskForm.tsx,
EventGateForm.tsx,
ConditionCheckForm.tsx,
WaitDelayForm.tsx,
RecordConversionForm.tsx,
CampaignAnalytics.tsx,
ConversionFunnel.tsx,
GroupComparison.tsx,
StatisticalSignificance.tsx,
ParticipantTable.tsx,
SendHistoryTable.tsx
TOUCHES (Hooks — 3):
useResearchApi.ts,
useOutreachApi.ts,
useAudienceApi.ts
TOUCHES (Backend):
backend/api/research.js (~2400 行),
backend/services/research/ (12 个 service 模块),
backend/services/research/dagExecutor.js (DAG 执行引擎)
PERMISSION: marketing:view (读) / marketing:manage (写)
1. EUJ 健康度评分卡
2. 全局导航入口
flowchart LR
A[侧边栏 Marketing] --> B[Marketing 主页]
B --> C["Research Platform 卡片"]
C --> D["/research 列表页"]
style A fill:#334155,stroke:#00d4ff,color:#fff
style B fill:#334155,stroke:#00d4ff,color:#fff
style C fill:#4c1d95,stroke:#a78bfa,color:#fff
style D fill:#4c1d95,stroke:#a78bfa,color:#fff
| 入口 | 权限 | 路径 | 状态 |
| 侧边栏 → Marketing |
marketing:view |
/admin/marketing |
OK |
| Marketing 主页 → Research 卡片 |
marketing:view |
/admin/marketing/research |
OK |
3. 端到端用户旅程
flowchart TD
L["列表页 /research"] -->|"新建 Campaign"| W1["Workflow 编辑器 (new)"]
L -->|"点击项目名"| W2["Workflow 编辑器 (existing)"]
W1 -->|"保存"| W2
W2 -->|"??? 缺少启动入口"| X["需返回列表/详情页"]
L -->|"下拉菜单 Start"| RUN["执行中"]
X -->|"详情页 Start"| RUN
RUN -->|"查看"| MON["监控子页面"]
MON --> P["参与者列表"]
MON --> O["触达历史"]
MON --> AN["分析面板"]
RUN -->|"暂停/完成"| DONE["已完成"]
DONE --> AN
style W1 fill:#4c1d95,stroke:#a78bfa,color:#fff
style W2 fill:#4c1d95,stroke:#a78bfa,color:#fff
style RUN fill:#166534,stroke:#4ade80,color:#fff
style DONE fill:#1e3a5f,stroke:#60a5fa,color:#fff
style X fill:#7f1d1d,stroke:#ef4444,color:#fff
3.1 Step 1 — 创建 Campaign
两套创建入口并存:
- 新流程(当前默认): 列表页 "创建" 按钮 →
/research/new/workflow → DAG 编辑器 (id=new 模式),在顶栏内联输入名称+场景 → 保存
- 旧流程(废弃但未删除):
/research/new → 独立表单页(名称+描述+场景+样本量)→ 创建 → 跳转到详情页 /research/:id
正常用户路径不会触及旧页面,但 URL 直接可访问。
1
用户点击"新建 Campaign"
research/page.tsx:230 → href="/research/new/workflow"
进入 DAG 编辑器 new 模式,左侧节点面板、中央空白画布、右侧配置面板
2
在顶栏输入名称 + 选择场景
research/[id]/workflow/page.tsx:268-287
内联 input + select,无需独立表单页
3
点击 Save 保存
workflow/page.tsx:134-209 → POST /api/research
先执行 DAG 验证 (validateWorkflowGraph),错误阻止保存,警告提示但允许保存。创建成功后 URL 切换为 /research/:id/workflow
| 层级 | 文件 | 状态 |
| 前端页面 | workflow/page.tsx (new 模式) | OK |
| React Hook | useCreateCampaign() | OK |
| API | POST /api/research | OK |
| Service | campaignService.createCampaign() | OK |
| DB | INSERT INTO research_campaigns | OK |
3.2 Step 2 — 设计 Workflow (DAG)
4
从节点面板拖拽节点到画布
NodePalette.tsx → WorkflowCanvas.tsx (ReactFlow)
可用节点类型:Customer Selector, Split Group, AI SMS, AI Email, AI Voice, Manual Task, Event Gate, Condition Check, Wait/Delay, Record Conversion
5
连接节点 + 配置参数
ConfigPanel.tsx → ConfigForms/*.tsx
点击节点 → 右侧面板显示对应配置表单。每种节点类型有独立的 ConfigForm
6
保存 Workflow
workflow/page.tsx:185 → PUT /api/research/:id
workflow_graph (nodes + edges + viewport) 存入 research_campaigns.workflow_graph JSONB 列
| 层级 | 文件 | 状态 |
| DAG 画布 | WorkflowCanvas.tsx (ReactFlow) | OK |
| 节点类型注册 | NODE_TYPE_REGISTRY | OK |
| 节点配置表单 | ConfigForms/*.tsx (10 种) | OK |
| DAG 验证 | validateWorkflowGraph() | OK |
| API | PUT /api/research/:id | OK |
3.3 Step 3 — 启动 Campaign
GAP: Workflow 编辑器缺少启动入口
用户在 Workflow 编辑器中完成 DAG 设计后,没有 Start 按钮。必须先导航回列表页或详情页才能启动。这是一个明显的 UX 断裂。
7
从 Workflow 页启动 Campaign
workflow/page.tsx — 无 Start 按钮
用户被迫:点击 "Back" → 回到列表页 → 找到项目 → 下拉菜单 → Start
或:手动修改 URL 为 /research/:id → 仪表板页面 → Start 按钮
| 启动入口 | 所在页面 | 状态 |
| 列表页下拉菜单 → Start | research/page.tsx:341 | OK |
| 详情仪表板页 → Start | research/[id]/page.tsx:188 | OK |
| Workflow 编辑器 → Start | workflow/page.tsx | MISSING |
| API | POST /api/research/:id/start | OK |
| 执行引擎 | dagExecutor.startCampaign() | OK |
3.4 Step 4 — 监控执行
8
Workflow 画布实时指标
workflow/page.tsx:74 → useNodeMetrics(campaignId, isRunning)
运行态每 5s 轮询 GET /api/research/:id/node-metrics,DAG 节点上覆盖 metrics overlay(processed / success_rate / waiting)
9
详情仪表板 Quick Stats
research/[id]/page.tsx:253-284
5 张统计卡片:Total Participants / Reached / Responded / Converted / Revenue
10
触达历史
research/[id]/outreach/page.tsx
按渠道/状态筛选,展开行查看 OutreachTimeline,统计卡片显示发送/投递/打开/失败率
| 监控维度 | 数据来源 | 状态 |
| DAG 节点实时指标 | GET /api/research/:id/node-metrics | OK |
| Campaign 整体统计 | GET /api/research/:id/stats | OK |
| 触达历史日志 | useOutreachHistory(campaignId) | OK |
| 触达 CSV 导出 | outreach/page.tsx:381 | TODO |
3.5 Step 5 — 查看分析
11
分析面板
research/[id]/analytics/page.tsx
CampaignAnalytics 组件:组别对比、转化漏斗、统计显著性检验。可导出 PDF/CSV 报告
| 分析功能 | 组件/API | 状态 |
| 组别对比表 | GroupComparison.tsx | OK |
| 转化漏斗 | ConversionFunnel.tsx / GET .../conversions/funnel | OK |
| 统计显著性 | StatisticalSignificance.tsx | OK |
| 报告生成 | POST /api/research/:id/reports/generate | OK |
| 时间序列 | GET /api/research/:id/stats/timeseries | OK |
3.6 Step 6 — 平台级发送历史
12
跨 Campaign 发送历史
research/page.tsx:421-442 (第二个 Tab)
列表页 "发送历史" Tab → SendHistoryTable 组件,跨所有 Campaign 的发送记录,支持筛选 + CSV 导出
| 功能 | Hook / API | 状态 |
| 发送日志列表 | useSendHistory() / GET /api/research/send-history | OK |
| 统计汇总 | useSendHistoryStats() | OK |
| CSV 导出 | useExportSendHistory() | OK |
4. 页面导航结构
flowchart TD
LIST["/research 列表页
Tab: 项目列表 | 发送历史"]
LIST -->|"创建"| NEW_WF["Workflow 编辑器 (new)"]
LIST -->|"点击项目名"| EXIST_WF["Workflow 编辑器 (existing)"]
subgraph "Campaign 子页面 (无统一导航栏)"
DETAIL["/research/:id 仪表板
Tab: overview | groups | participants"]
EXIST_WF["/research/:id/workflow
DAG 编辑器"]
PART["/research/:id/participants
参与者列表"]
OUT["/research/:id/outreach
触达历史"]
ANA["/research/:id/analytics
分析面板"]
end
EXIST_WF -.->|"Back 按钮"| LIST
DETAIL -.->|"Back"| LIST
DETAIL -->|"participants tab 链接"| PART
PART -.->|"Back"| DETAIL
style LIST fill:#334155,stroke:#00d4ff,color:#fff
style EXIST_WF fill:#4c1d95,stroke:#a78bfa,color:#fff
style NEW_WF fill:#4c1d95,stroke:#a78bfa,color:#fff
style DETAIL fill:#334155,stroke:#60a5fa,color:#fff
style PART fill:#334155,stroke:#60a5fa,color:#fff
style OUT fill:#334155,stroke:#60a5fa,color:#fff
style ANA fill:#334155,stroke:#60a5fa,color:#fff
GAP: 子页面之间缺乏统一 Tab 导航
进入某个 Campaign 后,5 个子页面(Workflow / Detail / Participants / Outreach / Analytics)之间
没有横向 Tab 栏 进行快速切换。
- 详情页有 3 个内部 Tab(overview / groups / participants),但 participants Tab 只是一个链接指向独立页面
- Workflow 页只有 Back 按钮回到列表页
- Outreach / Analytics是完全独立的页面,互不链接
- 用户需要手动修改 URL 或多次返回才能在子页面间导航
5. 关键 GAP 汇总
| # | GAP 描述 | 影响 | 建议优先级 |
| G1 |
缺少统一子导航栏 Campaign 子页面之间无横向 Tab 切换 |
用户在 Workflow/Detail/Outreach/Analytics 之间切换极其不便 |
P0 |
| G2 |
Workflow 页缺少 Start 按钮 完成 DAG 设计后无法直接启动 |
核心操作链路断裂,强制用户跳转 |
P0 |
| G3 |
两套创建流程并存 /research/new(旧表单)与 /research/new/workflow(新入口)同时存在 |
代码冗余,旧页面可直接访问但不在正常流程中 |
P1 |
| G4 |
Outreach 页 CSV 导出是 TODO 按钮存在但点击只弹 alert |
功能承诺但未交付 |
P1 |
| G5 |
详情页 "Configure" 链接指向不存在的 /edit 页面 详情页:179 → /research/:id/edit |
点击后 404 |
P0 |
| G6 |
参与者分配无前端入口
POST /api/research/:id/allocate API 存在但前端无触发按钮 |
API 可用但用户无法操作 |
P1 |
| G7 |
详情页硬编码 Channel 标签
CHANNEL_LABELS 对象未使用 i18n |
违反 i18n 规范 |
P2 |
6. 理想 EUJ 流程(建议改进后)
flowchart TD
LIST["列表页 /research"] -->|"新建"| WF_NEW["Workflow 编辑器 (new)"]
LIST -->|"点击项目"| WF_EXIST["Workflow 编辑器 (existing)"]
WF_NEW -->|"Save"| WF_EXIST
subgraph "Campaign 统一子导航栏"
direction LR
TAB_WF["Workflow"]
TAB_PART["Participants"]
TAB_OUT["Outreach"]
TAB_ANA["Analytics"]
TAB_SET["Settings"]
end
WF_EXIST -->|"顶栏 Start 按钮"| RUNNING["执行中"]
TAB_WF --- TAB_PART
TAB_PART --- TAB_OUT
TAB_OUT --- TAB_ANA
TAB_ANA --- TAB_SET
RUNNING --> TAB_WF
RUNNING --> TAB_PART
RUNNING --> TAB_OUT
RUNNING --> TAB_ANA
style LIST fill:#334155,stroke:#00d4ff,color:#fff
style WF_NEW fill:#4c1d95,stroke:#a78bfa,color:#fff
style WF_EXIST fill:#4c1d95,stroke:#a78bfa,color:#fff
style RUNNING fill:#166534,stroke:#4ade80,color:#fff
style TAB_WF fill:#1e3a5f,stroke:#60a5fa,color:#fff
style TAB_PART fill:#1e3a5f,stroke:#60a5fa,color:#fff
style TAB_OUT fill:#1e3a5f,stroke:#60a5fa,color:#fff
style TAB_ANA fill:#1e3a5f,stroke:#60a5fa,color:#fff
style TAB_SET fill:#1e3a5f,stroke:#60a5fa,color:#fff
改进方向:
- 添加 Campaign 级统一 Tab 导航栏,连接所有子页面
- 在 Workflow 编辑器顶栏增加 Start/Pause/Complete 按钮
- 移除旧创建表单
/research/new,统一用 Workflow 编辑器 new 模式
- 修复详情页 "Configure" 链接(指向 /edit → 应改为 /workflow)
- 在参与者页面增加 "Allocate" 按钮,或在 Workflow 中集成分配操作
7. 数据库表结构
erDiagram
research_campaigns ||--o{ campaign_groups : "has"
research_campaigns ||--o{ campaign_participants : "has"
research_campaigns ||--o{ campaign_audience_criteria : "has"
research_campaigns ||--o{ campaign_outreach_logs : "has"
research_campaigns ||--o{ campaign_stats : "has"
research_campaigns ||--o{ campaign_node_metrics : "has"
research_campaigns ||--o{ campaign_event_listeners : "has"
research_campaigns ||--o| campaign_delivery_config : "has"
campaign_groups ||--o{ campaign_participants : "contains"
campaign_participants ||--o{ campaign_outreach_logs : "sent to"
campaign_participants ||--o{ campaign_conversions : "converted"
research_campaigns {
uuid id PK
text name
text scenario
text status
jsonb workflow_graph
}
campaign_groups {
uuid id PK
uuid campaign_id FK
text channel
int allocation_percent
}
campaign_participants {
uuid id PK
uuid guest_id FK
uuid group_id FK
text status
}
campaign_outreach_logs {
uuid id PK
text channel
text status
text rendered_content
}
campaign_conversions {
uuid id PK
uuid participant_id FK
text conversion_type
numeric revenue
}
8. API 端点清单
| 方法 | 端点 | 用途 | 前端调用 |
| GET | /api/research | 列表(分页+筛选) | useCampaigns |
| POST | /api/research | 创建 Campaign | useCreateCampaign |
| GET | /api/research/:id | 详情 | useCampaign |
| PUT | /api/research/:id | 更新(含 workflow_graph) | useUpdateCampaign |
| DELETE | /api/research/:id | 删除 | useDeleteCampaign |
| POST | /api/research/:id/duplicate | 克隆 | useDuplicateCampaign |
| POST | /api/research/:id/start | 启动 DAG | useStartCampaign |
| POST | /api/research/:id/pause | 暂停 | usePauseCampaign |
| POST | /api/research/:id/complete | 标记完成 | useCompleteCampaign |
| POST | /api/research/:id/cancel | 取消 | useCancelCampaign |
| GET | /api/research/:id/groups | 分组列表 | useCampaignGroups |
| GET | /api/research/:id/participants | 参与者(分页) | useParticipants |
| POST | /api/research/:id/allocate | 分配参与者 | 无前端入口 |
| GET | /api/research/:id/node-metrics | 节点实时指标 | useNodeMetrics |
| GET | /api/research/:id/stats | Campaign 统计 | useCampaignStats |
| GET | /api/research/:id/conversions/funnel | 转化漏斗 | useConversionFunnel |
| POST | /api/research/:id/reports/generate | 生成报告 | useGenerateReport |
| GET | /api/research/send-history | 跨项目发送历史 | useSendHistory |
| GET | /api/research/send-history/export | 发送历史 CSV | useExportSendHistory |