Files
huihuiSquare/digital-avatar-app/docs/H5_PRODUCTION_DEPLOYMENT.md

7.7 KiB

数字分身 H5 生产接入与部署

1. 接入方式

生产会会在用户已登录后打开以下地址:

https://digital.99hui.com/#/avatar/manage?token=<encodeURIComponent(会会 access token)>

测试环境示例:

http://192.168.1.188:8099/#/avatar/manage?token=<encodeURIComponent(token)>

兼容参数位于域名查询串的形式,但生产统一使用上面的 hash 路由形式。必须对 token 调用 encodeURIComponent,不能拼接用户 ID 代替 token。

免登录流程如下:

  1. H5 在页面渲染前读取 token,立即通过 history.replaceState 从地址栏和浏览器历史中移除。
  2. H5 调用 POST /api/huihui/token/login,不会把会会 token 当作数字分身接口 token 直接使用。
  3. 后端通过会会生产接口 /im/box/netease 换取 BOXIM 凭证,再调用 BOXIM /user/self 校验用户身份。
  4. 后端以返回的 huihuiUserId 绑定本地用户,保存会会凭证供 BOXIM 接管功能使用,并签发本系统 app_token。
  5. 浏览器只保存 app_token 和非敏感用户资料。会会原始 token 不返回浏览器存储。
  6. token 无效、过期或上游校验失败时清除旧会话并进入登录页,不会沿用上一位用户的缓存身份。

2. 生产配置

后端 .env 至少配置以下内容,密钥由部署平台注入,禁止提交 Git:

HUIHUI_DEV_MOCK=false
HUIHUI_AUTH_BASE_URL=https://99hui.com/api/usercenter
HUIHUI_PLATFORM_BASE_URL=https://open.99hui.com/api
BOXIM_API_BASE_URL=https://im.99hui.com/api
HUIHUI_APP_ID=<production-app-id>
HUIHUI_ACCESS_ID=<production-access-id>
HUIHUI_ACCESS_SECRET=<production-access-secret>
HUIHUI_CLIENT_CODE=<production-client-code>
BOXIM_TIMEOUT_SECONDS=20
BOXIM_POLL_CONCURRENCY=8
BOXIM_MAX_MESSAGE_AGE_SECONDS=600
HUIHUI_PAYMENT_BASE_URL=https://open.99hui.com/api/payment-v3
HUIHUI_PAYMENT_CALLBACK_BASE_URL=https://digital.99hui.com
HUIHUI_PAYMENT_CALLBACK_SECRET=<至少32位随机密钥>
HUIHUI_PAYMENT_TIMEOUT_SECONDS=30

DATABASE_URL=sqlite:////data/avatar.db
UPLOAD_DIR=/data/uploads
CHAT_MODEL_CONFIG_URL=http://<huihuisquare-api>/api/ai-models/runtime/digital-avatar
EMBEDDING_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
EMBEDDING_API_KEY=<production-embedding-api-key>
EMBEDDING_MODEL=text-embedding-v3
EMBEDDING_BATCH_SIZE=10

VISION_MODEL=qwen3.6-flash
VISION_OCR_MODEL=qwen-vl-ocr
VISION_MAX_OUTPUT_TOKENS=2048
VISION_TIMEOUT_SECONDS=90
VISION_TOKEN_RESERVE=12000
CHAT_IMAGE_MAX_BYTES=8388608
CHAT_IMAGE_MAX_PIXELS=16000000
CHAT_ATTACHMENT_RETENTION_HOURS=24
CHAT_ATTACHMENT_CLEANUP_MINUTES=60

如生产 AI 配置中心不可用,还应提供当前项目支持的 OPENAI_API_KEY、OPENAI_BASE_URL、CHAT_MODEL 等兜底配置。/data 必须挂载持久卷,数据库与知识库文件不可存放在容器临时层。

EMBEDDING_API_URL 同时支持 OpenAI 兼容基础地址(如上面的 /v1)和完整的 /v1/embeddings 地址,后端会统一请求 /embeddings。发布后必须在后端容器内执行一次最小向量探针,确认返回向量数量和维度,而不能只检查 /api/health。

积分充值使用会会支付体系的 payment-v3/payment/pay,渠道值为 WECHAT / ALIPAY,端内支付场景为 APP,微信内 H5 使用 JSAPI。HUIHUI_PAYMENT_CALLBACK_SECRET 只用于为每笔订单生成 HMAC 回调签名,不会发送到前端或直接出现在回调地址中。支付回调确认状态成功且金额与套餐价格完全一致后才增加积分,重复回调不会重复到账。

3. 构建与发布

首次发布前备份数据:

BACKUP_DIR="backups/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp /srv/digital-avatar/data/avatar.db "$BACKUP_DIR/"
tar -C /srv/digital-avatar/data -czf "$BACKUP_DIR/uploads.tgz" uploads

在发布目录执行:

git fetch origin
git checkout <已验收的提交SHA>
cd digital-avatar-app
docker compose build --pull avatar-backend avatar-frontend
docker compose up -d avatar-backend avatar-frontend
docker compose ps
curl -fsS http://127.0.0.1:8099/api/health
docker compose exec avatar-backend python -c 'import embeddings; v=embeddings.embed(["部署向量探针"]); print(len(v), len(v[0]))'

生产编排应把示例中的测试端口改为内网暴露,由统一 HTTPS 网关接入。后端暂时使用 SQLite,必须保持单实例写入;若扩展为多后端实例,应先迁移到 PostgreSQL,并把延迟接管任务改为共享队列。

4. 网关要求

必须使用 HTTPS。同域部署时,H5 静态资源与 /api/ 由同一域名提供,可避免跨域和 Cookie/来源策略问题。Nginx 关键配置示例:

server_name digital.99hui.com;

location / {
    try_files $uri $uri/ /index.html;
}

location /api/ {
    proxy_pass http://avatar-backend:8000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 300s;
    client_max_body_size 100m;
}

proxy_buffering off 用于数字分身 SSE 流式吐字,client_max_body_size 同时用于知识库文件和聊天图片上传。应用只保存图片识别结果,不保存原图;识别结果 24 小时失效,后台默认每小时清理一次。公开分享图片识别会消耗分身所有者积分,生产网关应针对 /api/public/avatar/*/chat/images 设置每 IP 和每分享令牌的上传频率限制,防止恶意消耗。

网关和应用日志必须关闭完整 URL 查询参数记录,任何异常日志都不得输出 token、Authorization、图片 Base64、病例正文或平台密钥。建议同时设置严格的 Referrer-Policy: no-referrer。

5. 发布验收

  1. 已登录会会用户通过带 token 链接打开后直接进入 /avatar/manage,不出现登录页或创建新账号页。
  2. 页面加载后地址栏中不再包含 token,刷新页面仍使用本地 app_token 正常访问。
  3. 后端用户绑定的是 BOXIM 返回的 huihuiUserId,不是 BOXIM 内部 id;原有数字分身、独立知识库和积分余额均存在。
  4. A、B 两个会会用户分别进入时只能看到各自的数字分身与知识库,不会继承上一用户缓存。
  5. 使用过期或伪造 token 时进入登录页并显示凭证失效,不得继续访问旧用户数据。
  6. 分身聊天 SSE 逐段输出正常,Markdown 正常渲染,知识库优先级和积分扣费正常。
  7. 开启 BOXIM 主动接管后保持在线,默认三分钟回复、自定义等待时间、已读回执、分身防回环和主人发言暂停均正常。
  8. 重建容器后数据库、头像、知识库文档仍存在,/api/health 返回成功。
  9. https://digital.99hui.com/api/health 可访问,证书域名和有效期正确,HTTP 自动跳转 HTTPS。
  10. 微信和支付宝各创建一笔最小套餐订单,未付款时积分不变;支付成功后回调到账一次,重复回调积分不重复增加。
  11. 私聊和公开分享各上传 JPG、PNG、WebP 图片并完成追问;上传非图片、超过 8MB 或跨分身附件时必须拒绝。
  12. 病例图片可以提取可见文字并标记待核对内容,医学影像不作确定诊断;视觉与 OCR 调用分别扣减积分。
  13. 检查服务器上传目录不残留聊天原图,数据库过期图片识别记录在清理周期后删除,日志不出现 Base64 或病例正文。

6. 回滚

保留上一版前后端镜像标签和发布前数据库/上传文件备份。代码回滚优先切回上一镜像;只有新版本执行了不可逆数据变更时才恢复数据库。恢复前先停止后端写入,恢复后对比用户数、分身数、知识库文档数并完成一次免登录和聊天验收。