# 中介黑名单双表迁移测试验证计划

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**目标:** 验证中介黑名单从单表迁移到双表（个人中介→ccdi_biz_intermediary，实体中介→ccdi_enterprise_base_info）后的所有接口功能正确性

**架构:** 使用 curl 脚本进行 HTTP API 测试，验证数据库表数据正确性，自动生成测试报告

**技术栈:**

- HTTP 测试工具：curl（命令行）
- 数据库验证：MySQL 命令行
- 报告生成：Bash 脚本
- 后端框架：Spring Boot 3.5.8 + MyBatis Plus 3.5.10
- 数据库：MySQL 8.2.0

---

## 测试环境准备

### Task 1: 创建测试工具脚本

**Files:**

- Create: `doc/test/scripts/test_utils.sh`
- Create: `doc/test/scripts/test_report.sh`

**Step 1: 创建测试工具函数库**

```bash
# file: doc/test/scripts/test_utils.sh

#!/bin/bash

# ============================================================
# 测试工具函数库
# ============================================================

# 配置
BASE_URL="http://localhost:8080"
LOGIN_URL="${BASE_URL}/login"
TOKEN=""
OUTPUT_DIR="doc/test/output"
REPORT_FILE="${OUTPUT_DIR}/test_report.md"

# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 初始化输出目录
init_output() {
    mkdir -p "${OUTPUT_DIR}"
    echo "# 中介黑名单双表迁移测试报告" > "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"
    echo "**测试时间:** $(date '+%Y-%m-%d %H:%M:%S')" >> "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"
    echo "---" >> "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"
}

# 登录获取 token
login() {
    echo -e "${YELLOW}正在登录...${NC}"

    response=$(curl -s -X POST "${LOGIN_URL}" \
        -H "Content-Type: application/json" \
        -d '{
            "username": "admin",
            "password": "admin123"
        }')

    TOKEN=$(echo "${response}" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)

    if [ -z "${TOKEN}" ]; then
        echo -e "${RED}登录失败${NC}"
        echo "响应: ${response}"
        exit 1
    fi

    echo -e "${GREEN}登录成功，Token: ${TOKEN:0:20}...${NC}"
    echo "" >> "${REPORT_FILE}"
    echo "## 1. 登录验证" >> "${REPORT_FILE}"
    echo "- 状态: ✅ 成功" >> "${REPORT_FILE}"
    echo "- Token: \`${TOKEN:0:20}...\`" >> "${REPORT_FILE}"
}

# 通用 HTTP 请求函数
http_request() {
    local method=$1
    local url=$2
    local data=$3
    local description=$4

    echo -e "${YELLOW}测试: ${description}${NC}"

    if [ "${method}" = "GET" ]; then
        response=$(curl -s -X GET "${BASE_URL}${url}" \
            -H "Authorization: Bearer ${TOKEN}")
    else
        response=$(curl -s -X "${method}" "${BASE_URL}${url}" \
            -H "Authorization: Bearer ${TOKEN}" \
            -H "Content-Type: application/json" \
            -d "${data}")
    fi

    # 保存响应
    echo "${response}" | jq '.' > "${OUTPUT_DIR}/temp_response.json" 2>/dev/null || echo "${response}" > "${OUTPUT_DIR}/temp_response.json"

    # 检查是否成功
    code=$(echo "${response}" | grep -o '"code":[0-9]*' | cut -d':' -f2)

    if [ "${code}" = "200" ]; then
        echo -e "${GREEN}✓ 通过${NC}"
        echo "响应: $(echo ${response} | jq -c '.msg' 2>/dev/null || echo ${response})"
        return 0
    else
        echo -e "${RED}✗ 失败${NC}"
        echo "响应: ${response}"
        return 1
    fi
}

# 断言响应包含特定字段
assert_field() {
    local field=$1
    local expected=$2
    local response_file="${OUTPUT_DIR}/temp_response.json"

    actual=$(jq -r ".${field}" "${response_file}" 2>/dev/null)

    if [ "${actual}" = "${expected}" ]; then
        echo -e "${GREEN}  ✓ 字段 ${field} = ${actual}${NC}"
        return 0
    else
        echo -e "${RED}  ✗ 字段 ${field}: 期望 ${expected}, 实际 ${actual}${NC}"
        return 1
    fi
}

# 记录测试结果
log_test_result() {
    local test_name=$1
    local status=$2
    local details=$3

    echo "### ${test_name}" >> "${REPORT_FILE}"
    echo "- 状态: ${status}" >> "${REPORT_FILE}"
    if [ -n "${details}" ]; then
        echo "- 详情: ${details}" >> "${REPORT_FILE}"
    fi
    echo "" >> "${REPORT_FILE}"
}

# 保存 API 响应快照
save_snapshot() {
    local test_name=$1
    local snapshot_file="${OUTPUT_DIR}/$(echo ${test_name} | tr ' ' '_').json"

    cp "${OUTPUT_DIR}/temp_response.json" "${snapshot_file}"
    echo "- 响应快照: \`${snapshot_file}\`" >> "${REPORT_FILE}"
}
```

**Step 2: 创建测试报告生成器**

```bash
# file: doc/test/scripts/test_report.sh

#!/bin/bash

# ============================================================
# 测试报告生成器
# ============================================================

OUTPUT_DIR="doc/test/output"
REPORT_FILE="${OUTPUT_DIR}/test_report.md"

# 生成统计摘要
generate_summary() {
    local total=$1
    local passed=$2
    local failed=$3

    echo "" >> "${REPORT_FILE}"
    echo "---" >> "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"
    echo "## 测试统计" >> "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"
    echo "| 指标 | 数量 |" >> "${REPORT_FILE}"
    echo "|------|------|" >> "${REPORT_FILE}"
    echo "| 总测试数 | ${total} |" >> "${REPORT_FILE}"
    echo "| 通过数 | ${passed} |" >> "${REPORT_FILE}"
    echo "| 失败数 | ${failed} |" >> "${REPORT_FILE}"
    echo "| 通过率 | $(awk "BEGIN {printf \"%.1f\", ${passed}/${total}*100}")% |" >> "${REPORT_FILE}"
    echo "" >> "${REPORT_FILE}"

    # 生成最终状态
    if [ ${failed} -eq 0 ]; then
        echo "## ✅ 所有测试通过" >> "${REPORT_FILE}"
    else
        echo "## ❌ 存在失败测试" >> "${REPORT_FILE}"
    fi
}

# 生成 HTML 报告
generate_html_report() {
    local html_file="${OUTPUT_DIR}/test_report.html"

    cat > "${html_file}" << 'EOF'
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>中介黑名单双表迁移测试报告</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; line-height: 1.6; margin: 40px; background: #f5f5f5; }
        .container { max-width: 1200px; margin: 0 auto; background: white; padding: 40px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        h1 { color: #2c3e50; border-bottom: 3px solid #3498db; padding-bottom: 10px; }
        h2 { color: #34495e; margin-top: 30px; }
        table { width: 100%; border-collapse: collapse; margin: 20px 0; }
        th, td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
        th { background-color: #3498db; color: white; }
        tr:hover { background-color: #f5f5f5; }
        .pass { color: #27ae60; font-weight: bold; }
        .fail { color: #e74c3c; font-weight: bold; }
        .status-pass { background-color: #d5f4e6; }
        .status-fail { background-color: #fadbd8; }
    </style>
</head>
<body>
    <div class="container">
        <h1>中介黑名单双表迁移测试报告</h1>
        <div id="content">
EOF

    # 转换 Markdown 为简单 HTML
    grep -E '^#{1,3}|^-|^\||^\* ' "${REPORT_FILE}" | sed 's/^### /<h2>/g; s/^## /<h2>/g; s/^# /<h1>/g; s/$/<\/h>/g' >> "${html_file}"

    cat >> "${html_file}" << 'EOF'
        </div>
    </div>
</body>
</html>
EOF

    echo "HTML 报告已生成: ${html_file}"
}
```

**Step 3: 设置执行权限**

```bash
chmod +x doc/test/scripts/test_utils.sh
chmod +x doc/test/scripts/test_report.sh
```

---

## 功能测试用例

### Task 2: 测试个人中介 CRUD 操作

**Files:**

- Create: `doc/test/scripts/test_person_intermediary.sh`

**Step 1: 编写个人中介测试脚本**

```bash
# file: doc/test/scripts/test_person_intermediary.sh

#!/bin/bash

# 加载工具函数
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/test_utils.sh"

# 初始化
init_output
login

# 测试计数器
TEST_COUNT=0
PASS_COUNT=0
FAIL_COUNT=0

# ============================================================
# Test 1: 新增个人中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 新增个人中介 ===${NC}"

person_data='{
    "name": "测试个人中介01",
    "certificateNo": "110101199001011234",
    "intermediaryType": "1",
    "status": "0",
    "dataSource": "MANUAL",
    "remark": "测试数据",
    "indivType": "中介",
    "indivSubType": "本人",
    "indivGender": "M",
    "indivCertType": "身份证",
    "indivPhone": "13800138000",
    "indivWechat": "test_wx_001",
    "indivAddress": "北京市朝阳区测试路123号",
    "indivCompany": "测试公司",
    "indivPosition": "测试员"
}'

if http_request "POST" "/ccdi/intermediary/person" "${person_data}" "新增个人中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 新增个人中介" "✅ 通过" "" "成功"

    # 验证响应字段
    assert_field "msg" "操作成功"
    save_snapshot "01_add_person"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 新增个人中介" "❌ 失败" ""
fi

# ============================================================
# Test 2: 查询个人中介详情
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 查询个人中介详情 ===${NC}"

# 获取新增的中介ID
person_id=$(jq -r '.data // empty' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

if [ -n "${person_id}" ] && [ "${person_id}" != "null" ]; then
    if http_request "GET" "/ccdi/intermediary/${person_id}" "" "查询个人中介详情"; then
        PASS_COUNT=$((PASS_COUNT + 1))

        # 验证字段
        assert_field "data.indivGender" "M"
        assert_field "data.indivCertType" "身份证"
        assert_field "data.dataSource" "MANUAL"

        log_test_result "Test ${TEST_COUNT}: 查询个人中介详情" "✅ 通过" "返回完整个人信息"
        save_snapshot "02_get_person_detail"
    else
        FAIL_COUNT=$((FAIL_COUNT + 1))
        log_test_result "Test ${TEST_COUNT}: 查询个人中介详情" "❌ 失败" ""
    fi
else
    echo -e "${RED}无法获取中介ID，跳过详情查询${NC}"
fi

# ============================================================
# Test 3: 修改个人中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 修改个人中介 ===${NC}"

person_edit_data='{
    "bizId": '${person_id}',
    "name": "测试个人中介01-已修改",
    "certificateNo": "110101199001011234",
    "indivPhone": "13900139000",
    "remark": "修改后的备注"
}'

if http_request "PUT" "/ccdi/intermediary/person" "${person_edit_data}" "修改个人中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 修改个人中介" "✅ 通过" "手机号已更新"
    save_snapshot "03_update_person"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 修改个人中介" "❌ 失败" ""
fi

# ============================================================
# Test 4: 验证数据库表数据（个人中介）
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 验证 ccdi_biz_intermediary 表数据 ===${NC}"

db_result=$(echo "SELECT biz_id, name, person_id, gender, mobile, date_source
FROM ccdi_biz_intermediary
WHERE person_id = '110101199001011234';" | mysql -u root -p123456 ccdi_db -N 2>/dev/null)

if [ -n "${db_result}" ]; then
    echo -e "${GREEN}✓ 数据库验证通过${NC}"
    echo "查询结果: ${db_result}"
    PASS_COUNT=$((PASS_COUNT + 1))

    log_test_result "Test ${TEST_COUNT}: 验证数据库表数据" "✅ 通过" "ccdi_biz_intermediary 表中存在该记录"
else
    echo -e "${RED}✗ 数据库验证失败${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 验证数据库表数据" "❌ 失败" "ccdi_biz_intermediary 表中未找到记录"
fi

# 输出统计
echo -e "\n${YELLOW}=== 个人中介测试统计 ===${NC}"
echo "总测试: ${TEST_COUNT}"
echo -e "通过: ${GREEN}${PASS_COUNT}${NC}"
echo -e "失败: ${RED}${FAIL_COUNT}${NC}"
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/scripts/test_person_intermediary.sh
```

---

### Task 3: 测试实体中介 CRUD 操作

**Files:**

- Create: `doc/test/scripts/test_entity_intermediary.sh`

**Step 1: 编写实体中介测试脚本**

```bash
# file: doc/test/scripts/test_entity_intermediary.sh

#!/bin/bash

# 加载工具函数
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/test_utils.sh"

# 初始化
init_output
login

# 测试计数器
TEST_COUNT=0
PASS_COUNT=0
FAIL_COUNT=0

# ============================================================
# Test 1: 新增实体中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 新增实体中介 ===${NC}"

entity_data='{
    "name": "测试实体中介有限公司",
    "certificateNo": "91110000123456789X",
    "intermediaryType": "2",
    "status": "0",
    "dataSource": "MANUAL",
    "remark": "实体中介测试数据",
    "corpCreditCode": "91110000123456789X",
    "corpType": "有限责任公司",
    "corpNature": "民营企业",
    "corpIndustryCategory": "制造业",
    "corpIndustry": "通用设备制造业",
    "corpEstablishDate": "2020-01-01",
    "corpAddress": "北京市海淀区测试大街456号",
    "corpLegalRep": "李四",
    "corpLegalCertType": "身份证",
    "corpLegalCertNo": "110101198001011234",
    "corpShareholder1": "股东A",
    "corpShareholder2": "股东B"
}'

if http_request "POST" "/ccdi/intermediary/entity" "${entity_data}" "新增实体中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 新增实体中介" "✅ 通过" ""
    save_snapshot "01_add_entity"

    # 验证响应
    assert_field "msg" "操作成功"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 新增实体中介" "❌ 失败" ""
fi

# ============================================================
# Test 2: 查询实体中介详情
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 查询实体中介详情 ===${NC}"

# 实体中介的主键是统一社会信用代码
entity_id="91110000123456789X"

if http_request "GET" "/ccdi/intermediary/0" "" "查询实体中介详情（使用ID=0）"; then
    PASS_COUNT=$((PASS_COUNT + 1))

    # 验证字段
    assert_field "data.corpCreditCode" "91110000123456789X"
    assert_field "data.corpNature" "民营企业"

    log_test_result "Test ${TEST_COUNT}: 查询实体中介详情" "✅ 通过" "返回完整实体信息"
    save_snapshot "02_get_entity_detail"
else
    # 如果ID=0查询失败，尝试通过列表查询验证
    echo -e "${YELLOW}尝试通过列表查询验证...${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
fi

# ============================================================
# Test 3: 修改实体中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 修改实体中介 ===${NC}"

entity_edit_data='{
    "socialCreditCode": "91110000123456789X",
    "enterpriseName": "测试实体中介有限公司-已修改",
    "corpAddress": "北京市海淀区修改后的地址999号",
    "remark": "修改后的实体中介备注"
}'

if http_request "PUT" "/ccdi/intermediary/entity" "${entity_edit_data}" "修改实体中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 修改实体中介" "✅ 通过" "地址已更新"
    save_snapshot "03_update_entity"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 修改实体中介" "❌ 失败" ""
fi

# ============================================================
# Test 4: 验证数据库表数据（实体中介）
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 验证 ccdi_enterprise_base_info 表数据 ===${NC}"

db_result=$(echo "SELECT social_credit_code, enterprise_name, risk_level, ent_source
FROM ccdi_enterprise_base_info
WHERE social_credit_code = '91110000123456789X'
AND ent_source = 'INTERMEDIARY';" | mysql -u root -p123456 ccdi_db -N 2>/dev/null)

if [ -n "${db_result}" ]; then
    # 验证 risk_level 和 ent_source
    if echo "${db_result}" | grep -q "1.*INTERMEDIARY"; then
        echo -e "${GREEN}✓ 数据库验证通过（risk_level=1, ent_source=INTERMEDIARY）${NC}"
        echo "查询结果: ${db_result}"
        PASS_COUNT=$((PASS_COUNT + 1))
        log_test_result "Test ${TEST_COUNT}: 验证数据库表数据" "✅ 通过" "实体中介已正确设置风险等级和来源"
    else
        echo -e "${RED}✗ risk_level 或 ent_source 不正确${NC}"
        echo "查询结果: ${db_result}"
        FAIL_COUNT=$((FAIL_COUNT + 1))
        log_test_result "Test ${TEST_COUNT}: 验证数据库表数据" "❌ 失败" "risk_level 或 ent_source 值不正确"
    fi
else
    echo -e "${RED}✗ 数据库验证失败${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 验证数据库表数据" "❌ 失败" "ccdi_enterprise_base_info 表中未找到记录"
fi

# 输出统计
echo -e "\n${YELLOW}=== 实体中介测试统计 ===${NC}"
echo "总测试: ${TEST_COUNT}"
echo -e "通过: ${GREEN}${PASS_COUNT}${NC}"
echo -e "失败: ${RED}${FAIL_COUNT}${NC}"
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/scripts/test_entity_intermediary.sh
```

---

### Task 4: 测试分页查询和类型过滤

**Files:**

- Create: `doc/test/scripts/test_list_query.sh`

**Step 1: 编写列表查询测试脚本**

```bash
# file: doc/test/scripts/test_list_query.sh

#!/bin/bash

# 加载工具函数
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/test_utils.sh"

# 初始化
init_output
login

# 测试计数器
TEST_COUNT=0
PASS_COUNT=0
FAIL_COUNT=0

# ============================================================
# Test 1: 查询全部中介（UNION查询）
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 查询全部中介列表 ===${NC}"

if http_request "GET" "/ccdi/intermediary/list?pageNum=1&pageSize=10" "" "查询全部中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))

    # 验证返回数据结构
    total=$(jq -r '.total' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)
    rows_count=$(jq -r '.rows | length' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

    echo "总记录数: ${total}"
    echo "当前页记录数: ${rows_count}"

    log_test_result "Test ${TEST_COUNT}: 查询全部中介列表" "✅ 通过" "UNION查询成功，共 ${total} 条记录"
    save_snapshot "01_list_all"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 查询全部中介列表" "❌ 失败" ""
fi

# ============================================================
# Test 2: 仅查询个人中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 仅查询个人中介 ===${NC}"

if http_request "GET" "/ccdi/intermediary/list?pageNum=1&pageSize=10&intermediaryType=1" "" "查询个人中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))

    # 验证所有返回的中介类型都是"1"（个人）
    all_person=$(jq -r '.rows[] | .intermediaryType' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null | grep -c "1" || echo "0")
    total_rows=$(jq -r '.rows | length' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

    if [ "${all_person}" -eq "${total_rows}" ]; then
        echo -e "${GREEN}✓ 类型过滤验证通过（全部为个人中介）${NC}"
        PASS_COUNT=$((PASS_COUNT + 1))
    else
        echo -e "${RED}✗ 类型过滤验证失败${NC}"
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi

    log_test_result "Test ${TEST_COUNT}: 仅查询个人中介" "✅ 通过" "类型过滤正确"
    save_snapshot "02_list_person_only"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 仅查询个人中介" "❌ 失败" ""
fi

# ============================================================
# Test 3: 仅查询实体中介
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 仅查询实体中介 ===${NC}"

if http_request "GET" "/ccdi/intermediary/list?pageNum=1&pageSize=10&intermediaryType=2" "" "查询实体中介"; then
    PASS_COUNT=$((PASS_COUNT + 1))

    # 验证所有返回的中介类型都是"2"（实体）
    all_entity=$(jq -r '.rows[] | .intermediaryType' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null | grep -c "2" || echo "0")
    total_rows=$(jq -r '.rows | length' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

    if [ "${all_entity}" -eq "${total_rows}" ]; then
        echo -e "${GREEN}✓ 类型过滤验证通过（全部为实体中介）${NC}"
        PASS_COUNT=$((PASS_COUNT + 1))
    else
        echo -e "${RED}✗ 类型过滤验证失败${NC}"
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi

    log_test_result "Test ${TEST_COUNT}: 仅查询实体中介" "✅ 通过" "类型过滤正确"
    save_snapshot "03_list_entity_only"
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 仅查询实体中介" "❌ 失败" ""
fi

# ============================================================
# Test 4: 测试分页功能
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 测试分页功能 ===${NC}"

# 请求第一页
if http_request "GET" "/ccdi/intermediary/list?pageNum=1&pageSize=5" "" "请求第1页（每页5条）"; then
    rows_page1=$(jq -r '.rows | length' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

    # 请求第二页
    if http_request "GET" "/ccdi/intermediary/list?pageNum=2&pageSize=5" "" "请求第2页（每页5条）"; then
        rows_page2=$(jq -r '.rows | length' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null)

        if [ "${rows_page1}" -le 5 ] && [ "${rows_page2}" -le 5 ]; then
            echo -e "${GREEN}✓ 分页验证通过（第1页: ${rows_page1}条, 第2页: ${rows_page2}条）${NC}"
            PASS_COUNT=$((PASS_COUNT + 1))
            log_test_result "Test ${TEST_COUNT}: 测试分页功能" "✅ 通过" "分页正确"
        else
            echo -e "${RED}✗ 分页验证失败${NC}"
            FAIL_COUNT=$((FAIL_COUNT + 1))
            log_test_result "Test ${TEST_COUNT}: 测试分页功能" "❌ 失败" "分页数量不正确"
        fi
    fi
else
    FAIL_COUNT=$((FAIL_COUNT + 1))
fi

# ============================================================
# Test 5: 验证枚举字段只返回代码值
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 验证枚举字段只返回代码值 ===${NC}"

# 检查响应中是否包含枚举名称字段（应该不存在）
has_person_type_name=$(jq -r '.rows[] | has("intermediaryTypeName")' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null | grep -c "true" || echo "0")
has_status_name=$(jq -r '.rows[] | has("statusName")' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null | grep -c "true" || echo "0")

if [ "${has_person_type_name}" -eq 0 ] && [ "${has_status_name}" -eq 0 ]; then
    echo -e "${GREEN}✓ 枚举字段验证通过（仅返回代码值）${NC}"
    PASS_COUNT=$((PASS_COUNT + 1))

    # 显示示例枚举代码值
    echo "示例数据:"
    jq -r '.rows[0] | {intermediaryType, status, dataSource}' "${OUTPUT_DIR}/temp_response.json" 2>/dev/null || echo "无法解析"

    log_test_result "Test ${TEST_COUNT}: 验证枚举字段" "✅ 通过" "后端只返回代码值，无名称字段"
else
    echo -e "${RED}✗ 枚举字段验证失败（存在名称字段）${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 验证枚举字段" "❌ 失败" "存在不应有的枚举名称字段"
fi

# 输出统计
echo -e "\n${YELLOW}=== 列表查询测试统计 ===${NC}"
echo "总测试: ${TEST_COUNT}"
echo -e "通过: ${GREEN}${PASS_COUNT}${NC}"
echo -e "失败: ${RED}${FAIL_COUNT}${NC}"
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/scripts/test_list_query.sh
```

---

### Task 5: 测试批量导入功能

**Files:**

- Create: `doc/test/scripts/test_import.sh`
- Create: `doc/test/data/person_import.xlsx`
- Create: `doc/test/data/entity_import.xlsx`

**Step 1: 准备测试数据**

创建 Excel 文件需要特殊工具，我们改用 CSV 格式或直接使用 API 测试：

```bash
# file: doc/test/scripts/test_import.sh

#!/bin/bash

# 加载工具函数
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/test_utils.sh"

# 初始化
init_output
login

# 测试计数器
TEST_COUNT=0
PASS_COUNT=0
FAIL_COUNT=0

# ============================================================
# Test 1: 下载个人中介导入模板
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 下载个人中介导入模板 ===${NC}"

response=$(curl -s -X POST "${BASE_URL}/ccdi/intermediary/importPersonTemplate" \
    -H "Authorization: Bearer ${TOKEN}" \
    -o "${OUTPUT_DIR}/person_template.xlsx" \
    -w "%{http_code}")

if [ "${response}" = "200" ]; then
    if [ -f "${OUTPUT_DIR}/person_template.xlsx" ]; then
        echo -e "${GREEN}✓ 模板下载成功${NC}"
        echo "文件大小: $(du -h "${OUTPUT_DIR}/person_template.xlsx" | cut -f1)"
        PASS_COUNT=$((PASS_COUNT + 1))
        log_test_result "Test ${TEST_COUNT}: 下载个人中介模板" "✅ 通过" "文件已保存"
    else
        echo -e "${RED}✗ 文件未生成${NC}"
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
else
    echo -e "${RED}✗ HTTP 状态码: ${response}${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 下载个人中介模板" "❌ 失败" "HTTP ${response}"
fi

# ============================================================
# Test 2: 下载实体中介导入模板
# ============================================================
TEST_COUNT=$((TEST_COUNT + 1))

echo -e "\n${YELLOW}=== Test ${TEST_COUNT}: 下载实体中介导入模板 ===${NC}"

response=$(curl -s -X POST "${BASE_URL}/ccdi/intermediary/importEntityTemplate" \
    -H "Authorization: Bearer ${TOKEN}" \
    -o "${OUTPUT_DIR}/entity_template.xlsx" \
    -w "%{http_code}")

if [ "${response}" = "200" ]; then
    if [ -f "${OUTPUT_DIR}/entity_template.xlsx" ]; then
        echo -e "${GREEN}✓ 模板下载成功${NC}"
        echo "文件大小: $(du -h "${OUTPUT_DIR}/entity_template.xlsx" | cut -f1)"
        PASS_COUNT=$((PASS_COUNT + 1))
        log_test_result "Test ${TEST_COUNT}: 下载实体中介模板" "✅ 通过" "文件已保存"
    else
        echo -e "${RED}✗ 文件未生成${NC}"
        FAIL_COUNT=$((FAIL_COUNT + 1))
    fi
else
    echo -e "${RED}✗ HTTP 状态码: ${response}${NC}"
    FAIL_COUNT=$((FAIL_COUNT + 1))
    log_test_result "Test ${TEST_COUNT}: 下载实体中介模板" "❌ 失败" "HTTP ${response}"
fi

# ============================================================
# 注意：实际导入测试需要准备有效的 Excel 文件
# 此处仅测试接口可访问性
# ============================================================

echo -e "\n${YELLOW}=== 批量导入测试统计 ===${NC}"
echo "总测试: ${TEST_COUNT}"
echo -e "通过: ${GREEN}${PASS_COUNT}${NC}"
echo -e "失败: ${RED}${FAIL_COUNT}${NC}"
echo -e "\n${YELLOW}注意: 完整的导入测试需要手动准备 Excel 文件${NC}"
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/scripts/test_import.sh
```

---

## 综合测试执行

### Task 6: 创建主测试执行脚本

**Files:**

- Create: `doc/test/run_all_tests.sh`

**Step 1: 编写主执行脚本**

```bash
# file: doc/test/run_all_tests.sh

#!/bin/bash

# ============================================================
# 中介黑名单双表迁移测试主执行脚本
# ============================================================

set -e  # 遇到错误立即退出

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
OUTPUT_DIR="${SCRIPT_DIR}/output"

echo "=========================================="
echo "中介黑名单双表迁移测试"
echo "=========================================="
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""

# 清理旧的输出
rm -rf "${OUTPUT_DIR}"
mkdir -p "${OUTPUT_DIR}"

# 总计数据
TOTAL_TESTS=0
TOTAL_PASSED=0
TOTAL_FAILED=0

# ============================================================
# 1. 运行个人中介测试
# ============================================================
echo -e "\n=========================================="
echo "第1部分: 个人中介测试"
echo "=========================================="

if bash "${SCRIPT_DIR}/scripts/test_person_intermediary.sh"; then
    echo "个人中介测试完成"
else
    echo "个人中介测试失败"
fi

# ============================================================
# 2. 运行实体中介测试
# ============================================================
echo -e "\n=========================================="
echo "第2部分: 实体中介测试"
echo "=========================================="

if bash "${SCRIPT_DIR}/scripts/test_entity_intermediary.sh"; then
    echo "实体中介测试完成"
else
    echo "实体中介测试失败"
fi

# ============================================================
# 3. 运行列表查询测试
# ============================================================
echo -e "\n=========================================="
echo "第3部分: 列表查询测试"
echo "=========================================="

if bash "${SCRIPT_DIR}/scripts/test_list_query.sh"; then
    echo "列表查询测试完成"
else
    echo "列表查询测试失败"
fi

# ============================================================
# 4. 运行导入测试
# ============================================================
echo -e "\n=========================================="
echo "第4部分: 导入功能测试"
echo "=========================================="

if bash "${SCRIPT_DIR}/scripts/test_import.sh"; then
    echo "导入功能测试完成"
else
    echo "导入功能测试失败"
fi

# ============================================================
# 5. 生成最终报告
# ============================================================
echo -e "\n=========================================="
echo "生成测试报告"
echo "=========================================="

bash "${SCRIPT_DIR}/scripts/test_report.sh"

echo ""
echo "=========================================="
echo "测试完成"
echo "=========================================="
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
echo "报告位置:"
echo "  Markdown: ${OUTPUT_DIR}/test_report.md"
echo "  HTML:     ${OUTPUT_DIR}/test_report.html"
echo "  响应快照: ${OUTPUT_DIR}/"
echo ""

# 显示报告摘要
if [ -f "${OUTPUT_DIR}/test_report.md" ]; then
    echo "=========================================="
    echo "测试摘要"
    echo "=========================================="
    grep -A 5 "## 测试统计" "${OUTPUT_DIR}/test_report.md" || echo "无法读取统计信息"
fi
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/run_all_tests.sh
```

---

## 执行测试

### Task 7: 执行完整测试套件

**Step 1: 确保后端服务运行**

```bash
# 检查后端服务状态
curl -s http://localhost:8080/actuator/health || echo "后端服务未启动，请先启动"
```

**Step 2: 运行完整测试**

```bash
cd /d/ccdi/ccdi
bash doc/test/run_all_tests.sh
```

**Step 3: 查看测试报告**

```bash
# Markdown 报告
cat doc/test/output/test_report.md

# 或在浏览器中查看 HTML 报告
start doc/test/output/test_report.html  # Windows
# open doc/test/output/test_report.html  # macOS
# xdg-open doc/test/output/test_report.html  # Linux
```

---

## 预期测试结果

### 成功标准

所有测试应该通过，验证以下功能：

1. **个人中介 CRUD**
    - ✅ 个人中介数据插入到 `ccdi_biz_intermediary` 表
    - ✅ 新增、查询、修改功能正常
    - ✅ 数据来源正确设置为 "MANUAL"

2. **实体中介 CRUD**
    - ✅ 实体中介数据插入到 `ccdi_enterprise_base_info` 表
    - ✅ 新增、查询、修改功能正常
    - ✅ `risk_level` 自动设置为 "1"
    - ✅ `ent_source` 自动设置为 "INTERMEDIARY"

3. **分页查询**
    - ✅ UNION ALL 查询正确合并两张表数据
    - ✅ 分页功能正常（pageNum, pageSize）
    - ✅ 类型过滤正确（intermediaryType=1/2）
    - ✅ 枚举字段只返回代码值，无名称字段

4. **导入功能**
    - ✅ 模板下载接口可访问
    - ✅ Excel 文件正确生成

### 失败场景处理

如果测试失败：

1. **HTTP 401**: Token 过期，检查登录接口
2. **HTTP 403**: 权限不足，检查用户权限配置
3. **数据库查询失败**: 检查数据库连接和表结构
4. **断言失败**: 检查业务逻辑实现

---

## 清理测试数据

### Task 8: 数据清理脚本

**Files:**

- Create: `doc/test/cleanup_test_data.sh`

**Step 1: 编写数据清理脚本**

```bash
# file: doc/test/cleanup_test_data.sh

#!/bin/bash

# ============================================================
# 清理测试数据
# ============================================================

echo "正在清理测试数据..."

# 清理个人中介测试数据
echo "清理 ccdi_biz_intermediary 表测试数据..."
mysql -u root -p123456 ccdi_db << 'EOF'
DELETE FROM ccdi_biz_intermediary
WHERE person_id IN ('110101199001011234');
EOF

echo "✓ 个人中介测试数据已清理"

# 清理实体中介测试数据
echo "清理 ccdi_enterprise_base_info 表测试数据..."
mysql -u root -p123456 ccdi_db << 'EOF'
DELETE FROM ccdi_enterprise_base_info
WHERE social_credit_code IN ('91110000123456789X')
AND ent_source = 'INTERMEDIARY';
EOF

echo "✓ 实体中介测试数据已清理"

echo ""
echo "测试数据清理完成"
```

**Step 2: 设置执行权限**

```bash
chmod +x doc/test/cleanup_test_data.sh
```

**Step 3: 执行清理**

```bash
bash doc/test/cleanup_test_data.sh
```

---

## 附录

### A. 测试数据示例

**个人中介测试数据:**

```json
{
  "name": "测试个人中介01",
  "certificateNo": "110101199001011234",
  "intermediaryType": "1",
  "status": "0",
  "dataSource": "MANUAL",
  "indivGender": "M",
  "indivPhone": "13800138000"
}
```

**实体中介测试数据:**

```json
{
  "name": "测试实体中介有限公司",
  "certificateNo": "91110000123456789X",
  "intermediaryType": "2",
  "corpCreditCode": "91110000123456789X",
  "corpNature": "民营企业",
  "corpLegalRep": "李四"
}
```

### B. 数据库验证查询

```sql
-- 验证个人中介数据
SELECT biz_id, name, person_id, gender, mobile, date_source
FROM ccdi_biz_intermediary
WHERE person_id = '110101199001011234';

-- 验证实体中介数据
SELECT social_credit_code, enterprise_name, risk_level, ent_source
FROM ccdi_enterprise_base_info
WHERE social_credit_code = '91110000123456789X';

-- 验证 UNION 查询结果
SELECT '1' AS type, COUNT(*) AS count
FROM ccdi_biz_intermediary
UNION ALL
SELECT '2' AS type, COUNT(*) AS count
FROM ccdi_enterprise_base_info
WHERE ent_source = 'INTERMEDIARY';
```

### C. 常见问题排查

| 问题                        | 可能原因    | 解决方法         |
|---------------------------|---------|--------------|
| 401 Unauthorized          | Token过期 | 重新登录获取新Token |
| 403 Forbidden             | 权限不足    | 检查用户角色和权限配置  |
| 500 Internal Server Error | 后端代码错误  | 查看后端日志，检查异常栈 |
| 数据库连接失败                   | 数据库未启动  | 检查MySQL服务状态  |
| UNION查询结果为空               | 表中无数据   | 先执行插入操作      |

---

**文档版本:** v1.0
**创建日期:** 2026-02-04
**最后更新:** 2026-02-04
**维护者:** CCDI 开发团队
