shinnosuke
4 天以前 86d6eb7a53efbdf5c0b68a34342d8064744f504e
更新....
2个文件已修改
85 ■■■■■ 已修改文件
.scratch/server-testenv/README.md 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika/pages/index/index.uvue 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.scratch/server-testenv/README.md
@@ -8,8 +8,8 @@
| 路径 | 内容 |
|---|---|
| `/data/talent-card/app.jar` | 后端 fat jar(103 MiB)。**2026-09-29 16:51 更新到提交 `7a5303f`**:V156 已应用(库版本 156,`merchant_onboarding_application.store_cashier_url` 列已建),启动 14.7 s、`/actuator/health` UP。上一版(提交 `61ba794`、库版本 155)留在 `app.jar.bak-20260929-1650`,更早一版在 `app.jar.bak-20260929-1326`;回滚直接换回即可(V156 同样只加一列可空列,旧代码忽略它,安全)。上传来源文件的 sha256:`79d63e44…e4554` |
| `/data/talent-card/admin/` | 后台管理端 dist(nginx 托管)。**2026-09-29 16:54 由 `redeploy-admin.sh` 从 Gitblit `main`(`7a5303f`) 重建并替换**:进件详情的「门店照片」区多了「店铺收银台」一列。注意本次构建日志里有「没有 `/etc/talent-card/admin-build.env`,本次不带高德 key 构建」——见下文「已知缺口」 |
| `/data/talent-card/app.jar` | 后端 fat jar(103 MiB)。**2026-10-10 13:57 更新到提交 `38e39c5`**:库版本仍是 156(`DbMigrate` 报 `Schema is up to date. No migration necessary.`,150 个迁移校验通过),启动 16.2 s、`/actuator/health` UP、`https://rencaika.dqrcfwpt.com/api/health` 与 `http://117.172.73.106:8082/api/health` 均 200。**这一版起改为「在服务器上从 Gitblit 源码就地构建」,不再上传 103 MiB 的包** —— 做法与由来见下「重建后端」,构建产物 sha256 `19efe2b4…ca66`。上一版(提交 `7a5303f`、上传来源文件 sha256 `79d63e44…e4554`)留在 **`app.jar.bak-20261010-1341`**(`cp -p` 保留原 mtime,sha 已校验 = `79d63e44…e4554`),更早两版是 `app.jar.bak-20260929-1650`(提交 `61ba794`、库版本 155)和 `app.jar.bak-20260929-1326`;回滚直接换回即可(本次前后都没有新迁移,旧代码读新库安全) |
| `/data/talent-card/admin/` | 后台管理端 dist(nginx 托管)。**最近一次:2026-10-10 13:59 由 `redeploy-admin.sh` 从 Gitblit `main`(`da90ca8`) 重建并替换**(当天与后端 `38e39c5` 的上线相隔约 2 分钟,两人并行操作,输出留在 `admin-redeploy-20261010.log`):新增人才卡升级页 `TalentUpgradeView.vue`。**注意它 `git fetch` 会把 `/data/talent-card/src` 硬对齐到 `origin/main`**(这次就是从 `38e39c5` 推到 `da90ca8`),所以此后重建后端会以 `da90ca8` 为基线。上上次是 2026-09-29 16:54 从 `7a5303f` 重建(进件详情多了「店铺收银台」一列)。两次构建日志里都有「没有 `/etc/talent-card/admin-build.env`,本次不带高德 key 构建」——见下文「已知缺口」 |
| `/data/talent-card/uploads/` | 上传落盘目录(对应 `UPLOAD_DIR`) |
| `/data/talent-card/src/` | 从 Gitblit 克隆的源码,服务器上构建 admin 用 |
| `/data/talent-card/redeploy-admin.sh` | admin 重部署脚本(见下) |
@@ -55,6 +55,56 @@
> 命令通道单次只能带约 4KB,所以 dist(4.4MB)走不了命令通道——这是当初需要人工 SFTP 传包的原因,
> 现在用「服务器自建」绕过去了。
## 重建后端(2026-10-10 起:服务器上构建,不再上传 jar)
在此之前后端 jar 是**人工传上去的**。2026-10-10 起改成和 admin 同一套路——**在服务器上从 Gitblit
源码构建**,理由与 admin 一样,而且更硬:本机的**唯一通道是堡垒机**(`172.18.105.9`,见下「堡垒机」),
它是个**只给交互式菜单的审计系统**,不接受任意远程命令、没有 SFTP 通道;命令通道单次约 4KB,
137MB 的 base64 分片要三万多次往返,且每一步都会被审计系统留档。所以**不要再试图"把 jar 传上去"**。
**前置条件(已装好,一次性的)**:机器原来只有 `openjdk-17-jre-headless`(**没有 `javac`**),
2026-10-10 装了 `openjdk-17-jdk-headless`(`javac 17.0.20.1`)。`java` 备选项随之指到 JDK 那份,
**与运行时同为 17**,不影响已在跑的服务。Maven 不用装:走仓库自带的 `mvnw`,它把
**Maven 3.9.16 下到 `/root/.m2/wrapper/dists/`**,依赖也缓存在 `/root/.m2`。
```bash
SRC=/data/talent-card/src                     # 与 admin 共用同一份 clone(浅克隆)
git -C $SRC fetch --depth 1 origin main
git -C $SRC checkout -B main origin/main      # 与 redeploy-admin.sh 同款:硬对齐 origin/main
cd $SRC/talent-card-server
bash ./mvnw -B -DskipTests package            # 首次 3m52s(要下全部依赖),4 vCPU
```
**替换 jar 一定要用「临时文件 + `mv`」,不要 `cp` 原地覆盖**:systemd 起的是
`java -jar /data/talent-card/app.jar`,旧进程 mmap 着那个 inode,`cp` 会**原地截断同一个 inode**。
原子 rename 则让旧进程继续用旧 inode、重启后用新 inode。现成脚本留在 `/data/talent-card/do-deploy.sh`
(做:拷 `.new` → `chmod 644` → `sha256sum` → `mv -f` → `systemctl restart` → 等 35 s →
`is-active` / `NRestarts` / `/actuator/health` → `tail app.log`),全部输出写 `/data/talent-card/deploy.log`。
**这台机器没有 `curl`**(`curl: command not found`),探健康检查用 `python3`:
```bash
python3 -c "import urllib.request as u;print(u.urlopen('http://127.0.0.1:8080/actuator/health',timeout=8).read().decode())"
```
## 堡垒机(唯一的进入通道,2026-10-10 实测)
**这台机器没有公网自服务控制台,SSH 也只有这一条路**:`ssh <账号>@172.18.105.9`,它是一台
**运维审计系统(VNC/console 之外的唯一入口)**。两条硬性质,决定了不能用普通跳板机的姿势:
- **它不是跳板机、也不接受任意远程命令。** 登录后是一个**读 stdin 的交互式菜单**:自动列目录→
  自动选中目标 `GT-KC-XXHJSB-DQRCKXM-YYserver (172.192.19.1)` → 停在 `Input account:`。
  用 `ssh host '命令'` 这种形式会把"命令"当成账号喂进去、连错三次被断开(**症状是 exec 被接受后
  立刻 broken pipe、无任何输出**)。
- **没有 SFTP / 端口转发通道**(子系统与 `-W` 都过不去),所以**传文件只能靠服务器自己去拉**(见
  「重建后端」)。同理,`scp` 到这台机器是不通的。
可用的姿势(免交互密码用 `SSH_ASKPASS` + `SSH_ASKPASS_REQUIRE=force`,密码通过环境变量传入、
**不要写进仓库**):按 4 s / 5 s / 6 s 的节奏依次喂 `<账号>`、`<服务器密码>`、`<远端命令>`,会话结束
再喂 `exit`。推荐**把长任务 `nohup ... &` 放后台、结果重定向进文件,下一个会话再 `cat`**——审计系统
有 900 s 自动退出,长任务卡在会话里会被截断(只喂固定等待时长的输出常被切掉)。本机可用的半成品
脚本在 `/tmp/sshtest/connect2.sh`(临时目录,**每次重启就没了**,照上面节奏重写即可)。
## HTTPS(2026-09-29 已上线;2026-10-08 域名变更)
> **2026-10-08 域名变更**:甲方把 `www.dqrcfwpt.com` 与退域名 `dqrcfwpt.com` 解析到了他们自己的机器(公司官网),
rencaika/pages/index/index.uvue
@@ -165,8 +165,9 @@
<style>
    .page { height: 100%; background-color: #FFFFFF; }
    .scroll-page { height: calc(100% - 126rpx); height: calc(100% - 126rpx - env(safe-area-inset-bottom)); background-color: #FFFFFF; }
    .hero { height: 468rpx; background-color: #FFFFFF; position: relative; overflow: hidden; border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; }
    .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 468rpx; }
    /* 顶图本身也裁切底角;较大的弧线在白色面板覆盖下沿 24rpx 后仍清晰可见。 */
    .hero { height: 468rpx; background-color: #FFFFFF; position: relative; overflow: hidden; border-bottom-left-radius: 96rpx; border-bottom-right-radius: 96rpx; }
    .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 468rpx; border-bottom-left-radius: 96rpx; border-bottom-right-radius: 96rpx; }
    .hero-gradient { position: absolute; z-index: 1; top: 0; left: 0; right: 0; height: 300rpx; background-image: linear-gradient(180deg, rgba(244,90,42,.88) 0%, rgba(255,130,60,.42) 62%, rgba(255,166,80,0) 100%); }
    .safe-space { height: 28px; position: relative; z-index: 2; }
    .nav-row { height: 32px; padding: 0 32rpx; position: relative; z-index: 2; flex-direction: row; align-items: center; justify-content: space-between; }
@@ -176,17 +177,18 @@
    .nav-title { position: absolute; z-index: 1; left: 30rpx; top: 10rpx; height: 100%; flex-direction: row; align-items: center; justify-content: flex-start; }
    .nav-title text { color: #FFFFFF; font-family: 'FoodCalligraphyFull', 'FoodCalligraphy'; font-size: 38rpx; font-weight: 700; opacity: .95; }
    .capsule-reserve { width: 88px; height: 100%; flex-shrink: 0; }
    .play-button { position: absolute; z-index: 2; bottom: 20rpx; left: 50%; margin-left: -78rpx; width: 156rpx; height: 56rpx; border-radius: 28rpx; background-image: linear-gradient(180deg, #FFB75A 0%, #FF6A24 100%); border-width: 2rpx; border-color: rgba(255,255,255,.82); box-shadow: 0 7rpx 14rpx rgba(234,92,26,.26); flex-direction: row; justify-content: center; align-items: center; }
    .play-button { position: absolute; z-index: 2; bottom: 34rpx; left: 50%; margin-left: -78rpx; width: 156rpx; height: 56rpx; border-radius: 28rpx; background-image: linear-gradient(180deg, #FFB75A 0%, #FF6A24 100%); border-width: 2rpx; border-color: rgba(255,255,255,.82); box-shadow: 0 7rpx 14rpx rgba(234,92,26,.26); flex-direction: row; justify-content: center; align-items: center; }
    .play-button text { color: #FFFFFF; font-size: 23rpx; }
    /*
     * 顶部图下方的衔接:原型里白色区是「通版」(左右不留边、上沿不圆角、没有橙色投影),
     * 主图只把底部两个角做圆角,让白色自然接上来;橙色城市区自己再左右内缩 16rpx。
     * 所以这里把面板改成通版(margin 左右 0、去掉圆角和投影),内缩交给 .city-grid。
     * 标题和城市卡片共用一个圆角面板,上移 24rpx 浅嵌入顶部图的底部圆角区。
     * 白色面板上沿两侧露出顶部图片;视频按钮底部留出 24rpx 重叠区和 10rpx 间距。
     * 面板用浅橙色实线描边,让白底上的圆角轮廓清晰可见,不依赖模糊阴影。
     * 保留 overflow: visible,让吉祥物和卡片底部自然露出,不能裁切整个面板。
     */
    .city-panel { margin: 0 0 38rpx; padding: 22rpx 0 0; overflow: visible; background-color: #FFFFFF; position: relative; }
    /* 面板改通版后,标题的绝对位置保持不变:原来 16(面板内缩)+22(自身边距)=38rpx,现在直接给 38rpx。 */
    .city-heading { height: 58rpx; margin: 0 38rpx; position: relative; flex-direction: row; justify-content: space-between; align-items: center; }
    .city-panel { box-sizing: border-box; margin: -24rpx 16rpx 38rpx; padding: 22rpx 0 0; overflow: visible; background-color: #FFFFFF; border-width: 2rpx; border-style: solid; border-color: #FFC39D; border-top-left-radius: 64rpx; border-top-right-radius: 64rpx; border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; position: relative; z-index: 2; }
    /* 面板内缩 16rpx + 标题边距 22rpx,维持标题和下方卡片的左侧对齐。 */
    .city-heading { height: 58rpx; margin: 0 22rpx; position: relative; flex-direction: row; justify-content: space-between; align-items: center; }
    /*
     * 站立吉祥物:贴在面板右上角,跨在白色面板上沿之上(身体压在城市主图上),和原型一致。
     * 原型量出来可见轮廓是 58×100rpx(右边缘正好贴面板右边缘、顶端高出面板上沿约 57rpx);
@@ -197,19 +199,18 @@
    /*
     * 趴着的吉祥物:在「才聚东区,共富未来」和「幸福东区」之间,草坪底边压在橙色城市区上沿。
     * 原型量出来是 51×28px(素材无透明留白、比例 1.815 与原型一致)→ 128×70rpx;
     * 右边缘距面板右边缘 64px→160rpx;顶端距面板上沿 21px→52rpx(它现在挂在 .city-panel 上,
     * 所以 right/top 直接按面板算:right 160rpx、top 52rpx 减去画布上留白对应的 12rpx → 40rpx)。
     * 面板右侧内缩 16rpx 后,right 使用 144rpx,保持距页面右侧 160rpx 的位置;
     * top 52rpx 减去画布上留白对应的 12rpx → 40rpx。
     * 素材底部补了 8px 透明留白,所以盒子高度要按画布比例放大到 75rpx(可见部分仍是 128×70rpx)。
     * top 取 40rpx:草地底边落在橙色城市区上沿之内、但不压到下方卡片(原型里草地底边距卡片上沿还有 4px)。
     */
    .city-mascot { position: absolute; right: 160rpx; top: 40rpx; width: 128rpx; height: 75rpx; z-index: 3; }
    .city-mascot { position: absolute; right: 144rpx; top: 40rpx; width: 128rpx; height: 75rpx; z-index: 3; }
    .city-heading > text { color: #F16624; font-size: 35rpx; font-weight: 700; letter-spacing: 1rpx; background-image: linear-gradient(180deg, #F45A2A 0%, #FFB257 100%); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
    /*
     * 「幸福东区」按钮:原型里按钮右边缘距面板右边缘 18px(面板 287px)→ 45rpx;
     * .city-heading 自身左右各留 38rpx,所以这里 margin-right 取 7rpx(38+7=45rpx),和原型一致。
     * 「幸福东区」按钮保持距页面右侧 45rpx:面板 16rpx + 标题 22rpx + 按钮 7rpx。
     */
    .city-label { margin-right: 7rpx; padding: 10rpx 13rpx; border-radius: 18rpx; background-image: linear-gradient(180deg, #FFB85E 0%, #FF6726 100%); box-shadow: 0 5rpx 10rpx rgba(239,101,34,.22); flex-direction: row; align-items: center; } .city-label text { color: #FFFFFF; font-size: 20rpx; } .city-label image { width: 20rpx; height: 20rpx; margin-left: 7rpx; }
    .city-grid { box-sizing: border-box; height: 292rpx; margin: 18rpx 16rpx 0; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-image: linear-gradient(135deg, #E96C3D 0%, #F8AA56 100%); }
    .city-grid { box-sizing: border-box; height: 292rpx; margin: 18rpx 0 0; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-image: linear-gradient(135deg, #E96C3D 0%, #F8AA56 100%); }
    .city-bg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
    .city-intro { width: 242rpx; height: 308rpx; box-sizing: border-box; margin-right: 14rpx; padding: 22rpx; } .intro-name { position: absolute; z-index: 1; top: 50%; left: 0; right: 0; transform: translateY(-50%); color: #FFFFFF; font-size: 37rpx; font-weight: 700; text-align: center; } .intro-button { position: relative; z-index: 1; align-self: center; box-sizing: border-box; width: 146rpx; height: 42rpx; margin-top: 13rpx; padding: 0 12rpx; border-radius: 21rpx; background-image: linear-gradient(90deg, rgba(255,207,138,.92) 0%, rgba(255,101,36,.68) 100%); border-width: 1rpx; border-color: rgba(255,255,255,.42); flex-direction: row; align-items: center; justify-content: space-between; } .intro-button text { color: #FFFFFF; font-size: 19rpx; } .intro-button image { width: 19rpx; height: 19rpx; margin-left: 8rpx; }
    .city-side { height: 308rpx; flex: 1; margin-right: 14rpx; justify-content: space-between; } .city-side:last-child { margin-right: 0; } .city-side .city-card { height: 147rpx; padding: 0; } .city-side .city-card > text { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; padding: 10rpx 12rpx; color: #FFFFFF; font-size: 22rpx; font-weight: 700; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.5); }