Files
DadaoTask/2.Design/PRODUCT-PLAN.md
T
Steven 0c53aa7062 feat(m0): 后端骨架 + Web/安卓前端骨架 + 设计文档
- backend: FastAPI 应用工厂、统一错误信封、JWT、短信登录、工作区/成员/邀请、幂等中间件
- alembic 0001: workspace/member/invitation/audit_log/app_user + RLS 策略 + 双角色 GRANT
- tests: 认证/工作区/租户隔离/幂等 四组用例(尚未在本机执行)
- web: React18+Vite 骨架(登录、工作区、邀请落地页)
- android: Compose 骨架(登录、工作区列表)
- 2.Design: PRODUCT-PLAN / ROADMAP-M0-M1 / NAMING / DEVELOPMENT
2026-10-28 02:38:53 +08:00

494 lines
28 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.
# DadaoTask 产品与技术方案
> 版本 **v1.0** · 2026-10-28 · 状态:已确认关键技术决策,可进入 M0
> v0.1 → v1.0 的变化:单租户改为 **SaaS 多租户**;删除离线同步;LLM 明确为 **OpenAI 兼容网关 + BYOK**;新增微信/飞书集成层与合规章节。
---
## 1. 一页纸结论
**定位**:一个"个人工作流"内核 + 按需外扩的协作 + 自动沉淀的知识库,三者由 LLM 贯通。**SaaS 形态,个人版免费起步,团队版按席位收费。**
**核心判断**:任务、工单、知识库不是三个并列模块,而是**同一件事的三个阶段**。拆成三个独立产品(滴答清单/禅道/Notion)的代价是重复输入、信息断层、知识库沦为第二个垃圾场。
```
想法 ──▶ 任务 ──(需要别人)──▶ 工单 ──(验收通过)──▶ 知识卡
│ │ │ │
└────────┴────────────────────┴─────────────────────┘
LLM 在每一段之间自动搬运、结构化、建立索引
```
**三条不可妥协**:
| 原则 | 含义 | 反面 |
|---|---|---|
| 一条流 | 任务升级工单不搬数据,时间线不断裂,知识卡可回链源头 | 工单从任务"复制"出去,两边各改各的 |
| 闭环沉淀 | 知识由已完成工作自动生成草稿,人只做确认/丢弃 | 让人下班后回忆今天学了什么再手敲 |
| LLM 只出草稿 | AI 写入一律 `suggestion` 待审,人点确认才落库 | AI 自动建 200 条任务,用户从此关闭该功能 |
**工期**:2 人约 14 周;第 10 周末可对外测(有任务+工单+知识+AI 的完整闭环)。
---
## 2. 已确认的决策(决策记录)
| # | 决策 | 选择 | 对方案的影响 |
|---|---|---|---|
| 1 | 商业模式 | **SaaS,未来对外卖** | 多租户、注册引导、套餐计量、计费钩子必须进 M0;数据隔离是最高优先级风险 |
| 2 | 离线能力 | **不需要,在线优先** | 删掉 outbox/冲突合并/CRDT,省约 4 周;安卓变成"在线客户端 + 只读缓存" |
| 3 | LLM 接入 | **标准 OpenAI 兼容接口**;系统自带 DeepSeek,用户可配 Ollama 等 | 引入 LLM 网关:provider 注册、密钥加密、能力探测、按租户路由与计量 |
| 4 | 后端 | **FastAPI** | 直接支持 SSE 流式、async、OpenAPI 自动出两端 SDK |
| 5 | 集成 | **微信 + 飞书** | 新增集成层(SSO / 通知 / Bot 三平面);需先明确"微信"= 企业微信还是公众号(见 §10.0) |
| 6 | 历史数据 | **无** | 知识库冷启动要靠"闭环沉淀 + 示例内容 + IM 聊天抽取",不能指望导入 |
---
## 3. 服务谁(SaaS 下的定位)
不是"更好的 Jira",也不是"更全的 Notion"。定位:
> **给"一个人干很多事、偶尔要拉人协作"的角色用。** 先服务单个专业用户(免费),当他的协作半径变大,自然升级到团队版。
| 阶段 | 用户 | 付费理由 |
|---|---|---|
| 免费 | 独立开发者、技术负责人、一人公司 | 任务 + 知识 + 少量 AI 额度 |
| Pro | 重度个人用户 | AI 额度、语义检索、画像、周报 |
| Team | 3–50 人小团队 | 席位、工单 SLA、权限、飞书/企微集成、审计 |
**与现有产品的边界**(写清楚是为了不被打):
| 产品 | 我们不做 | 我们做它做不了的 |
|---|---|---|
| 滴答 / Todoist | 极致 GTD、习惯打卡 | 任务能升级成有验收的工单并沉淀知识 |
| 禅道 / Jira | 大型敏捷、sprint、缺陷全家桶 | 轻到一个人就能用,不需要流程专家 |
| Notion / 语雀 | 块编辑器、Wiki 治理 | 知识从工作流里自动长出来 |
| 飞书 / 企微 | IM 本体 | 把聊天里的"事"沉淀成可追踪对象(我们做它的插件) |
---
## 4. 九个核心设计决策
### D1 · 统一实体 `item`,任务与工单同表
`item` 存共有字段,`type ∈ {task, ticket, note}`;工单专属字段放 1:1 的 `ticket_ext`。
**理由**:两者 80% 字段重合;分表会导致升级搬数据、时间线断裂、搜索 UNION、知识来源挂两套外键。**放弃**:全单表 + JSONB(SLA 计算要写 JSON 路径,约束退化)。
### D2 · 一切皆事件:`activity` 是唯一真相源
所有变更写 `activity`(who/what/changed_fields)。一张表同时喂四个消费者:时间线、审计、知识沉淀素材、LLM 上下文。
### D3 · 知识是工作流的产物
`knowledge_doc` 独立成表(有审核/版本/过期生命周期),但**必须能**带 `source_item_id`。入口三个:工单关闭自动生成、手动随手记、IM 消息抽取。
**理由**:知识库失败从来不是功能少,是没人写。
### D4 · 人的画像 `person_profile`
由 activity/item/knowledge 聚合,LLM 定期生成结构化事实(负责领域、近期事项、待跟进),带生成时间与"过期重算"。
只描述**工作事实**,不做性格或绩效评价 —— 产品伦理,也避免用户反感。
### D5 · LLM 写入必须过审:`suggestion` 表
AI 一切结构性写入(建任务、改状态、打标签、合并知识)都落 `suggestion(status=pending)`,人接受/修改/忽略后才 apply。
**理由**:AI 直接写库是所有 AI 产品翻车的头号原因;审批流同时把 AI 从"黑箱"变成"助理"。
### D6 · 多租户:共享库 + `workspace_id` + PostgreSQL RLS
**这是 v1.0 最重要的变更。** 每个租户表都带 `workspace_id`,靠 RLS 做第二道防线(应用层照常过滤,RLS 兜底漏写条件的 bug)。
**放弃**:schema-per-tenant(迁移要跑 N 次、连接池难管)、DB-per-tenant(成本高)。
**逃生口**:大客户私有化 = 同一套代码换 DSN,即"cell 模式",不为它改代码。
**铁律**:多租户必须第一天做对,事后补是灾难 —— 一条漏写 `workspace_id` 的查询就是一次数据泄露事故。
### D7 · 在线优先,不做离线同步
写操作走乐观 UI + 幂等键重试;安卓用 Room 仅作**只读缓存**(不是真相源)。
**理由**:已确认不需要离线。省掉 outbox、冲突合并、墓碑、单调游标这 4 周工作量。
### D8 · 检索只用 Postgres
`pg_trgm` + 中文分词(`zhparser` / `pg_bigm`)+ `pgvector` HNSW。
**多租户细节**:向量检索必须带 `workspace_id` 过滤;用 pgvector ≥ 0.8 的 iterative scan,或按租户建**部分索引**,避免"跨租户召回再过滤"把 top-k 稀释。
### D9 · LLM 网关:OpenAI 兼容 + BYOK + 平台兜底
一个 `LLMProvider` 抽象层兼容所有 OpenAI 协议实现(DeepSeek / OpenAI / Ollama / 通义 / 智谱 / 月之暗面)。
平台自带 DeepSeek 按套餐给额度;用户可自带 key 与 base_url。密钥加密存储、永不回显、永不进日志。
### D10 · 集成层三平面
SSO(扫码登录)/ 通知(消息卡片)/ Bot(在 IM 里建任务)。三者各自是适配器,不耦合进领域逻辑;出站通知落库后由异步分发,IM 挂了不阻塞主流程。
---
## 5. 领域模型
### 5.1 实体关系
```mermaid
erDiagram
WORKSPACE ||--o{ MEMBER : has
USER ||--o{ MEMBER : joins
WORKSPACE ||--o| SUBSCRIPTION : "billed by"
WORKSPACE ||--o{ LLM_PROVIDER : configures
WORKSPACE ||--o{ INTEGRATION : connects
USER ||--o{ ITEM : owns
ITEM ||--o| TICKET_EXT : extends
ITEM ||--o{ ITEM_LINK : links
ITEM ||--o{ COMMENT : has
ITEM ||--o{ ACTIVITY : logs
ITEM ||--o| KNOWLEDGE_DOC : "distills to"
KNOWLEDGE_DOC ||--o{ CHUNK : "split into"
USER ||--o| PERSON_PROFILE : "profiled by"
SUGGESTION }o--|| ITEM : "applies to"
THREAD ||--o{ MESSAGE : contains
WORKSPACE ||--o{ USAGE_EVENT : meters
```
### 5.2 表清单(22 张;MVP 需 14 张)
| 表 | 作用 | 关键字段 | MVP |
|---|---|---|---|
| `workspace` | 租户 | id, name, slug, plan, status | ✅ |
| `user` | 账号(跨租户唯一) | id, phone, email, name, avatar, password_hash | ✅ |
| `member` | 成员与角色 | ws_id, user_id, role, status, invited_by | ✅ |
| `invitation` | 邀请 | ws_id, token, role, expires_at | ✅ |
| `item` | 统一实体 | id, ws_id, type, title, body_md, status, priority, owner_id, due_at, done_at, visibility, ext, version, deleted_at | ✅ |
| `item_assignee` | 多负责人 | item_id, user_id, role | ✅ |
| `ticket_ext` | 工单专属 | item_id, requester_id, sla_due_at, accepted_at, resolved_at, verified_at, verify_result | ✅ |
| `item_link` | 关系 | from_id, to_id, kind | ✅ |
| `comment` | 评论/内部备注 | item_id, author_id, body_md, internal | ✅ |
| `activity` | 事件流 | item_id, ws_id, actor_id, verb, changed_fields, at | ✅ |
| `tag` / `item_tag` | 标签 | — | ✅ |
| `knowledge_doc` | 知识卡 | ws_id, title, summary, body_md, source_item_id, status, review_at, version | ✅ |
| `suggestion` | AI 待审草稿 | ws_id, kind, payload, target_id, status, model, confidence | ✅ |
| `subscription` / `usage_event` | 套餐与计量 | plan, seats, period, kind, qty | ✅ |
| `chunk` | 切片 + vector(1024) | doc_id, seq, text, tsvector, embedding | V1 |
| `attachment` | 附件 | owner_id, oss_key, mime, size | V1 |
| `llm_provider` | 租户 LLM 配置 | ws_id, kind, base_url, model, key_enc, status | V1 |
| `integration` | 集成配置 | ws_id, provider, plane, config_enc, status | V1 |
| `thread` / `message` | AI 会话 | anchor_item_id, role, content, citations, tokens | V1 |
| `person_profile` | 人物画像 | user_id, ws_id, summary_md, facts, generated_at | V2 |
| `notification` | 出站消息队列 | ws_id, channel, payload, status, retry | V1 |
| `audit_log` | 管理操作审计 | ws_id, actor, action, target, ip | V1 |
### 5.3 状态机
**任务**:`inbox → todo → doing → done`,旁挂 `blocked`、`canceled`
**工单**:`open → accepted → in_progress → pending_verify → done`,旁挂 `reassigned`、`canceled`
**知识**:`draft → in_review → published → archived`
> **一条流的关键机关**:任务升级为工单后自动进 `blocked`;工单验收通过自动回流 `doing/done`。
> 用户不需要在两个地方各改一次状态 —— 比任何 AI 功能都更能说明"这是一个产品"。
---
## 6. 功能地图
### 6.1 MVP(第 1–10 周,目标:自己天天用 + 可对外测)
**任务**:一句话快速录入(全局快捷键/分享面板)、收件箱/今天/本周/全部四视图、优先级/截止/标签/子任务、看板拖拽、移动端列表。
**工单**:任务一键升级(选人+期望时间+说明)、我发起的/待我处理/我参与的、状态流转、评论与内部备注、验收通过或打回、关闭时触发沉淀。
**知识**:手动新建、从工单生成四段式草稿(问题/根因/处理/验证)、全文搜索、来源回链。
**AI 三场景**:NL → 结构化任务/工单;工单关闭 → 知识草稿;基于知识库问答(必须带引用,无来源就说不知道)。
**SaaS 必备**:手机号验证码注册、创建团队、邀请成员、四角色权限、免费额度与用量页、数据导出、管理后台(看租户/用量/封禁)。
**基础**:站内通知 + 邮件;安卓端(在线)。
### 6.2 V1(第 11–16 周)
- 语义检索(pgvector)+ 混合排序
- 对话式 Agent(对话里查任务/改状态,仍走审批)
- 人物画像、自动周报
- 飞书/企微:扫码登录、消息卡片通知、Bot 建任务
- 附件 + 图片 OCR 入知识
- 套餐升级与在线支付(支付宝/微信)
- 审计日志、知识卡过期提醒
### 6.3 V2
- 知识图谱与可视化
- 相似去重与自动归档(>0.92 提示合并)
- 自定义工单字段与流程模板
- 开放 API / Webhook
- 小程序端(承接微信生态的轻量入口)
- 安卓桌面小组件
### 6.4 明确不做
甘特图与资源负载 · BPMN 审批引擎 · 实时协同编辑 · 自建 IM · 离线模式 · 细粒度字段级权限 · 对外开放插件市场(至少两年内)
---
## 7. LLM 网关设计(v1.0 重写)
### 7.1 Provider 模型
```python
class LLMProvider(BaseModel):
ws_id: str | None # None = 平台内置
kind: Literal["openai_compatible"]
base_url: str # https://api.deepseek.com/v1 ; http://ollama:11434/v1
model: str # deepseek-chat / qwen2.5:14b ...
embedding_model: str | None
key_enc: bytes | None # AES-256-GCM 密文,永不回显
status: Literal["ok", "invalid", "unreachable"]
capabilities: dict # 探测结果:stream / tools / json_mode / vision
```
- **预置模板**:DeepSeek(平台默认,给额度)、OpenAI、Ollama(本地/内网)、通义千问、智谱、月之暗面、硅基流动 —— 全部是 OpenAI 兼容协议,只是 `base_url` 与模型名不同。
- **能力探测**:保存时调一次 `GET /models` + 一次 8 token 的 chat,标记 `capabilities`;流式/tools 不支持就自动降级(例如不支持的走两段式:先出 JSON 再渲染)。
- **密钥安全**:AES-256-GCM,主密钥来自环境变量或 KMS;前端只看到 `sk-****abcd`;日志、追踪、错误上报统一脱敏中间件。
- **分层覆盖**:租户默认 → 功能级覆盖(打标签用便宜模型,问答用强模型)→ 全局兜底(平台 key)。
- **网络可达性**:Ollama 场景下用户填的是自己内网地址,SaaS 后端根本连不上 —— **必须支持"客户端代理调用"模式**:浏览器/安卓直接请求用户的 Ollama,结果回传后端只落库不入模型链路。这是私有化用户最常见的坑,M4 就要处理。
### 7.2 六个能力
| 能力 | 输入 | 输出 | 边界 |
|---|---|---|---|
| A 结构化录入 | 一句话 | item 草稿 | 只建议不建 |
| B 闭环沉淀 | 工单 activity 流 | 四段式知识卡草稿 | 必须回链 source_item_id |
| C 自动索引 | 知识正文 | 标签/实体/相似卡 | 标签库受控,不许乱造 |
| D 问答 | 问题 + 检索 | 带引用回答 | 无来源就说不知道 |
| E 人物画像 | 某人 activity | 5–8 条事实 | 只写工作事实 |
| F 主动提醒 | 全局状态 | 风险项 | 每日最多 1 条 |
### 7.3 上下文组装
```
[系统] 角色 + 输出格式约束 + 工作区一句话摘要
[对象] 对话对象画像(≤5 条,各 1 行)
[事实] 相关 item 摘要(top 8,各 1 行)
[证据] 相关 chunk(top 12,各 ≤200 字,带 [doc#seq] 来源标记)
[历史] 本会话最近 6 轮
[输入] 用户当前话语
```
超预算裁剪顺序:chunk 细节 → item 数量 → 画像条数 → **绝不砍系统规则与来源标记**。
预算:输入 ≤ 8k tokens,输出 ≤ 1.5k。
### 7.4 计量、限流、降级
- 每次调用写 `usage_event`(tokens_in/out, model, latency, ws_id)→ 套餐月度汇总 → 超限只降级不硬断(降级为关键词检索 + 模板回答)。
- 租户级 QPS 与并发上限;平台 key 按租户配额,BYOK 只做滥用防护。
- 缓存:同问题 + 同检索结果哈希 → 命中即回,省 30%+。
- **隐私**:prompt/response 默认不落库;调试开关 7 天自动清除,且界面上明确告知。
- **评测**:20 条黄金用例(含 5 条"应当回答不知道"),改 prompt 必跑。
---
## 8. 多租户、权限与计费(SaaS 核心)
### 8.1 隔离
- **应用层**:所有仓储方法强制要求 `ws_id`(类型层面让它成为必填参数,而不是可选)。
- **数据库层**:`SET LOCAL app.ws_id = $1` 于每个请求事务开头,RLS 策略 `USING (workspace_id = current_setting('app.ws_id')::uuid)`。
- **测试层**:每个资源一套「跨租户越权测试」,断言必须 0 行 / 403。这条测试进 CI,红了不许合并。
- **对象存储**:key 前缀 `ws/{ws_id}/...`,签名 URL 绑定租户。
### 8.2 角色(4 个,不做矩阵)
`owner`(含计费与删除租户)/ `admin`(管人与配置)/ `member`(读写团队内容)/ `guest`(仅被指派的工单)。
实现:`PERMISSIONS` 集中式守卫表(资源 → 动作 → 最低角色),绝不散落 `if`。PosSync 的 `PERMISSION_GUARDS` 写法可直接搬。
### 8.3 套餐与计量
| 套餐 | 席位 | AI 额度 | 关键功能 |
|---|---|---|---|
| Free | 1 | 少量/月 | 任务、工单、知识、基础搜索 |
| Pro | 1 | 中 | 语义检索、画像、周报、更大附件 |
| Team | 3+ | 按席位 | SLA、集成、审计、SLA 提醒、优先支持 |
计量只做三样:AI tokens、存储、席位数。**MVP 不做在线支付**(先手工开通/邀请码),避免把 3 周花在支付对账上;V1 再接支付宝/微信。
### 8.4 注册与引导
手机号验证码 → 建个人工作区 → 3 条示例任务(可一键清空)→ "要不要接入 AI?"(默认平台额度)→ 完成。
**关键指标**:从注册到"完成第一个任务"的时间,目标 < 90 秒。
---
## 9. 技术架构
```mermaid
flowchart TB
subgraph C[客户端]
W["PC Web · React 18 + Vite + Tailwind"]
A["Android · Kotlin + Compose"]
B["飞书 / 企微 Bot"]
end
subgraph G[接入]
N["Nginx · TLS · 限流 · 静态资源"]
end
subgraph S[应用层 · FastAPI]
API["REST + SSE"]
AUTH["认证 · JWT · OAuth"]
SVC["领域服务 item / ticket / knowledge"]
AI["AI 编排 · suggestion 审批 · 上下文组装"]
JOB["异步任务 · arq + Redis"]
INT["集成适配器 · SSO / 通知 / Bot"]
end
subgraph D[数据层]
PG["PostgreSQL 16 · RLS · pgvector"]
R["Redis · 缓存 / 队列 / 限流"]
OSS["MinIO/S3 · 附件"]
end
AI --> GW["LLM 网关"]
GW --> P1["平台 DeepSeek"]
GW --> P2["用户 BYOK"]
GW --> P3["用户 Ollama · 客户端代理"]
W --> N --> API
A --> N
B --> N
API --> SVC --> PG
API --> AI
JOB --> SVC
INT --> B
```
| 层 | 选择 | 理由 |
|---|---|---|
| 后端 | FastAPI + Pydantic v2 | SSE 流式、OpenAPI 出 SDK、async |
| 数据库 | PostgreSQL 16 + pgvector | 关系/全文/向量一库;RLS 直接可用 |
| 队列 | Redis + arq | 轻量 asyncio;Celery 更重 |
| 前端 | React 18 + Vite + Tailwind | 沿用 PosSync 栈,熟 |
| 安卓 | Kotlin + Compose | 沿用已有经验;Flutter 收益不明显 |
| SDK | OpenAPI → TS/Kotlin 生成 | 手写两端必漂移 |
| 部署 | Docker Compose 单机起步 | 早期不上 K8s;按 cell 预留水平扩展 |
| 分发 | 官网 APK + 华为/小米/OPPO/vivo/应用宝 | 国内不上 Google Play |
**代码结构**
```
DadaoTask/
├─ 2.Design/ 设计文档、数据库、部署、事故记录
├─ backend/
│ ├─ app/api/ items, tickets, knowledge, ai, auth, admin, sync(无)
│ ├─ app/domain/ 状态机、权限、SLA、租户上下文
│ ├─ app/infra/ db, redis, oss, llm, integration
│ ├─ app/ai/ context builder, prompts, retriever, suggestion
│ └─ app/jobs/ embedding、周报、画像、通知分发
├─ web/ React
├─ android/ Kotlin Compose
└─ deploy/ compose、nginx、备份
```
**沿用 PosSync 的铁律**(写进 AGENTS.md):改完必须提交并 push;生产不得领先仓库;前端产物只能由仓库 HEAD 构建,部署后校验 build 标记。
---
## 10. 微信与飞书集成
### 10.0 纠正:个人微信**有**官方 Bot 通道
v1.0 初稿这里写错了(原判断"个人微信没有官方机器人接口")。核实后的事实:腾讯已开放 **微信 ClawBot / iLink Bot 协议**(`https://ilinkai.weixin.qq.com`),DSH 的 IM 插件 `@michengai/dsh-im-connect` 微信渠道就是基于它实现,源码里明确写着"腾讯官方产品,非逆向方案"。
已核实的技术事实(来源:`@michengai/dsh-im-connect` 的 `src/channels/weixin.ts`、`src/channels/qr/weixin.ts`、README 支持矩阵):
| 事实 | 内容 | 对产品的影响 |
|---|---|---|
| 登录方式 | `POST /ilink/bot/get_bot_qrcode?bot_type=3` 取二维码 → 轮询 `/ilink/bot/get_qrcode_status` → 拿到 `bot_token` | 用户扫码即绑定,不用填任何密钥,接入摩擦极低 |
| 收消息 | 长轮询(poller),必须持久化 `syncBuf` 游标防丢防重 | 一个绑定 = 一条常驻长轮询连接,这是架构约束 |
| 回消息 | `POST /ilink/bot/sendmessage`,按 `context_token` 回复 | — |
| 媒体 | 图片/语音/文件/视频,CDN AES-128-ECB 加密,单文件上限 50 MB | 可以语音记任务、直接把文件甩进知识库 |
| 会话范围 | **仅单聊**,不支持群 | 团队协作的消息面仍得靠飞书/企微 |
| 账号模型 | **一个微信号一个 poller**,官方建议用专用小号 | 见 10.3 的架构分歧,这是最关键的决策点 |
| 合规 | 官方 iLink,插件声明 "no reverse-engineered personal protocol";使用即同意《微信 ClawBot 功能使用条款》 | 不能用 hook/逆向;条款原文的商用边界需甲方自查(我无法联网检索条款全文) |
### 10.0.1 其他微信通道(仍然有效,按需用)
| 通道 | 能做什么 | 建议 |
|---|---|---|
| **企业微信** | 应用消息、审批、扫码登录、群机器人 | ✅ 团队协作首选 |
| **公众号/服务号** | 模板消息(提醒推送)、扫码关注绑定、H5 入口 | ✅ 个人用户的推送通道 |
| **小程序** | 轻量移动入口,可替代部分安卓场景 | 后续,若安卓分发成本过高再做 |
| **个人微信 iLink** | 扫码绑定、单聊收发、语音/文件 | ✅ 个人用户入口(见 10.3) |
### 10.1 三个平面
| 平面 | 飞书 | 企微 | MVP |
|---|---|---|---|
| SSO | 网页扫码/免登 | 网页授权扫码 | V1 |
| 通知 | 消息卡片(可带"接受/完成"按钮) | 应用消息 / 群机器人 | V1 |
| Bot | 事件订阅 → 建任务/查工单 | 群机器人 → 同上 | V2 |
### 10.2 设计要点
- `integration` 表存 provider + plane + 加密配置,每个租户可挂多个。
- 出站走 `notification` 队列 + 重试 + 死信,IM 故障不阻塞业务。
- 入站必须验签(飞书 encrypt key / 企微 token+sha1),并且**幂等**(同一 event_id 只处理一次)。
- Bot 收到的自然语言 → 走 §7 能力 A → 返回**卡片待确认**,用户点确认才建。避免"群里一句话建了十条任务"。
- 双向价值:群里的讨论也能反向抽成知识卡(V2 的差异化点,别人没有)。
### 10.3 个人微信在 SaaS 里的三种接法(要你拍板)
约束源自 10.0 表格最后两行:**单聊 only + 一个微信号一个 poller + 建议专用小号**。这直接推出三种互斥的架构:
| 接法 | 怎么做 | 优点 | 代价 |
|---|---|---|---|
| **A 用户自带微信号** | 每个用户自己扫码,把一个小号变成"我的助理",主号私聊它记任务 | 风险分散到用户;我们不用养号;天然多租户隔离 | 用户得有第二个微信号 → 对普通用户是转化率杀手,只适合重度用户 |
| **B 平台统一号** | 我们运营一个微信助理号,所有用户加它,按白名单识别身份 | 零门槛,注册即用,是真正能拉新的入口 | 一条 poller 承载全平台会话(要压测);好友数上限;**一个号被封 = 全员失联**;条款是否允许商用未知 |
| **C 本地 bridge** | 用户在自己电脑上跑轻量转发程序(DSH 插件就是这种形态),微信连接留在用户侧,云端只收结构化数据 | 最稳、最合规、服务器最省 | 要装东西;电脑关机就断;不适合纯手机用户 |
**我的建议**:MVP 走 **A**,但设计成"高级开关、默认关闭"——先把数据模型和 `integration` 表做对,接不接微信都不影响主流程。B 等条款核实 + 压测数据出来再评估,不要现在押注。C 等桌面端有了再说。
⚠️ **但选型前必须做 0.5 天 spike**,验证三件必须实测才知道的事(拍脑袋会返工):
1. **谁是 bot** —— 扫码的那个微信号自己变成 bot,还是被授权成某个服务实体?(决定"用户要不要备小号")
2. **能否多用户共用** —— 不同微信号私聊同一个 bot,是否都能收到、且会话互不串?(决定 B 是否可行)
3. **风控边界** —— 连续/并发发消息的限流阈值、长轮询断线重连行为、`bot_token` 有效期与失效时机。
结论写进 `2.Design/SPIKE-WECHAT-ILINK.md`,再回来定 10.3 的最终选型。
---
## 11. 安全与合规(SaaS 必答)
| 事项 | 现在要做 | 说明 |
|---|---|---|
| 个人信息保护法 | 隐私政策、最小采集、导出/删除接口、注销 | 卖给企业客户时会被问到 |
| 数据出境 | 默认用境内模型(DeepSeek 等);用境外模型时明确告知并取得同意 | 用户 BYOK 到 OpenAI 属于用户自选,需二次确认弹窗 |
| ICP 备案 | 域名必须备案;涉及在线交易可能需 EDI | 提前办,别卡上线 |
| 等保 | 面向企业客户大概率要求等保二级 | 初期至少做到:日志留存 6 个月、堡垒机、备份、漏洞扫描 |
| 密钥 | 加密入库、不进日志、支持轮换 | 一条脱敏中间件统一处理 |
| 越权 | RLS + 跨租户测试进 CI | 最高优先级 |
| 备份 | 每日全量 + WAL 归档,恢复演练每季度 | 复用现有备份脚本 |
---
## 12. 里程碑(2 人)
| 阶段 | 周 | 交付 | 验收 |
|---|---|---|---|
| M0 骨架 | 1–2 | 多租户认证、RLS、迁移、CI、Compose、部署 | 两个租户互不可见(有测试证明) |
| M1 任务 | 3–4 | 四视图、看板、标签、搜索 | 自己连续 3 天全量使用不别扭 |
| M2 工单 | 5–6 | 升级、状态机、评论、验收、blocked 联动 | 两人真实跑通一个工单闭环 |
| M3 知识 | 7–8 | 知识卡、来源回链、工单草稿、全文搜索 | 关一个工单能顺手产出一张有用卡 |
| M4 AI | 9–10 | 三场景、suggestion 审批、LLM 网关、BYOK | 黄金用例 ≥ 80%;换模型不改业务代码 |
| **对外测** | **10** | 邀请制内测,10–20 个真实用户 | 周留存与"第一个任务完成率"有数 |
| M5 安卓 | 11–12 | Compose 主界面、推送、在线缓存 | 弱网可用、崩溃率 < 1% |
| M6 集成 | 13 | 飞书/企微登录与通知卡片 | 群里能收到并一键处理工单 |
| M7 SaaS | 14 | 套餐计量、用量页、管理后台、导出 | 能手工开单、看用量、封禁滥用 |
| M8 打磨 | 15 | 性能、合规材料、文档、商店上架 | 可公开注册 |
> 提前到第 10 周对外测,是为了让真实用户决定 M5 之后做什么,而不是我猜。
---
## 13. 风险与对策
| 风险 | 概率 | 影响 | 对策 |
|---|---|---|---|
| 多租户越权泄露 | 中 | 致命 | 仓储层强制 ws_id + RLS + 跨租户测试进 CI |
| 范围蔓延(三产品叠加) | 高 | 致命 | §6.4 不做清单当合同;加功能必砍功能 |
| 免费用户刷 AI 额度 | 高 | 中 | 硬配额 + 每日衰减 + 异常检测(同 IP 多账号) |
| LLM 幻觉 | 中 | 高 | 强制引用、无来源不答、黄金用例回归 |
| Ollama 内网不可达 | 高 | 中 | 客户端代理调用模式(§7.1) |
| 知识冷启动无人写 | 中 | 高 | 闭环沉淀 + 示例内容 + IM 抽取;首批种子由我们代写 20 张 |
| 一个人维护三端 | 中 | 中 | OpenAPI 生成 SDK;安卓只做读写与推送 |
| 三个月后自己不用了 | 中 | 致命 | M1 起强制 dogfooding,每周记一条"用得不爽"并优先修 |
| 备案/等保卡上线 | 中 | 高 | 现在就去办 ICP;等保按客户要求提前准备 |
---
## 14. 下一步
1. **M0 骨架**:仓库结构、Compose(PG16/Redis/MinIO)、多租户认证、RLS、迁移、CI、部署脚本。
2. **`2.Design/DATABASE.md`**:22 张表落成 DDL + RLS 策略 + 索引。
3. **`2.Design/API.md`** + OpenAPI 导出脚本:三端并行开发的契约。
4. 确认三件小事:产品名/域名、端口分配(避开 PosSync 的 5888/5176)、Gitea 仓库创建。
> 顺序建议:**先让"任务"这条腿站住,再谈 AI**。反着做的项目,两周后演示很炫,用户却没有真实数据可用。