2026-10-12 16:10:18 +08:00
|
|
|
|
# CLAUDE.md — all-kefu 工作约定
|
|
|
|
|
|
|
|
|
|
|
|
## 项目定位
|
|
|
|
|
|
|
|
|
|
|
|
本项目 all-kefu 是**统一多租户 AI 客服平台**:一套引擎 + 租户配置包,消除"每客户复制一套代码"的旧模式。项目由 ai-kefu / ai-kefu-jc 两个复制分叉项目合并而来,**jc 为主线**(代码以 ai-kefu-jc 为基础迁移,再做租户化改造)。
|
|
|
|
|
|
|
|
|
|
|
|
## 硬约束
|
|
|
|
|
|
|
|
|
|
|
|
- **uvicorn 必须以 `--workers 1` 运行**。内存中的 token 缓存、限流状态、配置缓存、每租户 engine 缓存均假设单进程,多 worker 会导致状态割裂。任何启动脚本/部署配置不得改动此项。
|
|
|
|
|
|
- **Windows 环境**:跑 python / pytest 前设置 `PYTHONUTF8=1`(GBK 控制台遇 emoji / 中文 payload 会直接崩,血泪教训)。
|
2026-10-13 17:23:02 +08:00
|
|
|
|
- **临时文件用完即删(用户要求)**:冒烟截图、调试日志、造数脚本、临时库/platform.db/tenants 运行产物、probe 脚本、`.playwright-mcp/` 产物等一切工作过程文件,任务收尾必须全部删除,以 `git status` 干净 + 仓库目录无残留为验收标准。不许堆积无用文件。
|
2026-10-12 16:10:18 +08:00
|
|
|
|
|
2026-10-12 20:47:01 +08:00
|
|
|
|
## 本地开发访问方式
|
|
|
|
|
|
|
|
|
|
|
|
- 路由按 Host 解析租户:`/` 与 `/static` 也需要已知租户域名(未知 Host 一律 404,唯一豁免 `/health`,供部署健康检查 curl 用)。
|
|
|
|
|
|
- 本地访问二选一:改 hosts 把 `chat.agent.hnxinyouhe.com` / `chat2.agent.hnxinyouhe.com` 指到 `127.0.0.1`;或 curl 显式带 `-H "Host: chat2.agent.hnxinyouhe.com"`。
|
|
|
|
|
|
- uvicorn 起在 8018:`PYTHONUTF8=1 .venv/Scripts/python -m uvicorn app.main:app --port 8018 --workers 1`。
|
|
|
|
|
|
|
2026-10-12 16:10:18 +08:00
|
|
|
|
## 开发流程
|
|
|
|
|
|
|
|
|
|
|
|
- 改动 `seed_profiles` 后必须跑全量 pytest(出厂配置影响面大)。
|
2026-10-12 23:51:26 +08:00
|
|
|
|
- 全量回归命令:`PYTHONUTF8=1 .venv/Scripts/python -m pytest -q`(根目录与 tests/ 子目录均可跑,pytest.ini 已锚定根路径)。
|
|
|
|
|
|
|
|
|
|
|
|
## Phase 1 完成状态(2026-10-12 收尾核对)
|
|
|
|
|
|
|
2026-10-13 12:21:10 +08:00
|
|
|
|
**测试基线:459 passed**(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 为 starlette TestClient 对 httpx 的弃用提示,无害)。此为 Phase 1 时点数字;现行回归基线以 Phase 2 完成状态段为准(566)。
|
2026-10-12 23:51:26 +08:00
|
|
|
|
|
|
|
|
|
|
**零同步 IO 审计**(`grep -rn "requests\.\|urllib\|time\.sleep" app/`):
|
|
|
|
|
|
|
|
|
|
|
|
- 命中数 **0**——app/ 内无 requests 调用、无 urllib、无 time.sleep。
|
|
|
|
|
|
- 全库唯一 HTTP 出站点为 `app/engine/services/lead_push_service.py` `_do_post`,使用 `httpx.AsyncClient`(async with 上下文),异步路径成立。
|
|
|
|
|
|
- `app/engine/providers/glm.py` 的同步 token 生成与兜底同步调用分别经 `loop.run_in_executor` / `asyncio.to_thread` 下卸,事件循环不被阻塞。
|
|
|
|
|
|
- `app/engine/services/rate_limit.py` 的 `import time` 仅用 `time.time()` 读时钟(纯 CPU,非 IO);app/ 源码内无 `open()` 文件读取。
|
|
|
|
|
|
|
|
|
|
|
|
**spec §6.2 合并清单核对**(逐项,全部在位):
|
|
|
|
|
|
|
|
|
|
|
|
- 取自 jc:cc_completion 完整判定树+状态机+extractor `cc_reply`+e164 出站渲染(`app/engine/services/cc_completion.py`,conversation.py 双门控消费);多语言墙(€/£、西/葡/俄/阿词表在 `seed_profiles/jiancai/wall.json`、ES 迂回池 boundary_filter+wall.json、建材防误杀白名单为 pattern 级 comment+后顾断言 DN800/SDR11/PN16/"euro pallets" 排除);dual-contact 分步双要(cc_settled 串行门控邮箱轻补)+`_email_asked`;CC 强制墙(conversation.py cc_wall 裁决段)+零号码墙回显豁免收窄(boundary_filter.py:旧豁免移除,改 allow_number 回显确认轮白名单+防自锁线);XFF 服务端优先(chat_rest `_server_ip`/chat.py 握手期提取,均取第一段)、阿语数字归一(contact_pipeline 捕获源头 translate)、`resolve_region_code`(ip_service.py)。
|
|
|
|
|
|
- 上游吸收:input-placeholder 可配置化(品牌配置组,public.py 下发,widget/chat data-placeholder 管道);7 个测试文件(test_channel_hygiene / test_chat_rest_ip / test_lead_push_cc / test_cc_integration / test_ip_service_region / test_prompt_static / test_placeholder_static,commit d1aed8e)。
|
|
|
|
|
|
- 三不带:blog.html(static/ 无此文件)、Redis(app+scripts 零命中)、旧版 whatsapp-cc-completion(零命中,jc 版 cc_completion.py 取代)。
|
|
|
|
|
|
|
2026-10-13 00:18:54 +08:00
|
|
|
|
**当前能力一览**:多租户引擎(TenantRuntime 隔离边界,engine 不感知 platform);站点模型(tenant_config.Site,一租户多站点覆盖);词表/提示词/品牌全配置化(seed_profiles 出厂包 + 文件级 registry_reload 热加载,Phase 2 升级为 platform.db);Host 路由(host_middleware,未知 Host 404);WS 防串库(租户化 client key + 会话站点标);WS 每 IP 连接上限(conn_limiter,租户级 ws_conn_limit 配置)。
|
2026-10-12 23:51:26 +08:00
|
|
|
|
|
2026-10-13 12:21:10 +08:00
|
|
|
|
## Phase 2 完成状态(2026-10-13 收尾核对)
|
2026-10-12 23:51:26 +08:00
|
|
|
|
|
2026-10-13 12:21:10 +08:00
|
|
|
|
**测试基线:566 passed**(根目录与 tests/ 子目录各跑一遍,数字一致;唯一 warning 同 Phase 1——starlette TestClient 对 httpx 的弃用提示,无害)。后续任务回归以此为准。
|
2026-10-12 23:51:26 +08:00
|
|
|
|
|
2026-10-13 12:21:10 +08:00
|
|
|
|
**零同步 IO 审计复查**(grep requests / urllib / time.sleep / open 于 app/):命中数 **0**——无 requests/urllib 导入、无 time.sleep、无 open() 调用;`import time` 仅 platform/auth.py 与 engine/services/rate_limit.py 两处,均只读 time.time() 时钟。scrypt 登录路径 CPU 开销注记在案(auth.py 模块 docstring:单次 ~50ms,只在登录/改密路径,不在每请求热路径——依赖只查内存 token 表)。
|
2026-10-12 23:51:26 +08:00
|
|
|
|
|
2026-10-13 12:21:10 +08:00
|
|
|
|
**安全自查**(读码 + 定向测试 28 条全绿:test_auth / test_admin_mounting / test_avatar_upload / test_host_routing):
|
|
|
|
|
|
|
|
|
|
|
|
- **密码/token 不进日志**:app/platform/ 全层**零 logger 调用**(干净由构造保证);main.py 启动日志仅静态串 + slug/db 路径;引擎层(Phase 1 既有)日志参数为会话内容摘录/session_id/notify_emails,无凭据字段。
|
2026-10-14 13:45:14 +08:00
|
|
|
|
- **gitignore 覆盖复核**(git check-ignore 实证全命中):`tenants/*.db{,-wal,-shm}`(租户库实际落 `tenants/<slug>.db`)、`data/`(含 `data/uploads/` 上传卷)。platform.db 位置注记(Phase 4 修正):**现落 `data/platform.db`,由 `data/` 规则覆盖**(main.py PLATFORM_DB 与 compose bind mount 钉死,P4T1 审查修复);.gitignore 根级 `platform.db*` 三条为历史留存,无害。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
- **登录限流在 /api/platform 豁免路径仍生效**:host_middleware 对 `/api/platform/` 前缀的豁免只跳过**租户解析**,认证守护在路由依赖;登录限流先于密码验证(锁定期内正确密码也 429)。测试钉位:test_auth.py::test_login_lockout_after_5_fails + test_admin_mounting.py::test_platform_api_bypasses_tenant_resolution / test_platform_prefix_not_open / test_prefix_match_precise。
|
|
|
|
|
|
- **上传目录不可执行**:上传只认魔数(png/jpg/webp)+ uuid 重命名落盘——.py 文件无进无出;avatar 下发(public.py)取 PurePosixPath.name 并显式拒绝 `/` `\` 与目录点(防 Windows 反斜杠穿透),FileResponse content-type 由扩展名映射(只可能 image/*)。
|
|
|
|
|
|
|
|
|
|
|
|
**平台层能力一览**(Phase 2 交付;engine 仍不感知 platform):
|
|
|
|
|
|
|
|
|
|
|
|
- **认证**:scrypt 版本化哈希(stdlib 零新依赖,偏离 spec bcrypt/argon2 有注记)、内存 token 8h TTL、登录限流(username+IP 滑动窗 5 次/15 分钟)、改密(must_change_password 引导)、角色两级(超管 / 租户管理员)+ 409 语义。
|
|
|
|
|
|
- **租户管理**:创建向导(消费出厂档案,blank=jc 墙提示词)、站点与域名子资源 CRUD、停用(聊天面 503、后台仍可见)、slug 不可变。
|
|
|
|
|
|
- **配置版本管线**:分组校验 → 整份版本快照(MAX+1)→ 回滚 / 重置出厂 / 试金石(wall/test)→ registry_reload 热生效。
|
|
|
|
|
|
- **上传**:头像 multipart(魔数校验 / 2MB 上限 / uuid 重命名),写配置版本 + 三级解析链下发(uploads → seed 资产 → 静态兜底)。
|
|
|
|
|
|
- **数据视图**:会话列表(筛选/分页)/ 详情 / 删除 + 推送日志——租户 scope 强制(跨租户 404)。
|
|
|
|
|
|
- **统计**:卡片 / 漏斗 / 趋势 / 分布 / 推送健康——逐租户库查询 + 应用层合并(**禁 ATTACH**,spec §7.6);指标口径表定死在 stats.py docstring。
|
|
|
|
|
|
- **DB 源生产装配**:TENANT_SOURCE=db 默认(DbTenantSource),FileTenantSource 保留为测试/档案/冷备形态;bootstrap_platform.py 一键引导。
|
|
|
|
|
|
|
|
|
|
|
|
**admin API 速览**(全部 `/api/platform` 前缀;Host 中间件豁免该前缀的租户解析,认证由路由依赖守护——require_super 超管 / _tenant_guard 租户管理员):
|
|
|
|
|
|
|
|
|
|
|
|
- 认证:`POST /auth/login`、`POST /auth/logout`、`POST /auth/change-password`
|
|
|
|
|
|
- 租户:`GET|POST /tenants`、`GET|PATCH /tenants/{slug}`、`POST /tenants/{slug}/sites`、`PATCH /tenants/{slug}/sites/{site_slug}`、`POST|DELETE /tenants/{slug}/sites/{site_slug}/domains[/{domain}]`
|
|
|
|
|
|
- 配置:`GET /tenants/{slug}/config`、`PUT /tenants/{slug}/config/{group}`、`POST /tenants/{slug}/config/{group}/reset`、`GET /tenants/{slug}/config/versions[/{version}]`、`POST /tenants/{slug}/config/rollback`、`POST /tenants/{slug}/wall/test`
|
|
|
|
|
|
- 上传:`POST /tenants/{slug}/brand/avatar`
|
|
|
|
|
|
- 数据:`GET /tenants/{slug}/conversations[/{conv_id}]`、`DELETE /tenants/{slug}/conversations/{conv_id}`、`GET /tenants/{slug}/push-logs`
|
|
|
|
|
|
- 统计:`GET /stats`(超管,`?tenant=` 单租户下钻)、`GET /tenants/{slug}/stats`
|
|
|
|
|
|
- 平台设置:`GET|PUT /settings`
|
|
|
|
|
|
|
|
|
|
|
|
## Phase 2 沉淀的取舍与 Phase 3 清理单
|
|
|
|
|
|
|
|
|
|
|
|
**实现取舍(API 冻结 / Phase 3 前端对接前须知)**:
|
|
|
|
|
|
|
|
|
|
|
|
1. **PUT config 整组替换语义**——前端必须整组读-改-写(Task 6;做部分更新需先 GET 全组再改再 PUT)。
|
2026-10-13 12:40:21 +08:00
|
|
|
|
2. **rollback 仍绕过 prefix 跨租户查重**(终审 I-3 已收口 PUT/创建向导/reset 三口;rollback 单独保留豁免 = 历史不可变语义优先——回滚到含已占前缀的历史版本属数据修复场景;如仍需收口,在 rollback 前补跑查重)。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
3. **并发保存同租户撞 UNIQUE → 500**(单管理员场景可接受;收口 = 捕 IntegrityError 转 409)。
|
|
|
|
|
|
4. **统计 API 无 `?site=` 筛选**(spec §7.6 提及;Phase 3 前端需要时补)。
|
|
|
|
|
|
5. **登录限流反代形态退化为按用户名**(限流键 username + request.client.host,无 XFF 解析;可信代理解析归部署批次)。
|
|
|
|
|
|
6. **avatar 旧文件不清理**(换头像后旧文件留盘;孤儿累积无害)。
|
|
|
|
|
|
7. **停用租户管理员存量 token 最长存活 8h**(TTL 限窗)——即时锁死需在 validate 时回查租户状态。
|
|
|
|
|
|
8. **400(预检撞车)vs 409(竞态 IntegrityError)并存**——API 冻结前统一。
|
|
|
|
|
|
9. **limiter 全量清扫**(token/登录限流的内存键永不回访,长跑进程缓慢累积;收口 = 定期清扫)。
|
|
|
|
|
|
10. **lead_push_service 的 settings re-export 锚**(app.engine.config 的 settings 在 lead_push_service 顶部 re-export,22 处测试 monkeypatch 依赖此锚——改动前先迁测试)。
|
2026-10-13 23:29:31 +08:00
|
|
|
|
11. ~~密钥掩码未实现~~(Phase 3 收口,commit 0c25287:GET config/settings 四处响应密钥值掩码为哨兵 `__SET__`,PUT 收哨兵保留库值;前端 `admin/src/api/mask.ts` secretToSubmit/secretPlaceholder 同款契约)。
|
|
|
|
|
|
12. ~~站点管理权限现为仅超管~~(Phase 3 收口,commit 0c25287:放开租户管理员同权,与 spec §7.3 原文一致)。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
|
2026-10-13 23:29:31 +08:00
|
|
|
|
**Phase 3 清理单**(Phase 1/2 审查挂账滚动合并;~~删除线~~ 项已结,结项时点见各条注记;未结项滚入 Phase 4 清理单增补):
|
2026-10-13 12:21:10 +08:00
|
|
|
|
|
|
|
|
|
|
1. ~~FakeAI 三份拷贝收敛进 conftest~~(Task 10 完成:conftest.py 单一定义,tests/ 零拷贝)。
|
|
|
|
|
|
2. ~~rate_limit 与 ws_conn_limit 的 0 语义统一 + 配置类型校验~~(Task 10 完成)。
|
|
|
|
|
|
3. ~~name_to_region_extras / context_seed_map 等散装 resolver 收拢为 config_views 单入口~~(Task 10 完成:app/platform/config_views.py)。
|
|
|
|
|
|
4. ~~avatar 从 seed_profiles 迁 uploads 卷~~(Task 7 完成:上传 + 三级解析链)。
|
|
|
|
|
|
5. **AIService.from_runtime 类方法**——组装正典化(出处:引擎改造审查)。
|
|
|
|
|
|
6. **boundary_filter 视图常量退役**——admin 重写后旧测试迁移时(出处:引擎改造审查)。
|
2026-10-13 23:29:31 +08:00
|
|
|
|
7. ~~push enabled 布尔空槽语义~~(Phase 3 收口:凭据页显式布尔开关 ElSwitch,normalize `enabled === true` 空槽归 false,提交永为显式布尔)。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
8. **contact.template_languages 运行期接线**——后台批次(出处:配置 schema 审查)。
|
|
|
|
|
|
9. **话术含引号/换行的转义**——编辑器批次(`_format_deflection_pool` docstring 有注)。
|
|
|
|
|
|
10. **产品决策项**:国名纠正 e164 变化时是否补发更正邮件(上游行为更优);REST/WS 服务端 IP 口径是否统一(出处:上游吸收审查)。
|
2026-10-14 13:45:14 +08:00
|
|
|
|
11. ~~/health 分级健康~~(Phase 4 Task 1 完成:`/health` 返回 status/tenants(engine_ok 三态)/domains 分级 JSON——单租户库损坏 degraded 仍 200 不杀容器;compose healthcheck 消费同一端点,Phase 4 终验 docker 演练实测与手册 §2 解读一致)。
|
|
|
|
|
|
12. ~~PromptProvider.from_dir 的 FileNotFoundError 带路径上下文~~(现状即满足:`read_text` 原生 FileNotFoundError 自带完整路径——终验实测 `PromptProvider.from_dir` 指向缺失目录时异常信息为 `[Errno 2] No such file or directory: '<完整路径>/system.md'`;DB 源路径另有 tenant_source 显式包装注记)。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
|
2026-10-13 23:29:31 +08:00
|
|
|
|
## Phase 3 完成状态(2026-10-13 收尾核对)
|
2026-10-13 12:21:10 +08:00
|
|
|
|
|
2026-10-14 00:05:40 +08:00
|
|
|
|
**测试基线:后端 pytest 595 passed / 前端 vitest 156 passed**(Phase 3 起步 139,Task 9 平台设置页 +14、终审修复通用 JSON 组 +3;两端数字均已实跑核对,后端唯一 warning 仍为 starlette TestClient 弃用提示,无害)。`pnpm build`(vue-tsc --noEmit && vite build)零错误。后续回归以此为准。
|
2026-10-13 23:29:31 +08:00
|
|
|
|
|
|
|
|
|
|
**交付形态**:`admin/` 子目录的 Vue3 + TS + Vite + Element Plus SPA(art-design-pro 模板,pnpm 管理),与后端同仓。本地起法:
|
|
|
|
|
|
|
|
|
|
|
|
- 后端:`PYTHONUTF8=1 .venv/Scripts/python -m uvicorn app.main:app --port 8018 --workers 1`(不变)。
|
|
|
|
|
|
- 前端:`cd admin && pnpm install && pnpm dev`(默认 3006,被占自动顺延);Vite 代理 `/api` → 8018,`/api/public` 与 `/static` 走 Host 改写代理(`VITE_TENANT_PREVIEW_HOST`,见 admin/.env.development 注释)。`pnpm test` 跑 vitest;`pnpm build` 出 dist。
|
|
|
|
|
|
- 登录:超管 / 租户管理员同一登录页(滑块验证为纯前端交互校验);must_change_password 由后端 409 门强制,前端跳 `#/auth/force-change-password`。
|
|
|
|
|
|
|
|
|
|
|
|
**admin 能力一览**(Phase 2 平台 API 的全量对接;菜单按角色过滤,后端 require_super / _tenant_guard 兜底):
|
|
|
|
|
|
|
|
|
|
|
|
- 认证流:登录 / 强制改密 / 退出;pinia + 内存 token,401 统一跳登录,改密 409 契约锁定。
|
|
|
|
|
|
- 仪表盘:平台总览 + 租户卡片墙(超管)/ 我的指标(租户管理员),时间窗切换。
|
|
|
|
|
|
- 租户管理(仅超管):列表 / 创建向导(消费出厂档案)/ 详情编辑 / 站点与域名子资源 / 停用复启。
|
|
|
|
|
|
- 会话与线索:列表(筛选 / 分页 / 租户切换)/ 详情消息流 / 推送日志 / 删除。
|
2026-10-14 00:05:40 +08:00
|
|
|
|
- 配置中心:品牌(表单 / 本地简化预览 / 嵌入代码 / 头像上传)、提示词四件套与结构化词表、试金石面板、凭据(邮件 / 推送 / AI 表单 + SMTP 测试 + 推送探测)、contact/engine/misc 三组通用 JSON 编辑(终审 I-1 补齐 spec §7.3 全组覆盖)、版本历史(两版对比 / 回滚 / 组级重置出厂)。
|
2026-10-13 23:29:31 +08:00
|
|
|
|
- 统计分析:卡片 / 转化漏斗 / 按日趋势 / 意向·来源·站点·国家分布(ECharts)/ 推送健康表;时间与租户筛选(口径同 stats.py docstring 口径表)。
|
2026-10-14 10:10:52 +08:00
|
|
|
|
- 平台设置(仅超管):自由 KV 表的推荐键表单(ai.* / smtp.*,键名是前端约定)+ 存储总览表格;保存 = 逐键 PUT(只提交脏键)。**Phase 4 Task 5A 起推荐键已接线 resolver 平台默认回退链(租户槽位 → 平台设置快照 → env),保存后即时生效**;密钥键掩码哨兵交互与凭据页同款(清除 / 恢复保持)。
|
2026-10-13 23:29:31 +08:00
|
|
|
|
|
|
|
|
|
|
**前端实现约定**(后续维护须知):
|
|
|
|
|
|
|
|
|
|
|
|
- 视图逻辑沉淀在各 `views/**/model.ts` 纯函数(掩码判定 / 归一 / 脏检测 / 提交值构造 / 校验),vitest 直接覆盖;.vue 只做装配。auto-import 生效(ref/reactive/ElMessage 等无需手写 import)。
|
|
|
|
|
|
- 密钥键判定前端镜像后端 `_SETTING_SECRET_MARKERS` 子串匹配('monkey' 含 'key' 也命中——与后端一致是契约,不"修正")。
|
|
|
|
|
|
- PUT config 整组替换语义:前端封装读-改-写(GET 全组 → 改 → PUT);平台设置不同,为逐键 upsert。
|
|
|
|
|
|
- `components.d.ts` 构建漂移:`pnpm build`/dev 会重写入库的 dts(仅行尾差异)——`git diff --ignore-cr-at-eol` 为空即 `git checkout --` 还原入库版(超集原则,勿提交行尾噪音)。
|
|
|
|
|
|
|
|
|
|
|
|
**滚动纪律**(自 Phase 3 计划注意延续):conftest 三坑(NullPool / portal 桥接 / 禁 asyncio.run)、平台层测试用 `db_source_app` / `super_client` 夹具、引擎读配置走 config_views 单入口、统计展示照 stats.py 口径表——均照抄勿发明。
|
2026-10-13 12:21:10 +08:00
|
|
|
|
|
|
|
|
|
|
## Phase 4 部署记档(提前锚定,部署批次展开成 docs/deploy-production.md)
|
|
|
|
|
|
|
|
|
|
|
|
- **`pip install -r requirements.txt` 必须执行**:python-multipart(>=0.0.20)为 Phase 2 新依赖——Task 7 实证缺失即头像上传 500(FastAPI multipart 解析依赖它)。
|
|
|
|
|
|
- **bootstrap_platform.py 用法**:`PYTHONUTF8=1 python scripts/bootstrap_platform.py [--db PATH] [--profiles SLUG ...] [--seed DIR]`。两步全幂等:init(schema + 超管引导,凭据取 env/.env 的 ADMIN_USERNAME/ADMIN_PASSWORD)+ 导入出厂租户(默认 auto jiancai 两租户,已有 slug 跳过);**不建租户管理员**(走后台创建向导补)。
|
|
|
|
|
|
- **TENANT_SOURCE=db 默认**(生产装配形态);`file` 为冷备/本地调试回退。
|
|
|
|
|
|
- **admin 域名反代区分**:`/api/platform/*` 前缀豁免租户 Host 解析——平台后台 API 可与租户聊天域名同源反代(按路径分流)或独立域名,两形态皆可;租户聊天面(`/`、`/static`、WS、`/api/public`、`/api/chat*`)仍严格按 Host 解析租户,未知 Host 404。
|
2026-10-13 12:40:21 +08:00
|
|
|
|
- **ADMIN_PASSWORD 默认值警示**(终审 I-4):lifespan 启动检测到 ADMIN_PASSWORD 仍是出厂默认(admin123)时打 WARNING 日志——生产部署必须设 ADMIN_PASSWORD 环境变量换掉,且超管首登立即改密(must_change_password 门强制)。
|
2026-10-14 00:05:40 +08:00
|
|
|
|
- **Phase 4 清理单增补**(Phase 3 冒烟沉淀 + 终审记档;Phase 3 清理单未结项 5/6/8/9/10 一并滚入):
|
2026-10-14 13:45:14 +08:00
|
|
|
|
1. `components.d.ts` 构建行尾漂移(长期收口 = 生成物出库或 EOL 规范钩子,现阶段靠手工还原)。**收档(已知现状非缺陷,2026-10-14 终验)**:build/dev 重写入库 dts 仅行尾差异(`components.d.ts` 与 `auto-imports.d.ts` 两文件同性状,终验 pnpm build 再证实)——`git diff --ignore-cr-at-eol` 为空即 `git checkout --` 还原,勿提交行尾噪音。
|
|
|
|
|
|
2. 推送探测 env 回退真址:租户 push URL 空槽时 `resolve_push_settings` 回退 env `PUSH_API_URL`(默认即真 UniService 生产址)——后台点「探测」会对真实服务发空批量 POST(`{"messages": []}`,按契约文档应为无操作,但属真实出站)。**收档(已知现状非缺陷,2026-10-14 终验)**:处置 = 介意真实出站则生产 .env 将 PUSH_API_URL 指 stub 域后再点探测。
|
|
|
|
|
|
3. 生产反代需规划 admin 域的头像/品牌公开端点:`/api/public/*` 按租户 Host 解析,admin 域直挂必 404——开发期靠 Vite Host 改写代理,生产要么同源反代改 Host(单租户形态)要么前端改用租户域名绝对 URL。**收档(已知现状非缺陷,2026-10-14 终验)**:处置 = 生产选形态 B(租户域名路径分流)即规避;坚持形态 A 则接受预览缺失(页内本地简化预览兜底,手册 §3.3 已注记)。
|
|
|
|
|
|
4. `VITE_TENANT_PREVIEW_HOST` 单一预览域名:开发代理只指向一个租户 Host,切换租户时头像等公开资源预览不准(.env.development 已注记)。**收档(已知现状非缺陷,2026-10-14 终验)**:仅影响开发期预览,切租户时改 .env.development 预览 Host 重启 dev server 即可。
|
2026-10-14 10:10:52 +08:00
|
|
|
|
5. ~~resolver 平台默认回退链接入 platform_settings~~(Phase 4 Task 5A 完成:`app/platform/settings_store.py` 内存快照 + lifespan 装载 + PUT settings 后 refresh 热刷新;config_views 的 email/ai 两组改三级回退(租户槽位 → 平台设置快照 → env),push 无平台推荐键保持两级;平台设置页"仅存储"注记移除)。
|
2026-10-14 10:43:13 +08:00
|
|
|
|
6. ~~email/test 与 push/test 速率限制~~(Phase 4 Task 5B 完成:config_routes 内 `SlidingWindowCounter` 滑动窗限流(登录限流器同款风格),每租户每类端点 15 分钟 5 次真实出站,超限 429;校验失败/未配置 400 不消耗预算,超限后 429 优先于一切预检)。
|
|
|
|
|
|
7. ~~锁屏功能已摘除入口~~ + ~~通知组件摘除~~(锁屏 Phase 3 摘入口,Phase 4 Task 5F 删代码:art-screen-lock/art-notification 组件目录、component.ts/headerBar.ts 配置项、useHeaderBar.shouldShowNotification、类型字段、components.d.ts 条目、crypto-js 依赖(env.d.ts 声明/package.json/optimizeDeps/lockfile)、en/zh 孤儿语言键全部清除,grep 零残留)。
|
|
|
|
|
|
8. ~~租户切换器 composable 收拢~~(Phase 4 Task 5E 完成:`admin/src/composables/useTenantOptions.ts` 收拢会话/配置/统计三页租户下拉(默认形态选中首个 + bootstrapTenantContext 引导;stats 用 selectFirst:false 纯数据源);dashboard 卡片墙形态不在此列)。
|
|
|
|
|
|
9. ~~ECharts 按需收窄~~(Phase 4 Task 5D 完成:echarts.use 收窄为 Bar/Line/Pie + Title/Tooltip/Grid/Legend + CanvasRenderer——全仓 grep 证实存活消费方仅 stats 页与 useChart;echarts chunk 767,864 B → 559,737 B(-27%),dist 总体积 -273 KB)。
|
2026-10-14 14:03:50 +08:00
|
|
|
|
10. **引擎日志含会话摘录 vs spec §9 承诺**(终审挂账)——观察期收口为 session_id 索引(手册 §6.2 有详述;收口前日志文件按含 PII 对待,备份/外发注意)。
|
2026-10-12 16:10:18 +08:00
|
|
|
|
|
2026-10-14 13:45:14 +08:00
|
|
|
|
## Phase 4 完成状态(2026-10-14 收尾核对)
|
|
|
|
|
|
|
2026-10-14 14:04:54 +08:00
|
|
|
|
**测试基线:后端 pytest 632 passed + 2 skipped / 前端 vitest 164 passed**(终审微修 +2 no-cache 测试;后端根目录与 tests/ 子目录各跑一遍数字一致;唯一 warning 仍为 starlette TestClient 弃用提示,无害)。`pnpm build`(vue-tsc --noEmit && vite build)与 `docker compose build`(两阶段)复验零错误。**docker 演练终检实测**:`up -d` 后 healthcheck 约 20s 转 healthy → bootstrap(run --rm 形态)输出与手册 §2 逐字吻合 → 重启后 `/health` 分级 JSON 与手册示例一致(status=ok、两租户 active、domains=3、engine_ok null 属懒建正常态)→ `/admin/` 200 → down 后 data/ 演练产物删净、tenants/ 清空、无容器/网络残留。演练顺带核实:**容器运行中跑 bootstrap 不热刷新内存租户注册表**(/health 需重启才反映,手册 §6.6 已补注记;首次部署按 §2 顺序无此问题)。**项目四阶段至此全部完成,此为最终回归基线。**
|
2026-10-14 13:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
**部署工件清单**(全部入库,上线路径自足):
|
|
|
|
|
|
|
|
|
|
|
|
- `Dockerfile`:两阶段构建(node:20-alpine 编译 admin dist → python:3.13-slim 运行时含 tzdata;服务器零 Python/node 依赖)。
|
|
|
|
|
|
- `docker-compose.yml`:`name: all-kefu`(防 1Panel 目录撞名互删);`./data` + `./tenants` 双 bind mount——**platform.db 在 data/ 卷内**(`data/platform.db`,容器重建不丢,P4T1 审查修复);healthcheck 用容器内 python urllib(slim 无 curl);json-file 日志封顶 10m×3;mem_limit 768m;`--workers 1` 钉死。
|
|
|
|
|
|
- `deploy/openresty-all-kefu.conf.template`:反代模板(多域名同 upstream、WS 升级头单套、1Panel include 行坑注记)。
|
|
|
|
|
|
- `docs/deploy-production.md`:runbook 工件(.env 逐项 / 首次部署 / 反代三件事 / 存量迁移对账 / 切换 runbook 四阶段 / 备份恢复演练 / 归档 / 踩坑实录 13 条;手册为 runbook 工件非实录,首次部署前按全局部署 SOP 与用户确认)。
|
|
|
|
|
|
- `scripts/` 四件:`bootstrap_platform.py`(幂等引导 schema+超管+出厂租户)、`import_legacy.py`(老库只读导入+四元组对账)、`archive.py`(push_logs 归档)、`breach_rate.py`(破防率观测,`--tenant` 多租户适配,Task 5 随迁)。
|
|
|
|
|
|
|
|
|
|
|
|
**上线前检查清单(浓缩版;命令与细节见手册)**:生产 .env 设 ADMIN_PASSWORD 等强凭据 → `docker compose build` → `docker compose run --rm app python scripts/bootstrap_platform.py` → `docker compose up -d` → `/health`(status=ok、domains=3)→ `/admin/` 超管首登强制改密、后台补租户管理员 → 1Panel 反代片段备好(Host 头 / WS 单套 / 5m 三件事,先不指流量)→ 老库导入对账(手册 §4)→ 隧道+hosts 全功能验证(清单见手册 §5 阶段一)→ 逐域名切流(先一切半观察再切二)→ 观察一周(日检清单 + breach_rate)→ 老项目停用归档(stop 不 rm,回滚预案在手册 §5)。
|
|
|
|
|
|
|
2026-10-12 16:10:18 +08:00
|
|
|
|
## 文档索引
|
|
|
|
|
|
|
|
|
|
|
|
- 设计文档(spec):`docs/superpowers/specs/2026-10-12-unified-kefu-platform-design.md`
|
|
|
|
|
|
- Phase 1 实施计划:`docs/superpowers/plans/2026-10-12-phase1-engine-merge-tenantization.md`
|
2026-10-13 12:21:10 +08:00
|
|
|
|
- Phase 2 实施计划:`docs/superpowers/plans/2026-10-13-phase2-platform-layer.md`
|
2026-10-13 23:29:31 +08:00
|
|
|
|
- Phase 3 实施计划:`docs/superpowers/plans/2026-10-13-phase3-admin-spa.md`
|
2026-10-14 09:44:28 +08:00
|
|
|
|
- Phase 4 实施计划:`docs/superpowers/plans/2026-10-14-phase4-deploy-migration.md`
|
|
|
|
|
|
- 部署手册(含切换 runbook + 踩坑实录):`docs/deploy-production.md`;反代模板 `deploy/openresty-all-kefu.conf.template`
|
2026-10-14 13:45:14 +08:00
|
|
|
|
|
|
|
|
|
|
## 项目完成总结(2026-10-14,四阶段收官)
|
|
|
|
|
|
|
2026-10-14 14:04:54 +08:00
|
|
|
|
ai-kefu / ai-kefu-jc 两套复制分叉代码 → 统一多租户平台,四个阶段全部完成:Phase 1 引擎合并与租户化(2026-10-12)、Phase 2 平台层(10-13)、Phase 3 管理后台 SPA(10-13)、Phase 4 部署与迁移工件(10-14)。历程数字:**测试 315 → 632+2skip(后端)+ 164(前端 vitest)**;终审环节抓出并当场收口的关键缝隙三例——**墙的执行侧缺口**(词表配置齐但执行路径未全量消费)、**站点停用语义**(停用租户的存量管理员 token 存活窗口与聊天面 503 边界)、**密钥掩码旁路**(GET 掩码但 PUT/回滚路径可绕过哨兵契约)——审查纪律的价值注脚。**后续演进方向**(均为增量,不动四层架构):提示词沙箱试聊(后台配置中心直接对话验证词表/提示词效果)、推送失败告警(lead_push 连续失败主动通知,当前只有推送健康表被动展示)、单租户独立部署路径已预留(engine 包不感知 platform,可整体拎走单租户跑)。
|