# 计划执行：P3 级检测模块移植

## 文档信息

- 创建时间：2026-07-22
- 关联需求文档：`Docs/需求文档/服务监测/PRD_需求文档_P3级移植.md`
- 预计改动文件：4 个
- 预计新增文件：2 个

---

## 执行步骤

### Step 1：文件权限检测模块 — 54_file_permission_check.sh

**文件**：`skill/code/web/service_monitor/assets/system/54_file_permission_check.sh`（新增）

**检测项**：

| KEY | 输出 |
|-----|------|
| `FILE_PERM_STATUS` | OK/WARN |
| `FILE_PERM_STATUS_LEVEL` | 正常/警告 |
| `FILE_PERM_TOTAL_CHECKS` | 整数 |
| `FILE_PERM_WRONG_COUNT` | 整数 |
| `FILE_PERM_WRONG_LIST` | JSON 数组字符串 |

**实现要点**：

```bash
#!/bin/bash
################################################################################
# 54_file_permission_check.sh — 文件权限检测
# 功能: 检查关键文件/目录的权限是否正确
# 参考: FilePermission.psm1
# 日期: 2026-07-22
# 说明: 只检测不修复，发现权限异常标记为警告
################################################################################

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="${LIB_DIR:-/tmp/check_modules}"

source "$LIB_DIR/lib/config.sh"
source "$LIB_DIR/lib/common.sh"

# ==================== 平台类型识别 ====================

detect_platform_type() {
    [ -d "/data/services" ] && echo "new" || echo "old"
}

# ==================== 权限检查逻辑 ====================

check_file_permission() {
    local file="$1"
    local expect_exec="$2"  # "yes" or "no"
    
    [ ! -e "$file" ] && return 0  # 文件不存在，跳过
    
    local perm
    perm=$(stat -c '%a' "$file" 2>/dev/null || stat -f '%Lp' "$file" 2>/dev/null)
    
    if [ -z "$perm" ]; then
        return 0  # 无法获取权限，跳过
    fi
    
    local is_exec=0
    [ $((perm & 0111)) -ne 0 ] && is_exec=1
    
    if [ "$expect_exec" = "yes" ] && [ $is_exec -eq 0 ]; then
        # 应可执行但不可执行
        return 1
    elif [ "$expect_exec" = "no" ] && [ $is_exec -ne 0 ]; then
        # 应不可执行但可执行
        return 1
    fi
    
    return 0
}

# ==================== 主检测流程 ====================

main() {
    log_info "开始文件权限检测..."
    
    set +e
    
    local platform_type
    platform_type=$(detect_platform_type)
    
    local total_checks=0
    local wrong_count=0
    local wrong_list=""
    
    # 定义检查路径（格式: path:expect_exec）
    local check_paths
    if [ "$platform_type" = "new" ]; then
        check_paths=(
            "/data/services/api/start.sh:yes"
            "/data/services/api/auth/auth-sso-auth/run.sh:yes"
            "/data/services/api/auth/auth-sso-gateway/run.sh:yes"
            "/data/services/api/auth/auth-sso-system/run.sh:yes"
            "/data/services/api/java-meeting/java-meeting2.0/run.sh:yes"
            "/data/services/api/java-meeting/java-meeting-extapi/run.sh:yes"
            "/data/services/api/java-meeting/java-message-scheduling/run.sh:yes"
            "/data/services/api/java-meeting/java-mqtt/run.sh:yes"
            "/data/services/api/java-meeting/java-quartz/run.sh:yes"
            "/etc/rc.d/rc.local:yes"
        )
    else
        check_paths=(
            "/var/www/java/start.sh:yes"
            "/var/www/java/api-java-meeting2.0/run.sh:yes"
            "/var/www/java/external-meeting-api/run.sh:yes"
            "/var/www/html/start.sh:yes"
            "/etc/rc.d/rc.local:yes"
        )
    fi
    
    for entry in "${check_paths[@]}"; do
        local path expect_exec
        path="${entry%:*}"
        expect_exec="${entry#*:}"
        
        total_checks=$((total_checks + 1))
        
        if ! check_file_permission "$path" "$expect_exec"; then
            wrong_count=$((wrong_count + 1))
            local escaped_path
            escaped_path=$(echo "$path" | sed 's/"/\\"/g')
            [ -n "$wrong_list" ] && wrong_list="${wrong_list},"
            wrong_list="${wrong_list}{\"path\":\"${escaped_path}\",\"expected_exec\":\"${expect_exec}\"}"
        fi
    done
    
    # 输出结果
    if [ "$wrong_count" -eq 0 ]; then
        output_result "FILE_PERM_STATUS" "OK"
        output_result "FILE_PERM_STATUS_LEVEL" "正常"
    else
        output_result "FILE_PERM_STATUS" "WARN"
        output_result "FILE_PERM_STATUS_LEVEL" "警告"
    fi
    
    output_result "FILE_PERM_TOTAL_CHECKS" "$total_checks"
    output_result "FILE_PERM_WRONG_COUNT" "$wrong_count"
    
    if [ -n "$wrong_list" ]; then
        output_result "FILE_PERM_WRONG_LIST" "[${wrong_list}]"
    else
        output_result "FILE_PERM_WRONG_LIST" "[]"
    fi
    
    log_info "文件权限检测完成"
}

main
```

---

### Step 2：定时任务预期检测模块 — 55_cron_expected.sh

**文件**：`skill/code/web/service_monitor/assets/system/55_cron_expected.sh`（新增）

**检测项**：

| KEY | 输出 |
|-----|------|
| `CRON_EXPECTED_STATUS` | OK/WARN |
| `CRON_EXPECTED_STATUS_LEVEL` | 正常/警告 |
| `CRON_EXPECTED_TOTAL` | 整数 |
| `CRON_EXPECTED_MISS_COUNT` | 整数 |
| `CRON_EXPECTED_MISS_LIST` | JSON 数组字符串 |

**实现要点**：

```bash
#!/bin/bash
################################################################################
# 55_cron_expected.sh — 定时任务预期检测
# 功能: 检查预期 crontab 条目是否存在
# 参考: check_server_health.sh (test_scheduled_tasks)
# 日期: 2026-07-22
# 说明: 只检测不修改，发现缺失标记为警告
################################################################################

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LIB_DIR="${LIB_DIR:-/tmp/check_modules}"

source "$LIB_DIR/lib/config.sh"
source "$LIB_DIR/lib/common.sh"

# ==================== 平台类型识别 ====================

detect_platform_type() {
    [ -d "/data/services" ] && echo "new" || echo "old"
}

# ==================== 主检测流程 ====================

main() {
    log_info "开始定时任务预期检测..."
    
    set +e
    
    local platform_type
    platform_type=$(detect_platform_type)
    
    # 预期 crontab 条目（不含时间部分，只匹配命令）
    local expected_entries
    expected_entries=(
        "bash /usr/local/docker/UbainsmysqlBakUp.sh"
    )
    
    # 新平台额外期望 ujava2 守护脚本
    if [ "$platform_type" = "new" ]; then
        expected_entries+=("/data/services/scripts/ujava2-startup.sh")
    fi
    
    # 获取当前 crontab
    local current_crontab
    current_crontab=$(crontab -l 2>/dev/null || echo "")
    
    local total_expected=${#expected_entries[@]}
    local miss_count=0
    local miss_list=""
    
    for entry in "${expected_entries[@]}"; do
        if ! echo "$current_crontab" | grep -qF "$entry"; then
            miss_count=$((miss_count + 1))
            local escaped_entry
            escaped_entry=$(echo "$entry" | sed 's/"/\\"/g')
            [ -n "$miss_list" ] && miss_list="${miss_list},"
            miss_list="${miss_list}\"${escaped_entry}\""
        fi
    done
    
    # 输出结果
    if [ "$miss_count" -eq 0 ]; then
        output_result "CRON_EXPECTED_STATUS" "OK"
        output_result "CRON_EXPECTED_STATUS_LEVEL" "正常"
    else
        output_result "CRON_EXPECTED_STATUS" "WARN"
        output_result "CRON_EXPECTED_STATUS_LEVEL" "警告"
    fi
    
    output_result "CRON_EXPECTED_TOTAL" "$total_expected"
    output_result "CRON_EXPECTED_MISS_COUNT" "$miss_count"
    
    if [ -n "$miss_list" ]; then
        output_result "CRON_EXPECTED_MISS_LIST" "[${miss_list}]"
    else
        output_result "CRON_EXPECTED_MISS_LIST" "[]"
    fi
    
    log_info "定时任务预期检测完成"
}

main
```

---

### Step 3：模块注册 — check_modules.py

**文件**：`skill/code/web/service_monitor/utils/check_modules.py`

**改动位置**：`ALL_MODULES` 列表

**新增内容**：

```python
# 在 system 类区块（53_config_console_check 之后）
CheckModule("54_file_permission_check", "文件权限检测", "system",
            {SUITE_FULL}, "54_file_permission_check.sh"),
CheckModule("55_cron_expected", "定时任务预期检测", "system",
            {SUITE_FULL}, "55_cron_expected.sh"),
```

---

### Step 4：显示名映射 — display_names.py

**文件**：`skill/code/web/service_monitor/utils/display_names.py`

**新增内容**（约 10 个 KEY）：

```python
# ==================== 54 文件权限检测 ====================
"FILE_PERM_STATUS": "文件权限检测状态",
"FILE_PERM_STATUS_LEVEL": "文件权限检测状态等级",
"FILE_PERM_TOTAL_CHECKS": "检查文件数",
"FILE_PERM_WRONG_COUNT": "权限异常数",
"FILE_PERM_WRONG_LIST": "权限异常明细",

# ==================== 55 定时任务预期检测 ====================
"CRON_EXPECTED_STATUS": "定时任务预期检测状态",
"CRON_EXPECTED_STATUS_LEVEL": "定时任务预期检测状态等级",
"CRON_EXPECTED_TOTAL": "预期任务数",
"CRON_EXPECTED_MISS_COUNT": "缺失任务数",
"CRON_EXPECTED_MISS_LIST": "缺失任务列表",
```

---

## 验收标准

| 验收项 | 验证方法 |
|--------|---------|
| 2 个脚本语法正确 | `bash -n script.sh` 无报错 |
| 模块已注册 | `get_suite('full')` 返回 36 个模块 |
| 显示名已添加 | 新 KEY 在 `DISPLAY_NAMES` 中存在 |
| 单元测试通过 | `pytest` 全绿（218 个） |

---

## 测试计划

### 单元测试

1. **check_modules.test_get_suite**：验证 `get_suite('full')` 包含新模块

### 集成测试

1. 在 5.44 目标运行 `full` 套件
2. 检查报告中新检测项输出

---

## 实施记录

| 步骤 | 状态 | 说明 |
|------|------|------|
| Step 1: 54_file_permission_check.sh | ⬜ 待实施 | |
| Step 2: 55_cron_expected.sh | ⬜ 待实施 | |
| Step 3: check_modules.py | ⬜ 待实施 | |
| Step 4: display_names.py | ⬜ 待实施 | |
| 集成测试 | ⬜ 待实施 | |
| 部署验证 | ⬜ 待实施 | |