#!/bin/bash # 居家上门服务系统 — 接口集成测试脚本 # 用法: chmod +x test-api.sh && ./test-api.sh set -e BASE="http://localhost:18080/api/hss" TS=$(date +%s) PASS=0; FAIL=0 GREEN='\033[0;32m'; RED='\033[0;31m'; NC='\033[0m' hdrs() { echo "-H 'Content-Type: application/json' -H 'X-Tenant-Id: 1' -H 'Idempotency-Key: test-$(date +%s%N)'"; } ok() { PASS=$((PASS+1)); echo -e "${GREEN}PASS${NC} $1"; } fail() { FAIL=$((FAIL+1)); echo -e "${RED}FAIL${NC} $1 — $2"; } check() { local code=$1; shift; if [ "$code" -eq 200 ] || [ "$code" -eq 302 ]; then ok "$*"; else fail "$*" "HTTP $code"; fi; } check_json() { echo "$1" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']==$2,f'Expected code $2 got {d[\"code\"]}: {d.get(\"message\",\"\")}'" 2>&1; } echo "============================================" echo " 居家上门服务系统 API 集成测试" echo "============================================" echo "" # === 1. Infrastructure === echo "--- 1. 基础设施 ---" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/applications?page=1&size=1") check "$R" "OpenAPI可达" R=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:18080/swagger-ui/index.html") check "$R" "Swagger UI" R=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:18080/api-docs") check "$R" "OpenAPI JSON" echo "" # === 2. 主链路:申请 → 评估 → 方案 → 计划 → 工单 → 执行 → 验收 → 结算 → 归档 === echo "--- 2. 主链路全流程 ---" # Step 1: 创建申请 RESP=$(curl -s -X POST "$BASE/applications" \ -H "Content-Type: application/json" \ -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-app-1" \ -d '{"patientId":"2001","serviceType":"HOME_CARE","channel":"WECHAT","contactName":"张三","contactPhone":"13812345678","address":"梅江区金山街道XX小区3栋201","regionCode":"441402001"}') echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']==200;assert d['data']['status']=='DRAFT'" 2>&1 && ok "创建申请(DRAFT)" || fail "创建申请(DRAFT)" "$RESP" APP_ID=$(echo "$RESP" | python3 -c "import sys,json;print(json.loads(sys.stdin.read())['data']['id'])") # Step 2: 提交申请 RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/submit" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-app-2") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['data']['status']=='PENDING_ACCEPTANCE'" 2>&1 && ok "提交申请(PENDING_ACCEPTANCE)" || fail "提交申请" "$RESP" # Step 3: 受理通过 RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/accept" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:RECEPTIONIST" -H "Idempotency-Key: e2e-run3-${TS}-app-3") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['data']['status']=='PENDING_ASSESSMENT'" 2>&1 && ok "受理通过(PENDING_ASSESSMENT)" || fail "受理通过" "$RESP" # Step 4: 派发评估 RESP=$(curl -s -X POST "$BASE/assessments/$APP_ID/assign" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:RECEPTIONIST" -H "Idempotency-Key: e2e-run3-${TS}-asm-1" \ -d '{"assessorId": 100}') echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']==200" 2>&1 && ok "派发评估" || fail "派发评估" "$RESP" ASM_ID=$(echo "$RESP" | python3 -c "import sys,json;print(json.loads(sys.stdin.read())['data']['id'])" 2>/dev/null || echo "1") # Step 5: 提交评估报告 RESP=$(curl -s -X POST "$BASE/assessments/$ASM_ID/submit" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:ASSESSOR" -H "Idempotency-Key: e2e-run3-${TS}-asm-2" \ -d '{"careLevel":"LEVEL_3","riskLevel":"MEDIUM","reportContent":"{\"mobility\":\"limited\",\"cognition\":\"normal\"}"}') echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']==200" 2>&1 && ok "提交评估报告" || fail "提交评估报告" "$RESP" # Step 6: 创建方案 RESP=$(curl -s -X POST "$BASE/service-plans" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PLANNER" -H "Idempotency-Key: e2e-run3-${TS}-plan-1" \ -d '{"applicationId":'$APP_ID',"assessmentTaskId":'$ASM_ID',"items":[{"serviceItemId":1,"itemName":"助洁服务","unitPrice":50.00,"frequency":3,"standardDuration":60,"evidenceRequired":false}]}') echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']==200" 2>&1 && ok "创建方案(PLAN_DRAFT)" || fail "创建方案" "$RESP" PLAN_ID=$(echo "$RESP" | python3 -c "import sys,json;print(json.loads(sys.stdin.read())['data']['id'])") # Step 7: 提交签署 RESP=$(curl -s -X POST "$BASE/service-plans/$PLAN_ID/submit-sign" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PLANNER" -H "Idempotency-Key: e2e-run3-${TS}-plan-2") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['data']['status']=='PLAN_PENDING_SIGN'" 2>&1 && ok "提交签署(PENDING_SIGN)" || fail "提交签署" "$RESP" # Step 8: 签署方案(自动生成服务计划) RESP=$(curl -s -X POST "$BASE/service-plans/$PLAN_ID/sign" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-plan-3") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['data']['status']=='PLAN_EFFECTIVE'" 2>&1 && ok "签署方案(PLAN_EFFECTIVE)" || fail "签署方案" "$RESP" # Step 9: 验证服务计划已生成 SCH_RESP=$(curl -s "$BASE/service-schedules/plans/$PLAN_ID" -H "X-Tenant-Id: 1") echo "$SCH_RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert len(d['data'])>0" 2>&1 && ok "服务计划已自动生成" || fail "服务计划生成" "$SCH_RESP" echo "" echo "--- 3. 异常流程 ---" # Step 10: 非法状态转换(已通过不能再次接受) RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/accept" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:RECEPTIONIST" -H "Idempotency-Key: e2e-run3-${TS}-fail-1") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']!=200" 2>&1 && ok "拦截非法状态转换" || fail "非法状态转换" "$RESP" # Step 11: 角色越权(PATIENT 不能受理) RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/accept" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-fail-2") echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']!=200" 2>&1 && ok "拦截角色越权" || fail "角色越权" "$RESP" # Step 12: 退回缺少原因 RESP=$(curl -s -X POST "$BASE/applications/99999/return" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:RECEPTIONIST" -H "Idempotency-Key: e2e-run3-${TS}-fail-3" \ -d '{"reason":""}') echo "$RESP" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());assert d['code']!=200" 2>&1 && ok "拒绝空退回原因" || fail "空退回原因" "$RESP" echo "" echo "--- 4. 幂等验证 ---" # 重复提交同一 Idempotency-Key R1=$(curl -s -X POST "$BASE/applications" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-idem-dup" \ -d '{"patientId":"3001","serviceType":"BATH_ASSIST","channel":"PHONE","contactName":"重复测试","contactPhone":"13900000000","address":"梅县区","regionCode":"441403001"}' | python3 -c "import sys,json;print(json.loads(sys.stdin.read())['data']['id'])") sleep 1 R2=$(curl -s -X POST "$BASE/applications" \ -H "Content-Type: application/json" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:PATIENT" -H "Idempotency-Key: e2e-run3-${TS}-idem-dup" \ -d '{"patientId":"3001","serviceType":"BATH_ASSIST","channel":"PHONE","contactName":"重复测试","contactPhone":"13900000000","address":"梅县区","regionCode":"441403001"}' | python3 -c "import sys,json;print(json.loads(sys.stdin.read())['data']['id'])") if [ "$R1" = "$R2" ]; then ok "幂等:重复提交返回同一结果($R1)"; else fail "幂等重复提交" "R1=$R1 R2=$R2"; fi echo "" echo "--- 5. 数据脱敏 ---" R_ADMIN=$(curl -s "$BASE/applications/$APP_ID" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:ADMIN") R_STAFF=$(curl -s "$BASE/applications/$APP_ID" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:STAFF") ADDR_ADMIN=$(echo "$R_ADMIN" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());print(d['data'].get('address',''))") ADDR_STAFF=$(echo "$R_STAFF" | python3 -c "import sys,json;d=json.loads(sys.stdin.read());print(d['data'].get('address',''))") if [ "$ADDR_ADMIN" != "$ADDR_STAFF" ] && echo "$ADDR_STAFF" | grep -q "\*\*\*\*"; then ok "角色脱敏:ADMIN完整地址, STAFF已脱敏" else fail "角色脱敏" "ADMIN=$ADDR_ADMIN STAFF=$ADDR_STAFF" fi echo "" echo "--- 6. Delivery 接口 ---" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/delivery/workbench" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:STAFF") check "$R" "Delivery工作台" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/delivery/work-orders/today" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:STAFF") check "$R" "Delivery今日工单" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/delivery/messages" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:STAFF") check "$R" "Delivery消息" echo "" echo "--- 7. 管理端 ---" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/admin/dashboard" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:ADMIN") check "$R" "管理端仪表盘" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/admin/work-orders" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:DISPATCHER") check "$R" "管理端工单列表" echo "" echo "--- 8. 监管/合规/主数据/运力/看板/绩效/智能助手 ---" for endpoint in \ "supervision/spot-checks:ADMIN" \ "compliance/consents/patients/1:PATIENT" \ "master/service-items:PATIENT" \ "master/price-rules:PATIENT" \ "master/regions:PATIENT" \ "master/staff:DISPATCHER" \ "capacity/grids:DISPATCHER" \ "capacity/dashboard:DISPATCHER" \ "analytics/quality:SUPERVISOR" \ "analytics/heatmap:SUPERVISOR" \ "analytics/continuity:PATIENT" \ "analytics/staff-load:DISPATCHER" \ "analytics/summary:ADMIN" \ "performance/ranking:DISPATCHER"; do path="${endpoint%%:*}" role="${endpoint##*:}" R=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/$path" -H "X-Tenant-Id: 1" -H "X-Org-Id: 1" -H "X-User-Role:$role") check "$R" "$path" done echo "" echo "============================================" echo -e " 结果: ${GREEN}$PASS 通过${NC} / ${RED}$FAIL 失败${NC}" echo "============================================"