chore(scripts): 新增授权到期时间测试工具 set-license-expiry.sh

按相对天数设置门店授权 expires_at,快速切换 normal/grace/readonly/locked
四个阶段验证客户端表现。SSH 到线上 jiu_mysql 执行,DB 密码仅在远端从
production.env 读取,不打印/外传。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-19 09:58:31 +08:00
parent 58266a2095
commit 914e2fb533
+82
View File
@@ -0,0 +1,82 @@
#!/usr/bin/env bash
# set-license-expiry.sh <门店编号> <带符号天数>
#
# 测试工具:把某门店「当前有效授权」(licenses.is_active=1) 的到期时间改成
# 「相对现在 N 天」,方便快速切换授权阶段验证客户端表现。
#
# 带符号天数 DAYS
# 30 -> 30 天后到期 → normal 正常(未过期)
# 0 -> 今天到期
# -6 -> 已过期 6 天 → grace 宽限期(仍可登录,有横幅)
# -10 -> 已过期 10 天 → readonly 只读(可登录,禁写)
# -20 -> 已过期 20 天 → locked 锁定(无法登录)
#
# 默认改「线上 prod」EC2 上 jiu_mysql 容器里的 jiu_db.licenses。
# 连接参数可用环境变量覆盖:
# EC2_HOST (默认 18.136.60.128)
# EC2_USER (默认 ec2-user)
# SSH_KEY (默认 ~/.ssh/wangjia.pem)
#
# 用法示例:
# sh scripts/set-license-expiry.sh S003 -6 # S003 → 已过期 6 天(grace
# sh scripts/set-license-expiry.sh S003 30 # S003 → 恢复,30 天后到期
set -euo pipefail
SHOP="${1:-}"
DAYS="${2:-}"
if [ -z "$SHOP" ] || [ -z "$DAYS" ]; then
echo "用法: $0 <门店编号> <带符号天数>" >&2
echo "示例: $0 S003 -6 # S003 授权改为已过期 6 天(grace 宽限期)" >&2
echo " $0 S003 30 # S003 授权恢复,30 天后到期" >&2
exit 1
fi
case "$DAYS" in
''|*[!0-9-]*) echo "错误: 天数必须是整数(可带负号),收到: '$DAYS'" >&2; exit 1;;
esac
EC2_HOST="${EC2_HOST:-18.136.60.128}"
EC2_USER="${EC2_USER:-ec2-user}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/wangjia.pem}"
echo "==> 目标(线上 prod): ${EC2_USER}@${EC2_HOST} 门店=${SHOP} 到期=现在 ${DAYS}"
# 全部在远端单次 ssh 内完成;DB 密码只在远端从 production.env 读取,绝不打印/外传。
ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -i "$SSH_KEY" \
"${EC2_USER}@${EC2_HOST}" bash -s -- "$SHOP" "$DAYS" <<'ENDSSH'
set -euo pipefail
SHOP="$1"; DAYS="$2"
sudo grep '^DB_PASSWORD=' /opt/jiu/config/production.env | cut -d= -f2- | {
read -r PW
MYSQL() { sudo docker exec -i jiu_mysql mysql --default-character-set=utf8mb4 -uroot -p"$PW" jiu_db "$@"; }
echo "--- 改前 ---"
MYSQL -e "SELECT s.code, s.name, l.id, l.type, l.is_active, l.expires_at,
TIMESTAMPDIFF(DAY, l.expires_at, NOW()) AS days_expired
FROM licenses l JOIN shops s ON s.id = l.shop_id
WHERE s.code = '${SHOP}' AND l.is_active = 1 ORDER BY l.id DESC;"
MYSQL -e "UPDATE licenses SET expires_at = DATE_ADD(NOW(), INTERVAL ${DAYS} DAY)
WHERE shop_id = (SELECT id FROM shops WHERE code = '${SHOP}' LIMIT 1)
AND is_active = 1;"
echo "--- 改后 ---"
MYSQL -e "SELECT s.code, l.id, l.expires_at,
TIMESTAMPDIFF(DAY, l.expires_at, NOW()) AS days_expired
FROM licenses l JOIN shops s ON s.id = l.shop_id
WHERE s.code = '${SHOP}' AND l.is_active = 1 ORDER BY l.id DESC;"
}
ENDSSH
# 阶段提示(与后端 middleware.CalcLicensePhase 边界一致:grace<=7天, readonly<=15天, locked>15天)
if [ "$DAYS" -ge 0 ]; then
PHASE="normal 正常(未过期)"
else
EXP=$(( -DAYS ))
if [ "$EXP" -le 7 ]; then PHASE="grace 宽限期(仍可登录,有横幅提醒)"
elif [ "$EXP" -le 15 ]; then PHASE="readonly 只读(可登录,所有写操作被禁)"
else PHASE="locked 锁定(无法登录,登录页给出续费入口)"
fi
fi
echo "==> 完成。预计阶段: ${PHASE}"
echo "==> 提示: 后端 phase 有约 30s 的每店缓存;已登录的客户端会话需等 ~30s 或重新登录后生效。"