
=== 2/8 校验产物与静态检查 ===
  ✅ pms-api  (12.1 MB)
  ✅ pms-device-server  (2.8 MB)
  ✅ web/dist  (6 个文件)

=== 3/8 服务器备份（保留旧二进制/.env/nginx/单元/前端） ===
  [DryRun] ssh: set -e
TS=$(date +%Y%m%d-%H%M%S)
mkdir -p /data/pms/backup-$TS
cp -a /data/pms/api-server/pms-api /data/pms/backup-$TS/ 2>/dev/null || true
cp -a /data/pms/device-server/pms-device-server /data/pms/backup-$TS/ 2>/dev/null || true
cp -a /data/pms/api-server/.env /data/pms/device-server/.env /data/pms/backup-$TS/ 2>/dev/null || true
mkdir -p /data/pms/backup-$TS/web && cp -a /data/pms/web/. /data/pms/backup-$TS/web/ 2>/dev/null || true
echo $TS > /tmp/pms_last_backup
echo "  备份目录: /data/pms/backup-$TS"

=== 4/8 上传（二进制传 .new，运行中的二进制不可直接覆盖） ===
  [DryRun] scp: D:\charging-cabinet\software\api-server\target\x86_64-unknown-linux-gnu\release\pms-api -> /data/pms/api-server/pms-api.new
  [DryRun] scp: D:\charging-cabinet\software\device-server\target\x86_64-unknown-linux-gnu\release\pms-device-server -> /data/pms/device-server/pms-device-server.new
  [DryRun] scp: C:\Users\12451\AppData\Local\Temp\pms-dist.tgz -> /tmp/pms-dist.tgz

=== 5/8 原子替换并重启服务 ===
  [DryRun] ssh: set -e
chmod +x /data/pms/api-server/pms-api.new /data/pms/device-server/pms-device-server.new
systemctl stop pms-api pms-device
mv -f /data/pms/api-server/pms-api.new /data/pms/api-server/pms-api
mv -f /data/pms/device-server/pms-device-server.new /data/pms/device-server/pms-device-server
rm -rf /data/pms/web/* && tar -xzf /tmp/pms-dist.tgz -C /data/pms/web/ && rm -f /tmp/pms-dist.tgz
systemctl daemon-reload
systemctl start pms-device pms-api
sleep 8
nginx -t && systemctl reload nginx

=== 6/8 验收（服务/端口/域名/日志） ===
  [DryRun] ssh: echo '--- 服务 ---'; systemctl is-active pms-api pms-device nginx | paste -sd' '
echo '--- 端口（应为 9000 / 9001）---'; ss -lntp | grep -E ':9000|:9001' | awk '{print "  " $4}'
echo '--- 域名 ---'
curl -sk -m 8 -o /dev/null -w '  /api/pms/cabinets -> %{http_code}\n' https://app.anzhizhichong.com/api/pms/cabinets
curl -sk -m 8 -o /dev/null -w '  /pms/            -> %{http_code}\n' https://app.anzhizhichong.com/pms/
echo '--- 启动日志尾部 ---'; journalctl -u pms-api --no-pager -n 4 | tail -4
echo '--- 新进程 ERROR 数（应为 0）---'
MAIN=$(systemctl show -p MainPID --value pms-api)
journalctl -u pms-api --since '-3 min' --no-pager | grep -c " ERROR " | sed 's/^/  ERROR 行数: /'

=== 7/8 上报消费链路探测（用不存在的 IMEI，不碰真实设备数据） ===

=== 8/8 回滚点保留策略（只保留最近 5 次 backup-YYYYMMDD-HHMMSS） ===
  [DryRun] ssh: set -e
shopt -s nullglob
KEEP=5
# glob 字符类只匹配严格的 backup-YYYYMMDD-HHMMSS；nullglob 让无匹配时展开为空数组。
# glob 结果按字典序排列，对该命名格式而言等价于时间戳升序（最早的在前）。
rollbacks=()
for p in /data/pms/backup-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]; do
    if [ -d "$p" ]; then
        rollbacks+=( "$p" )
    fi
done
total=${#rollbacks[@]}
echo "  当前回滚点 $total 个（deploy 会为每次部署新建一份）:"
for p in "${rollbacks[@]}"; do
    echo "    $p"
done
if [ "$total" -gt "$KEEP" ]; then
    drop=$((total - KEEP))
    echo "  将删除最早的 $drop 个（保留最近 $KEEP 个）:"
    i=0
    for p in "${rollbacks[@]}"; do
        if [ "$i" -ge "$drop" ]; then
            break
        fi
        i=$((i + 1))
        # 复核：名字必须严格符合 backup-YYYYMMDD-HHMMSS，否则拒绝删除（绝不碰其它目录）
        case "$(basename "$p")" in
            backup-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9])
                echo "    删除 $p"
                rm -rf -- "$p"
                ;;
            *)
                echo "    [skip] 名字不符合回滚点模式，拒绝删除: $p"
                ;;
        esac
    done
else
    echo "  回滚点数量未超过 $KEEP，无需清理"
fi
kept=()
for p in /data/pms/backup-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9]; do
    if [ -d "$p" ]; then
        kept+=( "$p" )
    fi
done
echo "  清理后保留的回滚点 ${#kept[@]} 个:"
for p in "${kept[@]}"; do
    echo "    $p"
done
echo "  /data/pms 占用: $(du -sh /data/pms | cut -f1)"

=== 部署完成 ===
  正式环境：https://app.anzhizhichong.com/pms/
  API :9000   TCP :9001
  
  回滚方法（如需）：
    plink -batch -pw <pwd> root@118.31.119.206 "systemctl stop pms-api pms-device && cp /data/pms/backup-<TS>/* /data/pms/... && systemctl start pms-api pms-device"
    备份目录见 /data/pms/backup-<时间戳>/（脚本已把 .env / 前端 / 单元一并备份；只保留最近 5 次）
