Files
koc-loop/docs/KOC LOOP 私有化部署指南.md
ABAPPLO 9697b5890d feat: 任务发布到企微客户群(企业群发)与资源库单条新增
- 任务中心新增「发布到企微群」:同步客户群清单(wecom_group_chats)、
  按群主分组创建企业群发任务(add_msg_template)、发送记录落库
  wecom_group_pushes,迁移 mysql/0009;lib/wecom-client.ts 补
  listCustomerGroupChats/createGroupMsgTemplate
- KOC 资源库支持单条新增:app/api/resources-insert + lib/resource-write,
  拆出资源写入公共逻辑供导入复用;0008 补合作方外部联系人字段
- 环境变量示例补企微凭证与 SEED_DEMO_DATA;next.config 增加
  allowedDevOrigins;CLAUDE.md 补充项目说明
- .gitignore 排除 .codegraph/ 与 .ipynb_checkpoints/
2026-08-20 15:16:18 +08:00

14 KiB
Raw Blame History

KOC LOOP 私有化部署指南

本文适用于 main 分支。目标架构是运维提出的Nginx 代理 + KOC 服务 + MySQL 数据库,并让后台、外部 KOC 领取页和 Agent MCP 都能通过一个公网域名访问。

1. 部署形态

组件 容器 作用 持久化
Nginx nginx 公网入口、反向代理、托管 KOC 领取页 配置随镜像
KOC 服务 app Next.js 后台、API、MCP、每天 09:00 自动采集 上传目录挂载卷
MySQL 8 mysql 任务、笔记、领取、回填、账号、采集和用户数据 MySQL 数据卷

访问路径:

  • https://你的域名/:运营后台;
  • https://你的域名/koc/:外部 KOC 领取和回填;
  • https://你的域名/api/mcpAgent MCP
  • https://你的域名/api/health:服务健康检查。

第一版按单实例 KOC 服务设计。MySQL 和上传目录均持久化。以后需要横向扩容时,可以把上传卷换成共享 NAS对象存储接口已与业务代码分离。

2. 服务器要求

  • Linux 服务器一台,建议至少 4 核、8 GB 内存、100 GB 数据盘;
  • Docker Engine 24+
  • Docker Compose v2
  • 可解析到服务器或负载均衡的公网域名;
  • HTTPS 证书由公司网关、负载均衡或 Nginx 统一终止;
  • 服务器可以访问飞书 OpenAPI 和正式数据采集 MCP。

服务器只需对公网开放 80/443。MySQL 不映射公网端口。

3. 准备代码与环境变量

git clone ssh://git@gta.gbotai.cn:42001/wufengping/koc-loop.git
cd koc-loop
git checkout main
cp .env.self-hosted.example .env.self-hosted

编辑 .env.self-hosted。必须替换所有 replace-with-* 占位值:

变量 用途
MYSQL_ROOT_PASSWORD MySQL root 密码,仅数据库容器使用
MYSQL_PASSWORD KOC 服务的数据库密码
APP_ORIGIN 后台公网地址,如 https://koc.example.com
KOC_PORTAL_URL 领取页完整地址,如 https://koc.example.com/koc/
SUPER_ADMIN_USERNAME 首次启动创建唯一超级管理员
SUPER_ADMIN_PASSWORD 首次启动的超级管理员初始密码,至少 8 位
ADMIN_INTERNAL_TOKEN 内部管理调用密钥
KOC_MCP_API_KEY Agent 调用 KOC LOOP MCP 的独立 Bearer 密钥
FEISHU_APP_ID / FEISHU_APP_SECRET 读取飞书内容表和配图
AI_TOOL_CENTER_MCP_URL / AI_TOOL_CENTER_MCP_KEY 小红书公开数据采集服务
ENABLE_SCHEDULER 是否启用每天 09:00 自动采集,生产保持 true
WECOM_CORP_ID / WECOM_AGENT_ID / WECOM_SECRET 企业微信自建应用,用于临期催办的应用消息推送;三项需同时填写,全部留空则只走群机器人
WECOM_ROBOT_WEBHOOK 企业微信群机器人 webhook用于当日催办汇总
WECOM_NOTIFY_DUE_DAYS 临期阈值,默认 3超过则不在催办可填 130
WECOM_NOTIFY_ENABLED 企业微信通知总开关,默认 true;临时关闭设为 false

企业微信变量全部选填:不填则临期催办与汇总都跳过,不影响其他功能。所有变量都可以在「合作方」页面顶部「企业微信连通性」面板查看就绪状态,并用「测试群机器人」「测试发送」按钮验证。

密钥必须由密码管理器生成,禁止提交到 Git、聊天、部署日志或 URL。三个业务密钥 ADMIN_INTERNAL_TOKENKOC_MCP_API_KEYAI_TOOL_CENTER_MCP_KEY 不得复用。

APP_ORIGIN 必须填写用户实际访问的 HTTPS 公网地址,不能填写 localhostapp:3000 或其他容器内部地址。Excel 中的视频下载链接会优先使用这个地址;前置网关还必须把原始 HostX-Forwarded-HostX-Forwarded-Proto 传给仓库内的 Nginx。

4. 首次启动

docker compose --env-file .env.self-hosted \
  -f docker-compose.self-hosted.yml up -d --build

应用容器启动时会先执行同一套数据库迁移脚本,成功后才启动 KOC 服务。查看状态:

docker compose --env-file .env.self-hosted \
  -f docker-compose.self-hosted.yml ps

curl -fsS http://127.0.0.1:${HTTP_PORT:-80}/api/health

健康检查应返回 status: okdatabase: true

首次打开后台登录页时,系统会根据环境变量创建唯一超级管理员。创建成功后,可从运行环境移除 SUPER_ADMIN_PASSWORD 的明文值并重启应用;后续账号与密码统一在“用户管理”中维护。

5. Nginx 与 HTTPS

仓库内 deploy/nginx/koc-loop.conf 默认监听容器 80 端口,适合前置公司网关或负载均衡终止 HTTPS。

如果证书直接挂在本机 Nginx

  1. 把证书和私钥以只读卷挂载进 nginx 容器;
  2. 增加 443 listen ... ssl 配置;
  3. 80 端口只做 301 跳转;
  4. 保留 /koc/ 静态规则、/api/mcp 长连接规则和 / 反向代理规则。

MCP 路由已经关闭代理缓冲并将超时时间延长到 1 小时,避免 Agent 的长调用被 Nginx 提前截断。仓库内 Nginx 同时将上传限制设为 85 MB用于接收最多 80 MB 的批量回填 Excel公司网关或负载均衡的请求体限制也必须不低于 85 MB。

视频下载接口必须经过 /api/partner-image 反向代理,正常响应应包含 Content-Type: video/mp4 和带 .mp4 文件名的 Content-Disposition: attachment。不要在网关层改写该响应类型或移除附件响应头。

6. 迁移原 Sites 数据

迁移分为数据库与图片两部分。先在原生产站点保持只读窗口,完成导出后再切换域名,避免新旧系统同时写入。

6.1 D1 数据导入 MySQL

把 D1 各表导出成一个 JSON 文件,结构如下:

{
  "tables": {
    "partners": [{ "id": "...", "name": "..." }],
    "tasks": [{ "id": "...", "name": "..." }],
    "contents": [],
    "accounts": [],
    "claims": [],
    "delegation_bundles": [],
    "distributions": [],
    "collection_runs": [],
    "users": [],
    "auth_sessions": [],
    "mcp_export_tokens": []
  }
}

先确保 MySQL 迁移已完成,再在应用环境中运行:

npm run db:import-json -- /backup/koc-d1-export.json

导入脚本按业务依赖顺序写入,并使用主键/唯一键安全更新已有记录。正式迁移前先在测试库演练并核对任务数、笔记数、领取数、发布数和账号数。

6.2 R2 媒体文件导入

把 R2 按原对象 key 导出到一个目录,目录层级必须保留,例如:

content-assets/...
content-videos/...
publish-evidence/...
creator-center/...

运行:

UPLOAD_DIR=/data/koc/uploads \
  npm run storage:import -- /backup/koc-r2-export

脚本会复制文件,并为缺少元数据的媒体文件生成 Content-Type 元数据。容器部署时也可以在宿主机临时挂载 upload_data 卷后执行。

7. 上线验收

必须逐项验证:

  1. 超级管理员可以登录,普通用户看不到 KOC 资源模块;
  2. 用真实飞书表格创建一个 1 篇测试任务;
  3. 返回的领取链接以 /koc/?task= 开头;
  4. 手机公网打开领取页,能查看正文与配图;
  5. 回填短链/长链、上传发布截图、刷新后记录仍在;
  6. 上传创作者截图并填写曝光量、阅读量;
  7. 后台立即采集一篇笔记成功;
  8. 保存次日采集计划,确认数据库产生 collection_runs
  9. 图文任务导出的 Excel 内能直接看到原图和截图;
  10. 视频任务导出的 Excel 不含“图片”列,包含“视频”列,点击链接能下载扩展名为 .mp4 且可正常播放的文件;
  11. 批量回填 Excel 可以上传,发布链接、笔记截图和单篇笔记数据分析截图均能正确回写;
  12. Agent 用 KOC_MCP_API_KEY 调用 /api/mcp 能发现全部工具;
  13. 重启全部容器后数据、图片和视频不丢失。

8. 备份与恢复

每天至少备份:

  • MySQLmysqldump --single-transaction
  • mysql_data 卷;
  • upload_data 卷;
  • 当前 Git 提交号和脱敏后的环境变量清单。

备份必须复制到另一台机器或对象存储,不能只保存在部署服务器。恢复演练至少每季度一次。

9. 升级与回滚

升级前先备份数据库和上传卷:

git pull
docker compose --env-file .env.self-hosted \
  -f docker-compose.self-hosted.yml up -d --build

应用容器每次启动都会按文件名顺序执行尚未应用的 mysql/*.sql。本次版本包含平台/视频字段、账号性别/简介/标签以及“当前联系人”字段的增量迁移;升级后应检查容器日志确认 000500060007 已执行或已被识别为历史迁移。

数据库迁移只允许向前追加新的 mysql/*.sql 文件,禁止修改已经在生产执行过的迁移。应用回滚到旧镜像前,要确认旧代码兼容当前数据库结构;涉及不可逆结构变化时,必须同时准备数据库恢复方案。

10. 运维排查

现象 处理
/api/health 返回 503 查看 MySQL 容器健康状态与应用数据库变量
登录页可开但登录失败 确认迁移完成、超级管理员变量仅用于初始化
配图或截图 404 检查 upload_data 卷和 UPLOAD_DIR=/data/koc/uploads
Excel 视频链接出现 localhost 或无法访问 检查 APP_ORIGIN、公网域名和网关转发的 Host/Proto 请求头
视频下载后不是 MP4 或无法播放 检查 /api/partner-image 是否经过应用代理、文件是否完整,以及网关是否保留 Content-Type/Content-Disposition
批量回填表上传返回 413 将公司网关、负载均衡和 Nginx 的请求体限制统一提高到至少 85 MB
09:00 未自动采集 检查 ENABLE_SCHEDULER=true、服务器日志和采集 MCP 网络
MCP 401 检查请求头是否为 Authorization: Bearer <KOC_MCP_API_KEY>
飞书读取失败 检查应用权限、文档授权和服务器到飞书 OpenAPI 的网络

生产日志不得打印数据库密码、飞书 Secret、MCP key 或完整带 key 的采集服务 URL。

11. 企业微信自建应用与群机器人配置

KOC LOOP 的企业微信通知是单向外发:每天 09:00 与定时采集一同触发,给合作方绑定的外部联系人推送临期催办,再向群机器人发送当日汇总。不需要 OAuth 回调,也不需要拉通讯录。

11.1 群机器人(用于当日催办汇总)

  1. 在企业微信里建立一个用于催办的群;
  2. 群设置 → 群机器人 → 添加机器人 → 复制 webhook 地址,形如 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=...
  3. 把整个 webhook 写入 WECOM_ROBOT_WEBHOOK

只要这一项就绪,每天 09:00 的催办汇总就会推到该群。

11.2 自建应用(用于给单个合作方发应用消息)

  1. 登录企业微信管理后台 → 应用管理 → 自建 → 创建应用,名称建议 KOC LOOP 催办
  2. 应用详情页记下 AgentId,填入 WECOM_AGENT_ID
  3. 我的企业 → 企业信息 → 企业 ID填入 WECOM_CORP_ID
  4. 应用详情页 → Secret → 发送 Secret 到管理端,填入 WECOM_SECRET
  5. 应用「可见范围」必须包含合作方对应的企业微信成员,否则推送时会返回 invaliduser

11.3 绑定外部联系人

企业微信应用消息推送需要 external_userid(外部联系人 IDKOC LOOP 不自动拉取,需要手动绑定:

  1. 在企业微信「客户联系 → 外部联系人」里找到合作方对应的客户;
  2. 复制其 external_userid形如 woxxxxxx
  3. 在 KOC LOOP 后台「合作方」页面,找到对应合作方行,点「绑定」粘贴 ID → 「保存」;
  4. 点「测试发送」验证;如果返回 应用消息发送失败,多半是应用可见范围未包含该外部联系人,或 external_userid 复制错了。

外部联系人 ID 不入库后不会自动同步企业微信端的变更;如果合作方的外部联系人在企业微信里被删除或转移,需要回来更新这一列。

11.4 验证

部署后到后台「合作方」页面,顶部「企业微信连通性」面板应显示三项就绪状态:群机器人、自建应用消息、临期阈值。逐项点「测试」:

  • 「测试群机器人」:群内收到 [KOC LOOP 测试] 群机器人连通性正常,时间 ... 即配置成功;
  • 合作方行的「测试发送」:对应外部联系人收到同样格式的测试消息即绑定正确。

如果面板显示「未配置」但已填环境变量,先确认容器加载了新的 .env.self-hosted(重启服务),再回到该页面点「刷新状态」。

11.5 任务发布到企微客户群(企业群发)

平台支持把任务以「企业群发」方式推送到多个外部客户群。企业微信不允许外部群添加群机器人 webhook,官方路径是半自动群发:平台创建群发任务 → 各群的群主在企业微信客户端「群发助手」里点击发送 → 消息才送达客户群。依赖 11.2 的同一个自建应用,无需新增环境变量;数据库表 wecom_group_chats / wecom_group_pushes 由迁移 mysql/0009_wecom_group_push.sql 创建(npm run db:migrate 自动执行)。

使用前需在企业微信管理后台完成三项配置:

  1. 客户联系 → 配置 → 可调用应用:把该自建应用加入可调用列表,否则客户群相关接口会报权限错误;
  2. 应用可见范围:必须包含所有目标群的群主(群主是群发任务的确认人);
  3. 应用可信 IP(应用详情页 → 开发者接口 → 企业可信 IP必须包含服务器出口 IP否则接口返回 60020 not allow to access from your ip

使用流程:后台「任务中心」→ 任务行「发布到企微群」→ 弹窗内「同步群列表」(拉取客户联系下的正常状态客户群)→ 勾选目标群、确认文案 → 「创建群发」。创建成功后平台返回 msgid 并落库 wecom_group_pushes,各群主在企微客户端收到群发助手提醒,点击发送后消息(含 KOC 领取链接)才出现在群里。

限制与注意:每个客户群每月最多接收「当月天数」条企业群发;群主使用的企业微信客户端需 ≥4.1.10 才能免选群直接发送;无效的 chat_id 会进入失败列表但不影响其他群。