diff --git a/README.md b/README.md index e37d8f5..6df7355 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,9 @@ - `h5/`:现有浏览器/微信公众号 H5、管理后台,以及共享后端 `server.js`。 - `taobao/`:淘宝原生小程序。开发、联调、测试说明见 [taobao/README.md](taobao/README.md)。 - `backend-java/`:SAE + 淘宝云网关的 Java 最小连通性 MVP;操作步骤见 [docs/SAE-Java-MVP-操作步骤.md](docs/SAE-Java-MVP-操作步骤.md)。 -- 当前交接:[docs/HANDOFF-2026-11-01.md](docs/HANDOFF-2026-11-01.md)。 +- 当前交接(**换开发机先读这份**):[docs/HANDOFF-2026-11-02.md](docs/HANDOFF-2026-11-02.md)。 +- 上一轮交接:[docs/HANDOFF-2026-11-01.md](docs/HANDOFF-2026-11-01.md)。 +- 服务器侧配置(systemd / nginx `/tb/` 白名单 / 安装脚本):[deploy/README.md](deploy/README.md)。 - `rednote/`:复用 H5 的持久浏览器身份入口,独立运行和存储。说明见 [rednote/README.md](rednote/README.md)。 - `h5/js/skin-score.js`、`report-ui.js`、`report-copy.js`:业务规则的源文件。小程序内的副本由同步脚本生成并检查哈希。 - `h5/lib/taobao-*.js`:淘宝网关验签、短期上传/异步任务和独立 API 路由。 diff --git a/deploy/README.md b/deploy/README.md new file mode 100644 index 0000000..85ba750 --- /dev/null +++ b/deploy/README.md @@ -0,0 +1,127 @@ +# deploy —— 1me.xajuzi.com 上的「淘宝专用」Node 实例 + +这份目录存放**不在 git 里自动生效**的服务器侧配置:systemd unit、nginx 片段、安装脚本。 +换机器/换人接手时,按本文重建即可。 + +## 拓扑 + +``` +淘宝小程序(taobao/,AppKey 35486228) + → 淘宝云网关(注入 app_key / open_id / sign,单请求 10s / 128KB 上限) + → 聚石塔云应用 1me-net(62829) → 私网 CLB 192.168.65.185:80 + → SAE 应用 nnn(taobao-mvp-backend-0.0.4-forward.jar,mode=forward,纯透传) + → 聚石塔 NAT 网关出网(EIP 47.92.113.176) + → https://1me.xajuzi.com/tb/ (nginx 白名单只放行上面这个 EIP) + → juzi-taobao.service(Node,127.0.0.1:18081,本文档这套配置) + → 旷视 Face++ skinanalyze_pro +``` + +生产 H5(`/state/`)和红书渠道(`/rednote/`)跑在同机另外两个 systemd 服务上,**8080/8081 端口,和本实例完全隔离**。 + +## 三个服务的分工 + +| 服务 | 端口 | 代码目录 | 用途 | +|---|---|---|---| +| `juzi-h5.service` | 8080 | `/home/ubuntu/juzi/h5` | 生产 H5(微信登录、报告、后台) | +| `juzi-rednote.service` | 8081 | `/home/ubuntu/juzi-rednote/current/h5` | 小红书渠道隔离实例 | +| `juzi-taobao.service` | 18081 | `/home/ubuntu/juzi-tb/h5` | **本目录新增**:淘宝小程序专用 | + +## 重建步骤 + +### 1. 代码 + +`/home/ubuntu/juzi-tb` 是**独立的一份 h5 代码**(不是生产目录的软链),来自本仓库的 `h5/`。 +部署方式(本地执行,把仓库里的 h5 打包传上去): + +```powershell +# 本地仓库根目录 +tar -czf temp/juzi-h5.tgz --exclude=h5/data --exclude=h5/test --exclude=h5/scripts -C . h5 +scp temp/juzi-h5.tgz ubuntu@1me.xajuzi.com:/tmp/ +``` + +```bash +# 服务器上 +sudo mkdir -p /home/ubuntu/juzi-tb /home/ubuntu/juzi-tb-data +sudo chown -R ubuntu:ubuntu /home/ubuntu/juzi-tb /home/ubuntu/juzi-tb-data +chmod 700 /home/ubuntu/juzi-tb-data +tar -xzf /tmp/juzi-h5.tgz -C /home/ubuntu/juzi-tb + +# 旷视凭据复用生产那份(gitignored,不随仓库走) +mkdir -p /home/ubuntu/juzi-tb/reference/facepp-skinanalyze +cp /home/ubuntu/juzi/reference/facepp-skinanalyze/credentials.env \ + /home/ubuntu/juzi-tb/reference/facepp-skinanalyze/credentials.env +chmod 600 /home/ubuntu/juzi-tb/reference/facepp-skinanalyze/credentials.env +``` + +> `h5/server.js` 启动时如果找不到旷视 key 会直接 `exit(1)`,所以这一步不能漏。 + +### 2. 淘宝凭据 + +```bash +sudo mkdir -p /etc/juzi +sudo tee /etc/juzi/taobao.env >/dev/null <<'EOF' +TAOBAO_APP_KEY=35486228 +TAOBAO_APP_SECRET=<35486228 的 AppSecret,向项目负责人索取> +EOF +sudo chmod 600 /etc/juzi/taobao.env && sudo chown root:root /etc/juzi/taobao.env +``` + +这个文件被 `h5/lib/taobao-auth.js` 读取,用于校验云网关签名。**没有它,`/session` 一律 401。** + +### 3. systemd + +```bash +scp deploy/systemd/juzi-taobao.service ubuntu@1me.xajuzi.com:/tmp/ +ssh ubuntu@1me.xajuzi.com "sudo install -m 644 -o root -g root /tmp/juzi-taobao.service /etc/systemd/system/ \ + && sudo systemctl daemon-reload && sudo systemctl enable --now juzi-taobao && systemctl is-active juzi-taobao" +``` + +### 4. nginx + +```bash +# 本地 +scp deploy/add-tb-nginx.sh deploy/nginx/tb-location.conf ubuntu@1me.xajuzi.com:/tmp/ + +# 服务器 +ssh ubuntu@1me.xajuzi.com "sed -i 's/\r\$//' /tmp/add-tb-nginx.sh && bash /tmp/add-tb-nginx.sh /tmp/tb-location.conf" +``` + +脚本是幂等的(检测到 `location /tb/` 就跳过),会先备份成 `juzi.bak-tb-<时间戳>`。 + +### 5. 验收 + +```bash +# 服务器本机(127.0.0.1 在白名单里) +curl -sk -H 'Host: 1me.xajuzi.com' https://127.0.0.1/tb/api/health +# 期望 {"ok":true} + +# 公网(应被白名单挡掉) +curl -s -o /dev/null -w '%{http_code}\n' https://1me.xajuzi.com/tb/api/health +# 期望 403 + +# 生产 H5 不受影响 +curl -s -o /dev/null -w '%{http_code}\n' https://1me.xajuzi.com/state/api/health +# 期望 200 +``` + +最后在聚石塔 SAE 容器里再验一次(这是唯一走 NAT 的路径): + +```bash +curl -s https://1me.xajuzi.com/tb/api/health # 期望 {"ok":true} +``` + +## 回滚 + +```bash +sudo systemctl disable --now juzi-taobao +sudo cp /etc/nginx/sites-available/juzi.bak-tb-<时间戳> /etc/nginx/sites-available/juzi +sudo nginx -t && sudo systemctl reload nginx +``` + +生产两个服务完全不受影响(端口、目录、数据都是独立的)。 + +## 数据 + +- 运行数据:`/home/ubuntu/juzi-tb-data`(700)—— 照片 `photos/`、原始返回 `results/`、记录 `records.jsonl`、账号 `accounts/` +- 与生产的 `/home/ubuntu/juzi/h5/data` 完全分开,两边用户/报告不互通 +- **没有任何自动清理**:每次检测约 10–12MB 常驻磁盘,长期要换 OSS + RDS diff --git a/deploy/add-tb-nginx.sh b/deploy/add-tb-nginx.sh new file mode 100644 index 0000000..ce1e946 --- /dev/null +++ b/deploy/add-tb-nginx.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# 幂等地把 deploy/nginx/tb-location.conf 插入 /etc/nginx/sites-available/juzi。 +# 只做「插入一段 location」,不改动任何已有 location;先备份,nginx -t 通过才 reload。 +# +# 用法(在 1me.xajuzi.com 上): +# scp deploy/add-tb-nginx.sh deploy/nginx/tb-location.conf ubuntu@1me.xajuzi.com:/tmp/ +# ssh ubuntu@1me.xajuzi.com "sed -i 's/\r$//' /tmp/add-tb-nginx.sh && bash /tmp/add-tb-nginx.sh" +set -euo pipefail + +CONF=/etc/nginx/sites-available/juzi +SNIPPET=${1:-/tmp/tb-location.conf} +STAMP=$(date +%Y%m%d%H%M%S) +BACKUP="$CONF.bak-tb-$STAMP" + +if grep -q 'location /tb/' "$CONF"; then + echo "SKIP: /tb/ 已存在,不重复插入" + exit 0 +fi +if [ ! -f "$SNIPPET" ]; then + echo "缺少片段文件: $SNIPPET" >&2 + exit 1 +fi + +sudo cp "$CONF" "$BACKUP" +echo "BACKUP: $BACKUP" + +sudo python3 - "$CONF" "$SNIPPET" <<'PY' +import sys + +conf_path, snippet_path = sys.argv[1], sys.argv[2] +text = open(conf_path, encoding='utf-8').read() +snippet = open(snippet_path, encoding='utf-8').read() + +block = "# BEGIN taobao-debug 2026-11-01\n" + snippet + "# END taobao-debug 2026-11-01\n\n" +marker = " location / {\n" +if marker not in text: + sys.exit("marker 'location / {' not found") +text = text.replace(marker, block + marker, 1) +open(conf_path, 'w', encoding='utf-8').write(text) +print("INSERTED") +PY + +echo "--- nginx -t ---" +sudo nginx -t +echo "--- reload ---" +sudo systemctl reload nginx +echo "RELOADED" diff --git a/deploy/nginx/tb-location.conf b/deploy/nginx/tb-location.conf new file mode 100644 index 0000000..041710a --- /dev/null +++ b/deploy/nginx/tb-location.conf @@ -0,0 +1,25 @@ +# 插入到 /etc/nginx/sites-available/juzi 的 HTTPS server { } 块内。 +# 作用:给淘宝小程序云网关一个专用入口 /tb/,反代到 juzi-taobao.service(127.0.0.1:18081)。 +# +# 为什么要 allow 白名单:/tb/ 暴露在公网,后面是真实旷视计费调用。 +# 只允许聚石塔 NAT 的出口 EIP(以及本机自测)访问,其余一律 403。 +# +# 注意:nginx 会剥掉 /tb/ 前缀,所以 /tb/api/taobao/session → Node 收到 /api/taobao/session, +# 与 h5/server.js 的路由完全一致。 +location = /tb { + return 301 /tb/; +} +location /tb/ { + # 聚石塔 NAT 网关绑定的 EIP。换环境时改这一行。 + allow 47.92.113.176; + # 本机自测(curl -H 'Host: 1me.xajuzi.com' https://127.0.0.1/tb/api/health) + allow 127.0.0.1; + deny all; + + proxy_pass http://127.0.0.1:18081/; + 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_read_timeout 150s; +} diff --git a/deploy/systemd/juzi-taobao.service b/deploy/systemd/juzi-taobao.service new file mode 100644 index 0000000..cf825ab --- /dev/null +++ b/deploy/systemd/juzi-taobao.service @@ -0,0 +1,29 @@ +[Unit] +Description=1me taobao mini program backend (Node, /tb/ debug instance) +After=network.target + +[Service] +Type=simple +User=ubuntu +WorkingDirectory=/home/ubuntu/juzi-tb/h5 +# 独立于生产 juzi-h5(8080)/juzi-rednote(8081):独立端口、独立数据目录、独立凭据文件 +Environment=JUZI_ENTRY_MODE=state +Environment=NODE_ENV=production +Environment=HOST=127.0.0.1 +Environment=PORT=18081 +Environment=JUZI_DATA_ROOT=/home/ubuntu/juzi-tb-data +Environment=TRUST_LOOPBACK_PROXY=1 +# 内容格式: +# TAOBAO_APP_KEY=35486228 +# TAOBAO_APP_SECRET=<35486228 的 AppSecret> +# 权限 600 root,绝不入库。 +EnvironmentFile=/etc/juzi/taobao.env +ExecStart=/usr/bin/node /home/ubuntu/juzi-tb/h5/server.js +Restart=on-failure +RestartSec=3 +NoNewPrivileges=true +PrivateTmp=true +UMask=0077 + +[Install] +WantedBy=multi-user.target diff --git a/docs/HANDOFF-2026-11-02.md b/docs/HANDOFF-2026-11-02.md new file mode 100644 index 0000000..c04e609 --- /dev/null +++ b/docs/HANDOFF-2026-11-02.md @@ -0,0 +1,301 @@ +# 开发交接 —— 换一台开发机继续做(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/ ★ 相机组件冒烟页(只放 ,判断组件是否可用) +│ ├── 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` 的原始返回(真机上唯一能看到这些的地方) | +| 相机组件测试 | 整页只有一个 ``,用来判断组件是否可用 | +| 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. **`` 组件在真机上会让整页崩溃**,且 `my.canIUse('camera')` 返回 `yes` 是**假阳性**。当前默认走系统相机(`config.nativeCamera = false`)。怀疑原因是相机里嵌了**微信专有的 ``**,已改成兄弟节点 + 普通 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%;**先真机验证 `` 支持 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/` | +| 点拍照整页崩(「系统遇到一点问题」) | `` 组件 | 保持 `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`——那是唯一还悬着的技术问题,也决定拍照页最终形态。 diff --git a/tools/env-java.ps1 b/tools/env-java.ps1 index adb96ba..b64d2a1 100644 --- a/tools/env-java.ps1 +++ b/tools/env-java.ps1 @@ -1,26 +1,42 @@ -# 在当前 PowerShell 会话中加载 JDK 17 + Maven 3.9.9 -# 用法:. D:\juzi\tools\env-java.ps1 +# 在当前 PowerShell 会话中加载 JDK 17 + Maven +# 用法:. <仓库根>\tools\env-java.ps1 +# +# 查找顺序: +# 1) 仓库内便携工具链 tools\jdk17\* + tools\maven\apache-maven-3.9.9(不入库,换机器需自备) +# 2) 已导出的环境变量 $env:JAVA_HOME / $env:MAVEN_HOME +# 3) PATH 上的 java / mvn(例:scoop/choco/winget 安装的) $ErrorActionPreference = 'Stop' $projectRoot = Split-Path -Parent $PSScriptRoot -$jdkRoot = Join-Path $projectRoot 'tools\jdk17' -$jdkCandidates = @($jdkRoot) -if (Test-Path $jdkRoot) { - $jdkCandidates += Get-ChildItem $jdkRoot -Directory -ErrorAction SilentlyContinue | - ForEach-Object { $_.FullName } + +function Find-Jdk { + $root = Join-Path $projectRoot 'tools\jdk17' + $candidates = @() + if (Test-Path $root) { + $candidates += $root + $candidates += Get-ChildItem $root -Directory -ErrorAction SilentlyContinue | ForEach-Object { $_.FullName } + } + if ($env:JAVA_HOME) { $candidates += $env:JAVA_HOME } + return $candidates | Where-Object { $_ -and (Test-Path (Join-Path $_ 'bin\java.exe')) } | Select-Object -First 1 } -$jdkHome = $jdkCandidates | - Where-Object { Test-Path (Join-Path $_ 'bin\java.exe') } | - Select-Object -First 1 +function Find-Maven { + $portable = Join-Path $projectRoot 'tools\maven\apache-maven-3.9.9' + if (Test-Path (Join-Path $portable 'bin\mvn.cmd')) { return $portable } + if ($env:MAVEN_HOME -and (Test-Path (Join-Path $env:MAVEN_HOME 'bin\mvn.cmd'))) { return $env:MAVEN_HOME } + $onPath = Get-Command mvn -ErrorAction SilentlyContinue + if ($onPath) { return Split-Path -Parent (Split-Path -Parent $onPath.Source) } + return $null +} +$jdkHome = Find-Jdk if (-not $jdkHome) { - throw "JDK 17 not found under $jdkRoot. Run the portable JDK setup first." + throw "找不到 JDK 17:请安装 JDK 17 并设置 JAVA_HOME,或把便携版放到 tools\jdk17\" } -$mavenHome = Join-Path $projectRoot 'tools\maven\apache-maven-3.9.9' -if (-not (Test-Path (Join-Path $mavenHome 'bin\mvn.cmd'))) { - throw "Maven not found at $mavenHome. Run the portable Maven setup first." +$mavenHome = Find-Maven +if (-not $mavenHome) { + throw "找不到 Maven:请安装 Maven 3.9+ 并放到 PATH,或放便携版到 tools\maven\apache-maven-3.9.9" } $env:JAVA_HOME = $jdkHome @@ -29,5 +45,7 @@ $env:PATH = "$jdkHome\bin;$mavenHome\bin;$env:PATH" Write-Host "JAVA_HOME=$env:JAVA_HOME" Write-Host "MAVEN_HOME=$env:MAVEN_HOME" +# java/mvn 往 stderr 写版本信息,在 ErrorActionPreference=Stop 下会被当成终止错误,这里放宽 +$ErrorActionPreference = 'Continue' java -version mvn -version