docs: 统一 AI 客服平台设计 spec(all-kefu)
单进程多租户架构:Host 识别租户、每租户独立 SQLite、 统一后台(art-design-pro)超管/租户两级、配置热加载+版本快照+试金石、 引擎以 jc 为主线合并上游、存量迁移保留会话 ID。 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
# all-kefu 统一 AI 客服平台设计(spec)
|
||||
|
||||
- 日期:2026-10-12
|
||||
- 状态:已与需求方逐节确认,待评审
|
||||
- 来源项目:`D:\refactor\ai-kefu`(Hesu Auto 汽车出口客服,端口 8016)、`D:\refactor\ai-kefu-jc`(HESUPIPE 管道建材客服,端口 8017)
|
||||
- 新项目位置:`D:\refactor\all-kefu`(本仓库)
|
||||
|
||||
## 1. 背景与问题
|
||||
|
||||
两个现有项目是**同一套 AI 客服引擎的两份拷贝**(2026-09-29 按当时的"方案 A:完整拷贝 + 行业化定制"分叉,约 85% 代码同源)。业务模式相同:AI 只做"需求了解 + 留资获客",价格/交期/MOQ 一律不正面回答、引导留联系方式,线索经邮件 + UniService CRM 推送转人工。技术栈相同:FastAPI + SQLite(aiosqlite) + 智谱 GLM(glm-4-flash)+ 纯静态前端(widget 用 script 标签 + Shadow DOM 嵌入官网)。
|
||||
|
||||
复制模式已产生的实际代价(均有实证):
|
||||
|
||||
1. **重复劳动**:2026-10-08 同一天,上游实现了 whatsapp-cc-completion,jc 独立实现了更完整的 cc-completion(判定树+状态机,376 行)——同一功能写了两遍且实现分叉。
|
||||
2. **人肉同步**:上游修 bug 需对照 changelog 手动同步到 jc,规则写在 jc 的 CLAUDE.md 里。
|
||||
3. **生产事故**:两套 docker-compose 在 1Panel 上项目名撞车(目录都叫 index),起一个容器会删掉另一个(互相 502)。已用 `name:` 修复,但这是"N 份部署"模式的固有风险。
|
||||
4. **同页冲突**:两个 widget 源自同一代码,`sessionStorage` key(`widget_invite_dismissed_<path>`)与 host id(`cc-widget-host`)同名互扰;集团官网同页挂两个客服时必然出问题。
|
||||
5. **僵尸配置**:ai-kefu 的 compose 跑着一个 Redis 容器,代码零引用。
|
||||
|
||||
每客户的差异只占约 15%(提示词、词表、品牌、环境配置、部署标识、少量代码点),其余 85% 是应该共享的引擎。**每加一个客户复制一套代码不可持续**——本设计把差异收敛为"租户配置",引擎单一份。
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
**目标**:
|
||||
|
||||
1. 一套代码(all-kefu)承载所有客户(租户),加客户 = 后台建租户 + 填配置 + 加一条反代,零代码复制、不起新容器。
|
||||
2. 统一管理后台:超管管理所有租户、查看全局数据;租户管理员查看并配置自己租户的一切(含提示词与词表,需求方已确认"租户全开")。
|
||||
3. 所有配置动态生效(热加载,不重启)。
|
||||
4. 存量两个项目的数据与线上行为无感迁移(官网嵌入代码零改动、CRM 线索不重复)。
|
||||
|
||||
**非目标(明确推迟,YAGNI)**:
|
||||
|
||||
- 提示词沙箱试聊(用草稿配置开真实对话预览)——需要"草稿态"整套机制,二期;现阶段版本回滚 + 出厂重置够用。
|
||||
- 换 PostgreSQL、超管/租户之外更细的角色权限、租户自助注册、计费——量级不到。
|
||||
- 引擎行为重构(conversation.py 结构重写)——只做租户化依赖注入,不动结构。
|
||||
|
||||
## 3. 已确认的关键决策
|
||||
|
||||
| 决策 | 结论 | 备注 |
|
||||
|---|---|---|
|
||||
| 运行架构 | **单进程多租户**,一个 FastAPI 进程 = 引擎 + 统一后台 | 需求方委托推荐后确认 |
|
||||
| 数据隔离 | 每租户独立 SQLite 文件;平台数据(租户/账号/配置)在 platform.db | 文件级隔离,备份=拷文件 |
|
||||
| 租户识别 | 按 Host 头(域名映射表);官网嵌入代码零改动 | 现有 widget 即从 script.src origin 推导连接地址 |
|
||||
| 后台形态 | 统一后台,超管 + 租户管理员两级登录 | |
|
||||
| 租户权限 | **全开**:租户可改自己租户的一切配置(含提示词、词表) | 配套安全网:版本历史/回滚/试金石/出厂重置 |
|
||||
| 后台前端 | **art-design-pro**(Vue3 + TypeScript + Vite + Element Plus,MIT) | 替代原先"单文件原生 JS 后台"的设想 |
|
||||
| 存量数据 | 导入新平台(写迁移脚本,保留原会话 ID) | |
|
||||
| worker 数 | `--workers 1` 写死 | 内存 token/限流/配置缓存/每租户 engine 缓存均假设单进程 |
|
||||
|
||||
## 4. 总体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ all-kefu 平台(单进程 FastAPI) │
|
||||
chat.agent.hnxinyouhe.com ──┐ │
|
||||
chat2.agent.hnxinyouhe.com ─┼→ 按 Host 识别租户 → ├─ platform.db(租户/域名/账号/配置)
|
||||
ai.hesupipe.com(未来) ──┘ (域名映射表) └─ tenants/auto.db、tenants/jc.db
|
||||
admin.xxx.com ────────────→ 统一后台(超管+租户登录)│
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **一个进程**:同时承载所有租户的聊天引擎 + 统一管理后台,一个 Docker 容器部署在现有阿里云 ECS(1Panel)上。
|
||||
- **租户识别 = Host**:数据库存"域名 → 租户"映射;加租户 = 后台加记录 + DNS/反代指过来。
|
||||
- **数据分层**:platform.db = 租户注册表、域名映射、admin_users、配置版本快照、平台级默认(集团 AI key、默认 SMTP);tenants/<tenant_id>.db = 现有 5 张表(conversations/messages/customer_profiles/evaluations/lead_push_logs),一租户一文件,互不可见。
|
||||
- **两级登录**:超管(tenant_id 为空)看所有租户数据、增删租户、改任何配置;租户管理员只看自己租户数据、改自己配置。数据级隔离由后端强制。
|
||||
|
||||
**边界规则**:
|
||||
|
||||
- 未知 Host 一律 404,**不做兜底租户**(防 Host 混淆打错租户)。
|
||||
- admin 后台走独立域名,不依赖 Host 租户解析。
|
||||
- 停用租户(status=disabled):聊天/公开端点返回中性"服务暂停"页(widget 优雅降级);超管仍可只读其数据;Registry 释放其 DB engine。
|
||||
|
||||
## 5. 租户配置模型
|
||||
|
||||
### 5.1 配置 Schema(两个项目 15% 差异的收敛)
|
||||
|
||||
| 配置组 | 内容 | 对应现在散落的位置 |
|
||||
|---|---|---|
|
||||
| 基础 | 租户名、启用/停用、绑定域名列表 | compose/openresty 硬编码 |
|
||||
| 品牌 | 展示名、主题色、头像、欢迎语、热门话题、输入框占位符、WhatsApp 号、联系邮箱、隐私政策 URL | chat.html / widget.js / chat.js 三处硬编码 |
|
||||
| 提示词 4 件套 | system.md、company_facts.md(知识库)、extractor.md、evaluator.md(markdown) | app/prompts/ |
|
||||
| 安全墙词表 | 价格墙正则组(多语言)、迂回话术池(EN/ZH/ES)、零号码替换句、触发词正则(边界/预算/索要)、防误杀白名单(建材 DN/PN/SDR vs 汽车 km/年款) | boundary_filter.py / conversation.py 内嵌 |
|
||||
| 联系方式策略 | 区号补全开关、模板句语言集、轻补邮箱开关 | conversation.py 内嵌参数 |
|
||||
| 邮件 | SMTP(可选"用平台默认")、发件人显示名、通知邮箱列表 | .env + email_service.py 硬编码 |
|
||||
| 推送 | 启用开关、URL、API Key、**external_key 前缀(全局唯一约束)**、超时/重试 | .env |
|
||||
| AI 凭据 | 可选覆盖(不填=平台默认集团 key) | .env |
|
||||
| 引擎杂项 | context 种子键映射(product_name vs vehicle_name)、限流参数、国名映射扩展 | chat_rest.py / cc_completion.py 代码差异 |
|
||||
|
||||
### 5.2 存储(platform.db)
|
||||
|
||||
```sql
|
||||
tenants(id, slug, name, status, created_at)
|
||||
tenant_domains(domain UNIQUE, tenant_id)
|
||||
tenant_config_versions(tenant_id, version, config_json, updated_by, comment, created_at)
|
||||
-- 当前生效版 = MAX(version);每次保存存整份快照(几十 KB,不做增量)
|
||||
admin_users(id, tenant_id NULL=超管, username, password_hash, status)
|
||||
platform_settings(key, value) -- 平台级默认:集团 AI 凭据、默认 SMTP
|
||||
```
|
||||
|
||||
回滚 = 把旧 version 复制成新 version(历史不可变)。提示词与词表在同一快照内,回滚原子。
|
||||
|
||||
### 5.3 生效机制(热加载)
|
||||
|
||||
进程内 TenantRegistry:`tenant_id → { config, db_engine, session_factory, manager }`。后台保存 → 校验 → 写 platform.db → **原子替换 Registry 条目**。进行中的会话下一条消息即用新配置(提示词逐轮拼装,天然支持)。不重启、无分发协议——单进程架构红利。
|
||||
|
||||
### 5.4 保存校验 + 试金石(租户可编辑正则的安全网)
|
||||
|
||||
1. **编译校验**:每条正则 re.compile 试跑,非法整单拒收。
|
||||
2. **试金石面板**:编辑页内嵌测试框,贴样例句("What's your price for DN800?"、"预算5000美元")实时显示拦/放/替换判定,发布前眼见为实。
|
||||
3. **回滚**:版本列表任意旧版一键恢复;每个配置组另有"重置为出厂默认"。
|
||||
|
||||
### 5.5 密钥处理
|
||||
|
||||
- 后台读取一律掩码;仅主动修改时明文输入;"用平台默认"状态下租户侧不存密钥。
|
||||
- platform.db 备份按敏感件对待(含 SMTP/push/AI 密钥),写入部署文档。
|
||||
|
||||
### 5.6 关键设计规则
|
||||
|
||||
- **话术单一事实源(R1)**:迂回话术池只存于墙配置;ai_service 组装 system prompt 时以模板变量(`$DEFLECTION_POOL` 等)注入提示词。改一处,提示词与墙同时变——消除"提示词教的说法"与"墙的执法标准"分叉。
|
||||
- **配置演进(E4)**:Registry 加载 = 快照深合并出厂默认值,引擎升级新增字段不炸旧租户。
|
||||
- **external_key 前缀全局唯一(E1)**:数据库约束 + 保存校验,防 CRM 线索空间串号。
|
||||
- **出厂档案**:`seed_profiles/auto/`、`seed_profiles/jiancai/`(提示词 md + 词表 json + 品牌 json),由提取脚本从两个老项目生成;新租户创建时选出厂档案或空白。
|
||||
|
||||
## 6. 引擎改造与代码合并
|
||||
|
||||
### 6.1 TenantContext——重构主轴
|
||||
|
||||
现状服务层是"进程级全局"(import 时读提示词、全局 Settings、模块级正则、全局 engine)。改造为"随租户走":
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class TenantRuntime:
|
||||
tenant_id: str
|
||||
config: TenantConfig # 快照 ⊕ 出厂默认 深合并;词表已编译校验
|
||||
engine: AsyncEngine # 该租户 SQLite(懒创建,停用时关闭)
|
||||
session_factory: async_sessionmaker
|
||||
manager: ConversationManager
|
||||
|
||||
class TenantRegistry:
|
||||
resolve(host) -> TenantRuntime # 域名表缓存,后台改域名即失效
|
||||
reload(tenant_id) # 配置保存后原子换条目
|
||||
```
|
||||
|
||||
请求流:中间件按 Host 解析租户 → `request.state` → 路由把 runtime 传进服务。服务层从"读全局"变"接参数"。
|
||||
|
||||
逐文件改造点:
|
||||
|
||||
| 文件 | 现状 | 改造后 |
|
||||
|---|---|---|
|
||||
| config.py | 全局 Settings 读 .env | 拆 ProcessSettings(.env:平台库路径/端口/超管初始密码)+ TenantConfig(platform.db) |
|
||||
| ai_service.py | import 时读 4 提示词 | PromptProvider 挂 TenantRuntime;组装 = system.md + company_facts.md + `$DEFLECTION_POOL` 注入 + 当轮 context reminder |
|
||||
| conversation.py(约 1250 行) | 3 组模块级正则 + 逐字话术 | 正则/话术从 config.wall 编译;Manager 每租户实例化;**只做依赖注入,不重写结构** |
|
||||
| boundary_filter.py | 正则话术焊死 | 纯函数化:接收已编译词表,机制与数据分离 |
|
||||
| email/lead_push | .env 全局配置 | 读租户配置段;启动 sweep_stale_pending 逐租户跑 |
|
||||
| rate_limit.py | key=ip+device | key=(tenant_id, ip, device) |
|
||||
| admin 路由 | 内存 token + 单密码 | platform.db 认证 + 会话超时 + 登录失败限流 |
|
||||
|
||||
### 6.2 合并清单(jc 为主线)
|
||||
|
||||
**取自 jc**(10-08 两条分叉线中更完整一方):
|
||||
|
||||
- cc_completion.py 完整判定树 + 状态机 + extractor cc_reply + e164 出站渲染
|
||||
- 多语言墙(€/£、西/葡/俄/阿词表、ES 迂回池、建材防误杀白名单)
|
||||
- dual-contact 分步双要(先 WhatsApp 带国家码→轻补邮箱)+ `_email_asked`
|
||||
- CC 强制墙 + 零号码墙回显豁免收窄
|
||||
- XFF 服务端优先、阿语数字归一、resolve_region_code
|
||||
|
||||
**从上游吸收**:input-placeholder 可配置化(落进品牌配置组);上游独有测试的断言价值(test_channel_hygiene、test_chat_rest_ip、test_lead_push_cc、test_cc_integration、test_ip_service_region、test_prompt_static 等)。
|
||||
|
||||
**不带走**:blog.html(模板残留)、Redis 配置(死代码)、上游旧版 whatsapp-cc-completion(被 jc 版取代,但其测试断言移植过来验证 jc 版行为)。
|
||||
|
||||
### 6.3 前端
|
||||
|
||||
1. widget.js:启动时 `fetch /api/public/brand`(Host 已识别租户,免鉴权返回品牌 JSON)→ 与 data-* 合并(data-* 优先);失败时 data-*/中性默认兜底;no-cache。
|
||||
2. 客户端 key 全部租户化:`cc-widget-host-<tenant>`、`widget_invite_dismissed_<tenant>_<path>`、chat 页 localStorage 会话 key 同理——同页双挂件根治。
|
||||
3. widget.js 与 chat.html 保持零构建原生 JS(嵌入官网资产必须轻量零依赖);主题派生单元机制不变,默认值来自品牌配置。
|
||||
|
||||
### 6.4 目标目录结构
|
||||
|
||||
```
|
||||
all-kefu/
|
||||
admin/ # art-design-pro SPA(Vue3+Vite+TS+Element Plus)
|
||||
app/
|
||||
main.py # 平台入口:租户中间件 + 路由挂载,--workers 1
|
||||
platform/ # registry、配置服务、校验+试金石、admin 认证、platform.db 模型
|
||||
engine/ # 合并后的客服引擎(jc+上游),全部租户感知
|
||||
routes/ # ws/chat、rest/chat(租户作用域)
|
||||
services/ # conversation、boundary_filter、cc_completion、contact_pipeline、
|
||||
# email、lead_push、ip、rate_limit、ai_service(PromptProvider)
|
||||
providers/ # glm
|
||||
models/ # 租户库 5 张表 + 逐租户 _migrate
|
||||
static/ # widget.js、chat.html、theme(零构建)
|
||||
seed_profiles/ # auto/、jiancai/
|
||||
tenants/ # 运行期:每租户一个 .db
|
||||
platform.db
|
||||
scripts/ # extract_config.py、import_legacy.py、mock_uniservice
|
||||
tests/
|
||||
docs/
|
||||
```
|
||||
|
||||
`platform/` 与 `engine/` 是隔离边界:引擎不感知平台存在,只认 TenantRuntime——将来某租户需独立部署时 engine 包可整体拎走。
|
||||
|
||||
## 7. 统一管理后台(art-design-pro)
|
||||
|
||||
### 7.1 工程与构建
|
||||
|
||||
- `admin/` 独立 SPA;开发时 vite dev proxy 到 FastAPI;生产两阶段 Docker(node 阶段 pnpm build → 产物拷入 Python 镜像),服务器零 node 依赖。
|
||||
- admin 静态文件打进镜像(延续"改前端必须 build"的既有教训);由 FastAPI 在管理域名下同源服务,无 CORS。
|
||||
- 只有 admin 走前端工程化;widget/chat 保持原生 JS。
|
||||
|
||||
### 7.2 认证与权限
|
||||
|
||||
- admin_users 表(超管 tenant_id 为空);token + 会话超时 + 登录失败限流 + 超管强制改初始密码。
|
||||
- art-design-pro 路由守卫/权限菜单映射两级角色:菜单级权限(超管可见租户管理/平台配置)+ 后端强制的数据级隔离(租户查询自动 scope)。
|
||||
|
||||
### 7.3 页面清单
|
||||
|
||||
| 页面 | 超管 | 租户管理员 |
|
||||
|---|---|---|
|
||||
| 仪表盘 | 租户卡片墙(今日会话/线索/推送成功率,ATTACH 跨库聚合)+ 平台总览 | 只看自己指标 |
|
||||
| 租户管理 | 创建向导(名称→域名→选出厂档案 auto/jiancai/空白→生成租户管理员)、停用/启用、域名绑定 | ✗ |
|
||||
| 会话与线索 | 顶部租户切换器,看所有租户会话/画像/推送日志 | 只看自己的 |
|
||||
| 配置中心 | 编辑任何租户全部配置组 | 编辑自己全部配置组(全开) |
|
||||
| 版本历史 | 快照列表、两版对比、一键回滚(回滚生成新版本) | 同 |
|
||||
| 平台配置 | 集团默认 AI 凭据、默认 SMTP、超管改密 | ✗ |
|
||||
|
||||
### 7.4 配置编辑形态
|
||||
|
||||
- 品牌:表单(颜色选择器、头像上传、文案)+ 挂件实时预览。
|
||||
- 提示词:markdown 编辑器(语法高亮),侧栏显示 `$DEFLECTION_POOL` 等注入变量当前值,保存必须留 comment。
|
||||
- 词表:结构化表格(每条正则一行:语言组/表达式/备注)+ 编译校验 + 内嵌试金石。
|
||||
- 邮件:SMTP 表单 + 发测试邮件;推送:目标配置 + 连通性探测(只验鉴权,不发真数据)+ 前缀唯一校验。
|
||||
|
||||
### 7.5 API 契约
|
||||
|
||||
后台 SPA 走 `/api/platform/*`(新增)与租户化 admin 数据端点;聊天引擎端点(`/ws/chat`、`/api/chat/*`、`/api/public/brand`)契约不变——widget 嵌入方完全无感。
|
||||
|
||||
## 8. 数据迁移
|
||||
|
||||
**scripts/extract_config.py**:从两个老项目提取首版配置快照,生成 seed_profiles/auto/ 与 seed_profiles/jiancai/(提示词拷 md;词表从 boundary_filter.py/conversation.py 提取正则话术转 JSON;品牌从 chat.html/widget.js/chat.js 三处硬编码 + .env 归一)。此后两个老项目只读。
|
||||
|
||||
**scripts/import_legacy.py**:老库 5 张表原结构导入对应租户库;**保留原 conversation_id**(external_key 连续,UniService 不产生重复线索);导入后对账报告(两会话数/消息数/推送日志数 + 最近 10 条会话抽查)。
|
||||
|
||||
**切换顺序**:新平台部署验证(不切域名)→ 导存量 → 租户域名反代从老容器切到新平台 → 观察一周(沿用 breach_rate 双向偏差观察法)→ 老项目归档下线。
|
||||
|
||||
## 9. 部署
|
||||
|
||||
```
|
||||
chat.agent.hnxinyouhe.com ─┐
|
||||
chat2.agent.hnxinyouhe.com ─┼→ 1Panel 各站点反代 → 127.0.0.1:8018 → all-kefu 容器
|
||||
admin.xxx.com(新) ─┘
|
||||
```
|
||||
|
||||
- 一个容器一个 upstream;compose 固定 `name: all-kefu`(互删根因根治——只剩一个 compose 项目);WS 升级头单套(426 教训);`/static/widget.js` no-cache(缓存教训);`--workers 1`。
|
||||
- 加租户 = 后台建租户 + 1Panel 加一条反代,不起容器。
|
||||
- 备份:platform.db(敏感件)+ tenants/*.db 每日拷贝 + SQLite backup API 快照,写入部署文档。
|
||||
- `/health` 升级:进程状态 + 各租户 engine 状态 + 域名表版本。
|
||||
|
||||
## 10. 测试策略
|
||||
|
||||
1. **机制测试全保留**:jc 的 230 项 + 上游独有测试,移植到统一 conftest——租户夹具(临时 platform.db + auto/jc 两个种子租户 + 各自租户库),断言基本不动;LLM 全 mock 原则不变。
|
||||
2. **多租户专项(改造的回归安全网)**:Host 路由(A 域名进 A、未知 Host 404);跨租户数据隔离(A 的 admin/API 摸不到 B 数据);配置热切换(保存新提示词→下一条消息生效;改词表→墙行为即时变);前缀唯一约束;快照深合并默认值;试金石判定;widget 客户端 key 租户前缀(静态断言);停用租户行为。
|
||||
3. **真实 LLM 验收**:真实智谱 Key + scripts/mock_uniservice.py 联调流程照旧(沿用 test-checklist 体系)。
|
||||
|
||||
## 11. 风险与对策
|
||||
|
||||
| 风险 | 对策 |
|
||||
|---|---|
|
||||
| conversation.py(1250 行核心编排器)租户化回归 | 230 项机制测试安全网 + 只做依赖注入不重写 |
|
||||
| admin 引入 node 构建链 | 两阶段 Docker 隔离,服务器零 node;widget/chat 零构建 |
|
||||
| 租户改坏提示词/词表 | 版本回滚 + 出厂重置 + 编译校验 + 试金石;话术池单一事实源消除双改遗漏 |
|
||||
| 迁移导致 CRM 重复线索 | 保留原 conversation_id + external_key 前缀不变 |
|
||||
| 单进程升级影响全部租户 | 秒级重启可接受;engine/ 与 platform/ 隔离保留拆分路径 |
|
||||
| 密钥入库 | 掩码显示、platform.db 按敏感件备份 |
|
||||
| 后台暴露面变大 | 登录失败限流、超管强制改初始密码、会话超时、admin 独立域名 |
|
||||
|
||||
## 12. 附录:两项目差异实证(配置 Schema 的来源)
|
||||
|
||||
| 差异点 | ai-kefu(auto) | ai-kefu-jc(建材) |
|
||||
|---|---|---|
|
||||
| 提示词 4 件套 | 汽车语义 | 管道建材语义(多语言墙、规格防误杀) |
|
||||
| 端口 / DB / 容器 | 8016 / cc_ai_kefu.db / cc-ai-kefu | 8017 / hesupipe_ai_kefu.db / hs-ai-kefu |
|
||||
| 品牌色 / 发件人 | 橙 #f08a31 / 【XYH】 | 蓝 #066fc0 / 【HESUPIPE】 |
|
||||
| context 种子键 | vehicle_name/vehicle_id | product_name/product_id |
|
||||
| 推送键前缀 | ai-kefu | ai-kefu-jc |
|
||||
| cc 实现 | whatsapp-cc-completion(旧) | cc_completion 判定树(新,主线) |
|
||||
| 独有功能 | input-placeholder 配置、blog.html(弃) | dual-contact、CC 强制墙、阿语数字归一 |
|
||||
| Redis | 有容器但代码零引用(弃) | 分叉时已移除 |
|
||||
Reference in New Issue
Block a user