PayrollManagementSystem/scripts/health_check.sh
liuwenxin 45c268b146 fix(m3): code-review P2——金额校验、参数解析与运维脚本容错
- 调整单金额恒正:schema 正则拦格式(非负两位小数、≤12 位整数防
  NUMERIC(14,2) 溢出)+ 服务层 Decimal>0 语义校验——负数会反转补发/
  扣减语义,'abc' 原样入库会以 500 暴露
- 分界天数参数解析:int(parse_fraction(...)) 替代 int(参数原文)——
  '360.0' 是 INT 参数的合法入库值(Fraction 分母为 1),直接 int(str)
  抛 ValueError 会让整月核算 500
- logo 旧文件清理容错:OSError 只记 warning——裸抛会把成功的 logo 更新
  记成失败审计并 500(库中 logo_path 却已是新值,审计与事实相反)
- health_check.sh:Redis 探活改容器内环境变量展开(密码不进宿主进程
  命令行,模式同 backup_full.sh);compose redis 补 REDIS_PASSWORD
- restore_drill.sh:行数核对扩到全部 32 张业务表(原 16 张漏 V7 四表、
  二次分配等核心表,备份漏表演练全绿);查询失败回显 '?' 一律判 ✗
  (原 '?'=='?' 判一致,表缺失时误报「备份可恢复」)
- 并入 88 会话的 #12 实现:manager.personal.included 开关消费
  (_build_manager_aggregates 参数化,测试见 828b573)
- 测试:调整单金额恒正 5 稿、INT 参数小数原文核算 200 回归;单测 469

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-19 12:10:29 +08:00

107 lines
3.6 KiB
Bash
Raw Permalink 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.

#!/usr/bin/env bash
# 运维健康检查 —— 容器/磁盘/备份新鲜度/登录异常(⑨ 输出一份可读报告)
#
# 用法:在项目根目录执行 bash scripts/health_check.sh
# 每项 PASS/WARN/FAIL,最后给出结论。WARN/FAIL 时退出码非零提示运维介入。
set -uo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT"
if [[ -f docker/.env ]]; then
# shellcheck disable=SC1091
source docker/.env
fi
ISSUES=0
report() { printf "%-10s %s\n" "$1" "$2"; }
warn() { report "WARN" "$1"; ISSUES=$((ISSUES+1)); }
fail() { report "FAIL" "$1"; ISSUES=$((ISSUES+1)); }
pass() { report "PASS" "$1"; }
echo "======== 衍水工资系统 健康检查 $(date '+%Y-%m-%d %H:%M:%S') ========"
# 1. 容器健康
for C in ys-mysql-dev ys-redis-dev; do
if ! docker inspect "$C" >/dev/null 2>&1; then
fail "容器 $C 不存在"
continue
fi
STATUS="$(docker inspect -f '{{.State.Health.Status}}' "$C" 2>/dev/null || echo none)"
RUNNING="$(docker inspect -f '{{.State.Running}}' "$C" 2>/dev/null)"
if [[ "$RUNNING" == "true" && "$STATUS" == "healthy" ]]; then
pass "容器 $C 运行中且健康"
elif [[ "$RUNNING" == "true" ]]; then
warn "容器 $C 运行中但健康检查=$STATUS"
else
fail "容器 $C 未运行"
fi
done
# 2. 磁盘水位
DISK_PCT="$(df -h "$ROOT" | awk 'NR==2 {gsub(/%/,"",$5); print $5}')"
if [[ -z "$DISK_PCT" ]]; then
warn "磁盘信息读取失败"
elif (( DISK_PCT < 80 )); then
pass "磁盘使用率 ${DISK_PCT}%(阈值 80%)"
elif (( DISK_PCT < 90 )); then
warn "磁盘使用率 ${DISK_PCT}%(建议清理)"
else
fail "磁盘使用率 ${DISK_PCT}%(高危,立即清理)"
fi
# 3. 备份新鲜度
BACKUP_DIR="$ROOT/db/backups"
LATEST="$(ls -t "$BACKUP_DIR"/payroll_*.sql.gz 2>/dev/null | head -1)"
if [[ -z "$LATEST" ]]; then
warn "未找到任何备份(应每日备份)"
else
AGE_H=$(( ($(date +%s) - $(stat -c %Y "$LATEST")) / 3600 ))
if (( AGE_H < 24 )); then
pass "最新备份 $(basename "$LATEST"),${AGE_H} 小时前"
elif (( AGE_H < 48 )); then
warn "最新备份已 ${AGE_H} 小时前(超过 24 小时)"
else
fail "最新备份已 ${AGE_H} 小时前(备份任务可能已停)"
fi
fi
# 4. Redis 存活(密码经容器内环境变量展开,不出现在宿主进程命令行;
# 写法同 backup_full.sh 的 MYSQL_ROOT_PASSWORD)
if [[ -n "${REDIS_PASSWORD:-}" ]]; then
if docker exec ys-redis-dev sh -c 'redis-cli --no-auth-warning -a "$REDIS_PASSWORD" ping' 2>/dev/null | grep -q PONG; then
pass "Redis 响应 PONG"
else
warn "Redis ping 无响应(检查 REDIS_PASSWORD 或容器状态)"
fi
else
warn "docker/.env 缺 REDIS_PASSWORD,跳过 Redis 探活"
fi
# 5. 登录日志异常(24h 失败次数)
if [[ -n "${MYSQL_ROOT_PASSWORD:-}" ]] && docker inspect ys-mysql-dev >/dev/null 2>&1; then
FAILS=$(docker exec ys-mysql-dev sh -c \
"mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" ${MYSQL_DATABASE:-payroll} -N -B -e \"
SELECT COUNT(*) FROM login_logs
WHERE success = 0 AND create_time > NOW() - INTERVAL 24 HOUR;\" 2>/dev/null" || echo "?")
if [[ "$FAILS" == "?" ]]; then
warn "登录日志查询失败"
elif (( FAILS < 10 )); then
pass "24h 登录失败 ${FAILS} 次(阈值 10)"
else
warn "24h 登录失败 ${FAILS} 次(可能有暴力破解,建议核查)"
fi
else
warn "跳过登录日志检查(无凭据或容器未运行)"
fi
echo "=============================================================="
if (( ISSUES == 0 )); then
echo "结论:全部通过,系统健康。"
exit 0
else
echo "结论:发现 $ISSUES 项需关注(见上)。"
exit 1
fi