Files
juzi/docs/HANDOFF-2026-11-02.md
T
xsl 43a5373233 docs: add development-machine handoff and server deploy configs
换开发机继续开发。新机器只需要拉仓库 + 自备工具链 + 单独拷贝两个机密文件,
服务器/聚石塔/SAE 全部沿用,不需要重建。

- docs/HANDOFF-2026-11-02.md:开发机交接(环境、仓库地图、三套测试、
  本地怎么跑、云端资源清单、上线方式、11 条关键结论、9 项待办、故障对照表)
- deploy/:服务器侧配置入库(原先只存在于机器上)
  - systemd/juzi-taobao.service(18081,独立数据目录与凭据文件)
  - nginx/tb-location.conf(/tb/ 反代 + 只放行聚石塔 NAT EIP)
  - add-tb-nginx.sh(幂等插入,先备份,nginx -t 通过才 reload)
  - README.md(重建与回滚步骤)
- tools/env-java.ps1:优先用仓库内便携工具链,缺失时回退 JAVA_HOME/PATH,
  换机器不用再下便携 JDK/Maven
- README.md:交接文档入口更新
2026-11-01 23:34:26 +08:00

302 lines
16 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.
# 开发交接 —— 换一台开发机继续做(2026-11-02)
> **写给**:在新 Windows 机器上接手的人(或未来的 agent)
> **不涉及**:服务器 / 聚石塔 / SAE 都不用重建,云端资源全部现成可用
> **上一份**:[HANDOFF-2026-11-01.md](HANDOFF-2026-11-01.md)(那一轮只到「链路打通 + Java MVP」,UI 和真机测肤是这一轮做的)
---
## 0. 一句话状态
**真机全链路已跑通**:淘宝小程序 → 云网关(AppKey 35486228)→ 聚石塔云应用 1me-net → 私网 CLB → SAE `nnn`(只做透传)→ 聚石塔 NAT 出网(EIP `47.92.113.176`)→ `https://1me.xajuzi.com/tb/` → 现有 H5 的 Node 服务 → 旷视 Face++,**三视图拍摄后报告正常返回**。
小程序五个页面的 UI 已按 H5 重做(像素级对齐,见下)。剩下的是相机组件验证和一批工程化工作,清单在第 8 节。
---
## 1. 新机器准备
### 1.1 必装
| 工具 | 版本 | 用途 |
|---|---|---|
| Git | 任意近期版本 | 拉代码 |
| Node.js | **18+(推荐 20/22)** | `taobao/` 测试、`h5/` 本地跑、零依赖 |
| JDK | **17** | `backend-java/` 编译与测试 |
| Maven | 3.9.x | 同上 |
| 淘宝小程序开发者工具 | 最新 | 真机预览(**模拟器不支持云应用 SDK**) |
| PowerShell 7(可选) | 7.x | 仓库里的脚本按 pwsh 语法写 |
### 1.2 拉代码
```powershell
git clone https://git.xiangsilian.com/xsl/juzi.git
cd juzi
```
> 远端证书有效期到 2027-01-29;凭据失效时重新配置 Git 凭据管理器。
### 1.3 小程序依赖(必做,否则真机报 `Cannot find module`)
```powershell
cd taobao
npm ci
```
`@tbmp/mp-cloud-sdk` 固定 1.5.10,只有这一个运行期依赖。
### 1.4 工具链
仓库自带 `tools/env-java.ps1`,查找顺序:仓库内便携工具链 → 环境变量 → PATH。
```powershell
Set-ExecutionPolicy -Scope Process Bypass
. .\tools\env-java.ps1 # 输出 JAVA_HOME / MAVEN_HOME / java -version
```
- 用系统安装的 JDK 17 + Maven 即可,**不需要**再下便携版
- 想复原老机器的布局:把 JDK 放 `tools/jdk17/`、Maven 放 `tools/maven/apache-maven-3.9.9/`(这两个目录 gitignore)
### 1.5 必须单独拷贝的机密文件(**不在 git 里**)
| 文件 | 内容 | 没有会怎样 |
|---|---|---|
| `backend-java/src/main/resources/application-local.yml` | 淘宝 AppKey/AppSecret + `mode: forward` + `forward-base` | JAR 里没有 AppKey/Secret,`/session` 一定 401 |
| `reference/facepp-skinanalyze/credentials.env` | 旷视 `FACEPP_API_KEY/SECRET` | `h5/server.js` 启动即 `exit(1)` |
| SSH 私钥 | 免密登录 `ubuntu@1me.xajuzi.com` | 无法部署 Node 侧 / 看日志 |
```powershell
# 从老机器拷(示例)
scp <老机器>:D:/juzi/backend-java/src/main/resources/application-local.yml .\backend-java\src\main\resources\
scp <老机器>:D:/juzi/reference/facepp-skinanalyze/credentials.env .\reference\facepp-skinanalyze\
```
新机器要能 ssh 服务器的话:`ssh-keygen -t ed25519` 后把公钥追加到服务器 `~/.ssh/authorized_keys`。
> 老机器若已不可用:AppSecret / 旷视 key 找项目负责人索取,或在开放平台重置(重置后记得同步更新服务器 `/etc/juzi/taobao.env` 与 JAR)。
### 1.6 淘宝 IDE 的一个已知坑(Windows)
老机器上 IDE 曾因 `wmic` 缺失打不开,用 `tools/fix-taobao-ide-wmic.ps1` 生成过一个 shim(`C:\Users\<你>\.local\bin\wmic.cmd`)。新机器如果 IDE 报 `install miniapp extension fail: ... wmic ...`,跑这个脚本。
---
## 2. 仓库地图
```
juzi/
├── taobao/ 淘宝小程序(当前主战场)
│ ├── config.js ★ 环境/传输配置(cloud+tower+test、assetBase)
│ ├── app.acss ★ 全局设计 token(对齐 H5 的 .btn-primary/.card/.chev/.popup)
│ ├── pages/
│ │ ├── splash/ 启动页(登录、隐私弹窗、开发版入口)
│ │ ├── profile/ 档案填写(出生/性别/地区/肤质轮播)
│ │ ├── camera/ 拍照页(蒙版取景 or 系统相机兜底)
│ │ ├── report/ 报告页(环形分、7 视角、叠加层、标记点)
│ │ ├── history/ 历史报告
│ │ ├── mvp/ 连通性调试页(ping/whoami/echo)
│ │ └── camtest/ ★ 相机组件冒烟页(只放 <camera>,判断组件是否可用)
│ ├── utils/
│ │ ├── transport.js 云网关/direct 两种传输 + 信封解包
│ │ ├── api.js 9 个接口封装(challenge/session/me/photo/skin-analyze/…)
│ │ ├── permissions.js ★ 权限(scope 前缀、诊断 diagnose())
│ │ ├── image.js 1280 长边 + JPEG q0.88 压缩
│ │ ├── voice.js 语音播报(异常静默)
│ │ └── report-model.js payload → 报告页 data
│ └── test/ 20 个 node:test 用例
├── h5/ ★ 真正的测肤后端(Node,零依赖)
│ ├── server.js 入口:Face++ 调用、评分、报告、admin
│ ├── lib/taobao-*.js ★ 淘宝云网关路由/验签/会话(关键算法在这里)
│ ├── js/skin-score.js 评分引擎(6 类 → 3 维,4:3:3 加权)
│ └── test/ 一堆独立测试脚本(没有统一 runner)
├── backend-java/ SAE 上的透传层(Spring Boot 3.5.6 / Java 17)
│ ├── src/main/java/com/juzi/mvp/api/
│ │ ├── TaobaoForwardController.java ★ mode=forward 时字节级透传
│ │ ├── TaobaoEnvelope.java 客户端要的 data.ok + data.http_status
│ │ ├── TaobaoAuthController.java native 模式的登录接口(当前未启用)
│ │ └── TaobaoSessionService.java challenge + Bearer token
│ └── src/main/java/com/juzi/mvp/gateway/TaobaoSignatureVerifier.java 验签算法
├── deploy/ ★ 服务器侧配置(systemd / nginx / 安装脚本)
├── docs/ 交接与实施文档(本文件所在)
└── tools/ 构建与素材脚本
```
---
## 3. 三套测试
```powershell
# 1) 小程序:20 个用例
cd taobao ; npm test
# 2) Java:11 个用例(含验签向量的回归测试)
cd backend-java ; mvn test
# 3) H5 单测(独立脚本,逐个跑)
node h5/test/taobao-auth.js # 网关签名,必须 PASS
```
**改动提交前请保证**:`taobao` 20/20 + `backend-java` 11/11。老机器最后一次全绿是在 `fed5293`。
> `taobao/test/frontend.test.cjs` 会用 vm 真跑页面 JS,页面 data 字段改名要同步改测试。
---
## 4. 本地怎么跑
### 4.1 H5 后端(Node,测肤真正的实现)
```powershell
cd h5
$env:PORT=8099 ; $env:HOST='127.0.0.1' ; $env:JUZI_ENTRY_MODE='state'
$env:JUZI_DATA_ROOT='..\temp\h5-data'
node server.js
# http://127.0.0.1:8099/api/health → {"ok":true}
```
启动时缺旷视 key 会直接退出。只想调 UI/接口时给假值即可(真实检测会失败,但路由可用):
```powershell
$env:SKIN_API_KEY='dummy' ; $env:SKIN_API_SECRET='dummy'
```
### 4.2 Java(SAE 那一层)
```powershell
. .\tools\env-java.ps1
cd backend-java
mvn spring-boot:run # 或 mvn -DskipTests package 后 java -jar target\*.jar
curl.exe -X POST -H "Content-Type: application/json" -d "{}" http://127.0.0.1:8080/api/taobao/ping
```
- 默认 `mode: native`(Java 自己实现登录,便于单独调接口)
- 部署用的 `application-local.yml` 里写的是 `mode: forward`
- 本地想看透传:`$env:TAOBAO_MODE='forward'` + `$env:TAOBAO_FORWARD_BASE='http://127.0.0.1:8099'`
### 4.3 小程序
1. 淘宝 IDE 导入仓库里的 `taobao/`(`mini.project.json` 的 `appid` 留空,以 IDE 账号为准)
2. **确认 IDE 当前小程序是 35486228(`1me 肌肤状态检测`)**
3. `config.js` 保持 `transport: 'cloud'` / `cloudType: 'tower'` / `cloudEnv: 'test'`
4. 顶部「预览」→ **手机淘宝扫码**(模拟器不支持 `cloud.application.httpRequest`)
5. 启动页右上角有三个**开发版入口**(`environment !== 'production'` 时才显示):
| 入口 | 作用 |
|---|---|
| 权限诊断 | 打印 `my.canIUse`、`getSetting` 的 authSetting、`authorize` 的原始返回(真机上唯一能看到这些的地方) |
| 相机组件测试 | 整页只有一个 `<camera>`,用来判断组件是否可用 |
| SAE MVP 连通性 | `ping/whoami/echo`,看 `transport:"gateway"`、`server:"java"` |
---
## 5. 云端资源(只读,无需重建)
| 资源 | 值 |
|---|---|
| 开放平台应用 | `1me 肌肤状态检测`,AppKey **35486228** |
| 旧应用(废弃) | `1me肌肤检测` 35479221 —— **不要再用它的 Secret** |
| 聚石塔云应用 | `1me-net`,ID **62829**,默认应用 |
| 私网 CLB | `lb-k2j324dwux1svaf2akwqx` / `192.168.65.185:80` |
| SAE 应用 | `nnn`,`cn-zhangjiakou:my-namespace` |
| 代码包 | `taobao-mvp-backend-0.0.4-forward.jar`(**环境变量全空**,配置都在包里) |
| VPC / vSwitch | `vpc-8vb5mcvenp8jwa1roqcsa` / `vsw-8vbna5zb2rq03gaeiymch`(可用区 A) |
| NAT 出口 EIP | **47.92.113.176**(nginx `/tb/` 白名单按它放行) |
| 服务器 | `ubuntu@1me.xajuzi.com`,Node v20.20.2 |
| 生产 H5 | `https://1me.xajuzi.com/state/` → `juzi-h5.service`(8080) |
| 红书渠道 | `https://1me.xajuzi.com/rednote/` → `juzi-rednote.service`(8081) |
| 淘宝专用 | `https://1me.xajuzi.com/tb/` → `juzi-taobao.service`(18081) |
---
## 6. 改完怎么上线
### 6.1 小程序
IDE 编译 → 预览(自测)→ 「上传」发版本。**不需要碰服务器。**
### 6.2 Java(SAE)
```powershell
. .\tools\env-java.ps1 ; cd backend-java
mvn clean package # 测试会自动跑
# 产物 target\taobao-mvp-backend-0.0.4-forward.jar
```
SAE 控制台 → `nnn` → 部署应用 → 代码包部署 → 上传 JAR(**版本号要和上次不同**,否则会被当成同一个版本)→ 环境变量保持全空。
### 6.3 Node(`/tb` 实例)
见 [deploy/README.md](../deploy/README.md) 第 1 节。核心三步:
```powershell
tar -czf temp/juzi-h5.tgz --exclude=h5/data --exclude=h5/test -C . h5
scp temp/juzi-h5.tgz ubuntu@1me.xajuzi.com:/tmp/
```
```bash
ssh ubuntu@1me.xajuzi.com "tar -xzf /tmp/juzi-h5.tgz -C /home/ubuntu/juzi-tb && sudo systemctl restart juzi-taobao"
```
> ⚠️ **不要动** `/home/ubuntu/juzi`(生产 H5)和 `/home/ubuntu/juzi-rednote`。我们那份是独立拷贝 `/home/ubuntu/juzi-tb`。
---
## 7. 这一轮的关键结论(踩过的坑,别重复踩)
1. **云网关签名算法**:真实请求**不带 `sign_method`**,缺省等价于 `MD5(secret + content + secret)`(`content` = 排序后的 `key+value` 拼接 + **原始 body 字节**)。之前按 `MD5(content + secret)` 实现,是 10 月底那个「signature mismatch」的真因。改在 [h5/lib/taobao-auth.js](../h5/lib/taobao-auth.js)。
2. **淘宝的 scope 是带前缀的** `scope.camera` / `scope.album`;传裸名 `camera` 返回 `{"errorCode":"5022","errorMessage":"Scope:camera信息不存在"}`。
3. **`<camera>` 组件在真机上会让整页崩溃**,且 `my.canIUse('camera')` 返回 `yes` 是**假阳性**。当前默认走系统相机(`config.nativeCamera = false`)。怀疑原因是相机里嵌了**微信专有的 `<cover-view>`**,已改成兄弟节点 + 普通 view 覆盖,并加了 `pages/camtest` 冒烟页——**这个还没在真机验证,是新机器的第一件事**。
4. **网关限制**:单请求 **10 秒 / 128KB**。所以 `skin-analyze` 必须 **202 + 1 秒轮询(最长 90 秒)**,不能同步等旷视。
5. **旷视 Face++**:正式 key 有并发窗口,必须**串行 + 4 秒间隔**;60 秒超时,失败/5xx/并发超限重试 1 次;返回值含 `face_maps`,4000px 素材时约 3.2MB(真实链路客户端压到 1280,约 200–300KB)。
6. **`/api/taobao/reports` 详情带 3 张 base64 照片**,历史报告页有超时风险(网关大响应未验证)。
7. **会话/任务全在内存**:SAE 只能单副本;`juzi-taobao` 重启后登录态与进行中的任务都会丢。
8. **没有任何清理策略**:每次检测在磁盘留 10–12MB,长期必须换 OSS。
9. **转发必须字节级透传**:签名覆盖原始 body 和 query,Java 一旦重新序列化 JSON 就会 401。
10. **生产那台 Node 是旧代码**,没有 `/api/taobao/*`;我们另外起 `/home/ubuntu/juzi-tb`(18081)跑新代码,用 nginx `/tb/` 分流。
11. **图片资源**走 `https://1me.xajuzi.com/state/assets/`(`config.assetBase`)。之前是 `127.0.0.1:18180`,真机上全部加载失败。
---
## 8. 待办(建议顺序)
| # | 事项 | 说明 |
|---|---|---|
| 1 | **真机验证 `pages/camtest`** | 能出实时画面 → 把 `config.nativeCamera` 改 `true`,拍照页立刻有蒙版取景;仍然崩 → 维持系统相机 |
| 2 | `/reports` 详情瘦身 | 现在透传 3 张 base64 原图(约 0.5MB+),改缩略图或 OSS URL |
| 3 | 存储工程化 | 照片/原始 JSON/记录 → OSS + RDS;现在全在容器/单机磁盘,无清理 |
| 4 | 旷视请求裁剪 | `return_maps` 请求 10 层但报告只用 6 层,可少传 4 层省带宽与落盘 |
| 5 | WebP 转码(可选) | 实测:6 层图 WebP q80 省 54%,叠 50% 缩放省 84%;**先真机验证 `<image>` 支持 WebP data URL** |
| 6 | 密钥治理 | `application-local.yml` / `/etc/juzi/taobao.env` 里的 AppSecret、JAR 内嵌 Secret → 改 KMS/保密字典,并**重置一次 AppSecret** |
| 7 | Java 端可选迁移 | `mode=native` 的登录接口已写好并测过(4 个用例),可把 auth 从 Node 搬到 Java;测肤仍留在 Node |
| 8 | 仓库收拾 | `sdk-java-35479221/`、三个 `.pptx`、`tools/build_taobao_proposal_*.py` 仍未跟踪,建议加 ignore 或删除 |
| 9 | 启用签名校验(Java 侧) | 当前 forward 模式下由 Node 验签;若切 `native` 或要 `/ping` 也验签,把 `verify-sign` 打开即可(算法已修复) |
---
## 9. 故障对照表
| 现象 | 原因 | 处理 |
|---|---|---|
| 真机 `cloud init failed` | IDE 当前 app 不是 35486228 / 云应用没设默认 | 确认 IDE 账号与 AppKey |
| `淘宝身份校验失败,请重新授权登录` | 云网关签名不匹配 | 检查服务器 `/etc/juzi/taobao.env` 的 AppSecret;算法见第 7 节第 1 条 |
| `请先配置淘宝云应用和 HTTPS 服务地址` | `cloudType` 不是 `tower` 或 `transport` 不是 `cloud` | 看 `config.js` |
| 页面一片灰没有图 | `assetBase` 指向 127.0.0.1 | 应为 `https://1me.xajuzi.com/state/assets/` |
| 点拍照整页崩(「系统遇到一点问题」) | `<camera>` 组件 | 保持 `nativeCamera: false`,用系统相机/相册 |
| 报告页白屏 | `payload` 为空或 `report-model` 抛错 | 看启动页「SAE MVP 连通性」是否 `server:"java"` |
| SAE 侧 `上游 403` | 容器出口 IP 不在 nginx `/tb/` 白名单 | `curl -s https://myip.ipip.net` 取新 IP,改 nginx 后 reload |
| `Cannot find module '@tbmp/mp-cloud-sdk'` | 没装依赖 / IDE 没构建 npm | `npm ci`,IDE「工具 → 构建 npm」 |
| Java 打包报找不到 `application-local.yml` | 机密文件没拷过来 | 见 1.5 |
---
## 10. 交接检查清单
- [ ] `npm ci`(taobao)跑过
- [ ] `tools/env-java.ps1` 能加载出 JDK17 + Maven
- [ ] `application-local.yml` 与 `credentials.env` 已从老机器拷来
- [ ] `taobao` 20/20、`backend-java` 11/11、`node h5/test/taobao-auth.js` PASS
- [ ] IDE 打开 `taobao/` 并确认 AppKey 是 35486228
- [ ] 真机预览能看到图片(说明资产域名通)
- [ ] 真机点「权限诊断」→ `authorize(scope.camera)=ok`
- [ ] 真机点「相机组件测试」→ 记录结果,决定 `nativeCamera` 开关
- [ ] 能 `ssh ubuntu@1me.xajuzi.com`,`systemctl is-active juzi-taobao` 为 active
---
**给接手者的第一句话**:先按第 1 节把机器装好、把两个机密文件拷过来,跑通三套测试;然后去真机验 `camtest`——那是唯一还悬着的技术问题,也决定拍照页最终形态。