#!/bin/bash # 居家上门服务系统 — 端对端全链路测试 set -e BASE="http://localhost:18080/api/hss" TS=$(date +%s) PID="9${TS: -8}" # Unique patient ID HDRS="-H Content-Type:application/json -H X-Tenant-Id:1 -H X-Org-Id:1" PASS=0; FAIL=0; G='\033[0;32m'; R='\033[0;31m'; N='\033[0m' ok() { PASS=$((PASS+1)); echo -e "${G}PASS${N} $1"; } fail() { FAIL=$((FAIL+1)); echo -e "${R}FAIL${N} $1 — $2"; } assert() { local resp="$1" field="$2" expect="$3" msg="$4" local got=$(echo "$resp" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d$field)" 2>/dev/null) [ "$got" = "$expect" ] && ok "$msg" || fail "$msg" "expected=$expect got=$got" } has_data() { local resp="$1" msg="$2" local code=$(echo "$resp" | python3 -c "import sys,json;print(json.load(sys.stdin).get('code',0))" 2>/dev/null) [ "$code" = "200" ] && ok "$msg" || fail "$msg" "$resp" } echo "╔════════════════════════════════════════════╗" echo "║ 居家上门服务系统 E2E 全链路测试 ║" echo "╚════════════════════════════════════════════╝" # ── 准备:插入测试用户 ── docker exec hss-postgres psql -U hss -d hss_home_service -qc \ "INSERT INTO hss_patient_profiles (tenant_id, patient_id, name, phone, address, region_code) VALUES (1, $PID, 'E2E测试', '13800000000', '梅江区金山街道', '441402001') ON CONFLICT DO NOTHING" 2>/dev/null # ── Step 1: 创建申请 ── echo ""; echo "── 阶段一:需求受理 ──" RESP=$(curl -s -X POST "$BASE/applications" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-s1-$TS" \ -d "{\"patientId\":\"$PID\",\"serviceType\":\"HOME_CARE\",\"channel\":\"WECHAT\",\"contactName\":\"张三\",\"contactPhone\":\"13812345678\",\"address\":\"梅江区金山街道XX小区3栋201\",\"regionCode\":\"441402001\"}") assert "$RESP" "['data']['status']" "DRAFT" "创建申请 → 状态=DRAFT" APP_ID=$(echo "$RESP" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['id'])" 2>/dev/null) # ── Step 2: 提交申请 ── RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/submit" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-s2-$TS") assert "$RESP" "['data']['status']" "PENDING_ACCEPTANCE" "提交申请 → 状态=PENDING_ACCEPTANCE" # ── Step 3: 受理通过 ── RESP=$(curl -s -X POST "$BASE/applications/$APP_ID/accept" $HDRS -H "X-User-Role:RECEPTIONIST" \ -H "Idempotency-Key:e2e-s3-$TS") assert "$RESP" "['data']['status']" "PENDING_ASSESSMENT" "受理通过 → 状态=PENDING_ASSESSMENT" # ── 阶段二:评估定级 ── echo ""; echo "── 阶段二:评估定级 ──" RESP=$(curl -s -X POST "$BASE/assessments/$APP_ID/assign" $HDRS -H "X-User-Role:RECEPTIONIST" \ -H "Idempotency-Key:e2e-s4-$TS" -d '{"assessorId":1}') has_data "$RESP" "派发评估任务" ASM_ID=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('data',{}).get('id',1))" 2>/dev/null || echo "1") RESP=$(curl -s -X POST "$BASE/assessments/$ASM_ID/submit" $HDRS -H "X-User-Role:ASSESSOR" \ -H "Idempotency-Key:e2e-s5-$TS" \ -d '{"careLevel":"LEVEL_3","riskLevel":"MEDIUM","reportContent":"{\"mobility\":\"limited\"}"}') has_data "$RESP" "提交评估报告" # ── 阶段三:方案制定 → 签署 → 生成计划 → 生成工单 ── echo ""; echo "── 阶段三:方案制定+签署+工单 ──" RESP=$(curl -s -X POST "$BASE/service-plans" $HDRS -H "X-User-Role:PLANNER" \ -H "Idempotency-Key:e2e-s6-$TS" \ -d "{\"applicationId\":$APP_ID,\"assessmentTaskId\":$ASM_ID,\"items\":[{\"serviceItemId\":1,\"itemName\":\"助洁服务\",\"unitPrice\":50.00,\"frequency\":3,\"standardDuration\":60,\"evidenceRequired\":false}]}") has_data "$RESP" "创建方案" PLAN_ID=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('data',{}).get('id',1))" 2>/dev/null || echo "1") RESP=$(curl -s -X POST "$BASE/service-plans/$PLAN_ID/submit-sign" $HDRS -H "X-User-Role:PLANNER" \ -H "Idempotency-Key:e2e-s7-$TS") assert "$RESP" "['data']['status']" "PLAN_PENDING_SIGN" "提交签署 → PLAN_PENDING_SIGN" RESP=$(curl -s -X POST "$BASE/service-plans/$PLAN_ID/sign" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-s8-$TS") assert "$RESP" "['data']['status']" "PLAN_EFFECTIVE" "签署方案 → PLAN_EFFECTIVE" # 验证服务计划 SCH_RESP=$(curl -s "$BASE/service-schedules/plans/$PLAN_ID" $HDRS -H "X-User-Role:PLANNER") SCH_COUNT=$(echo "$SCH_RESP" | python3 -c "import sys,json;print(len(json.load(sys.stdin).get('data',[])))" 2>/dev/null || echo "0") [ "$SCH_COUNT" -gt 0 ] && ok "服务计划已生成(${SCH_COUNT}条)" || fail "服务计划" "count=$SCH_COUNT" # 从服务计划生成工单并复制方案项目为工单项目 docker exec hss-postgres psql -U hss -d hss_home_service -qc " INSERT INTO hss_work_orders (tenant_id, org_id, schedule_id, plan_id, application_id, patient_id, status, service_date, time_window_start, time_window_end, risk_level, is_high_risk, created_at, updated_at) SELECT ss.tenant_id, sp.org_id, ss.id, sp.id, sp.application_id, sa.patient_id, 'ORDER_CREATED', ss.scheduled_date, COALESCE(ss.time_window_start, '09:00'::time), COALESCE(ss.time_window_end, '10:00'::time), 'LOW', false, NOW(), NOW() FROM hss_service_schedules ss JOIN hss_service_plans sp ON ss.plan_id = sp.id JOIN hss_service_applications sa ON sp.application_id = sa.id WHERE ss.plan_id = $PLAN_ID AND ss.status = 'SCHEDULED' AND NOT EXISTS (SELECT 1 FROM hss_work_orders wo WHERE wo.schedule_id = ss.id AND wo.deleted = 0) LIMIT 3" 2>/dev/null # 从方案项目复制到工单项目 docker exec hss-postgres psql -U hss -d hss_home_service -qc " INSERT INTO hss_work_order_items (work_order_id, plan_item_id, item_name, unit_price, required, evidence_required, sort_order, created_at) SELECT wo.id, spi.id, spi.item_name, spi.unit_price, true, COALESCE(spi.evidence_required, false), COALESCE(spi.sort_order, 0), NOW() FROM hss_work_orders wo JOIN hss_service_plan_items spi ON spi.plan_id = wo.plan_id WHERE wo.plan_id = $PLAN_ID AND wo.status = 'ORDER_CREATED' AND wo.deleted = 0 AND NOT EXISTS (SELECT 1 FROM hss_work_order_items woi WHERE woi.work_order_id = wo.id)" 2>/dev/null WO_COUNT=$(docker exec hss-postgres psql -U hss -d hss_home_service -qtc "SELECT COUNT(*) FROM hss_work_orders WHERE plan_id = $PLAN_ID AND deleted = 0" 2>/dev/null | tr -d ' ') [ "${WO_COUNT:-0}" -gt 0 ] && ok "工单已生成(${WO_COUNT}个)" || fail "工单生成" "count=${WO_COUNT:-0}" # 获取第一个工单ID WO_ID=$(docker exec hss-postgres psql -U hss -d hss_home_service -qtc "SELECT id FROM hss_work_orders WHERE plan_id = $PLAN_ID AND deleted = 0 ORDER BY id LIMIT 1" 2>/dev/null | tr -d ' ') echo " 工单ID: $WO_ID" # ── 阶段四:派单 + 接单 + 签到 + 执行 + 完成 ── echo ""; echo "── 阶段四:派单→执行→完成 ──" curl -s -X POST "$BASE/work-orders/$WO_ID/assign" $HDRS -H "X-User-Role:DISPATCHER" \ -H "Idempotency-Key:e2e-s9-$TS" -d '{"staffId":1,"reason":"E2E测试"}' | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "派单" || fail "派单" "" curl -s -X POST "$BASE/work-orders/$WO_ID/accept" $HDRS -H "X-User-Role:STAFF" \ -H "Idempotency-Key:e2e-s10-$TS" | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "接单" || fail "接单" "" curl -s -X POST "$BASE/work-orders/$WO_ID/check-in" $HDRS -H "X-User-Role:STAFF" \ -H "Idempotency-Key:e2e-s11-$TS" \ -d '{"latitude":24.2878,"longitude":116.1271,"photoFileId":"e2e_photo","patientConfirmed":true}' | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "GPS签到" || fail "签到" "" curl -s -X POST "$BASE/work-orders/$WO_ID/start-service" $HDRS -H "X-User-Role:STAFF" \ -H "Idempotency-Key:e2e-s12-$TS" | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "开始服务" || fail "开始服务" "" # 查询工单项目ID用于执行记录 WOI_ID=$(docker exec hss-postgres psql -U hss -d hss_home_service -qtc "SELECT id FROM hss_work_order_items WHERE work_order_id = $WO_ID ORDER BY id LIMIT 1" 2>/dev/null | tr -d ' ') echo " 工单项目ID: $WOI_ID" RESP=$(curl -s -X POST "$BASE/work-orders/$WO_ID/finish" $HDRS -H "X-User-Role:STAFF" \ -H "Idempotency-Key:e2e-s13-$TS" \ -d "{\"executionRecords\":[{\"planItemId\":${WOI_ID:-1},\"status\":\"COMPLETED\",\"notes\":\"E2E测试完成\"}],\"serviceSummary\":\"全链路测试\",\"signOffLatitude\":24.2878,\"signOffLongitude\":116.1271}") has_data "$RESP" "完成服务" # ── 阶段五:验收 → 结算 → 归档 ── echo ""; echo "── 阶段五:验收→结算→归档 ──" curl -s -X POST "$BASE/acceptances/work-orders/$WO_ID/confirm" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-s14-$TS" \ -d '{"rating":5,"tags":["态度好","专业"],"comment":"非常满意"}' | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "验收确认+评价" || fail "验收" "" # 工单已完成(finish 已将其置为 ORDER_COMPLETED),无需额外更新 RESP=$(curl -s -X POST "$BASE/settlements/generate" $HDRS -H "X-User-Role:SETTLER" \ -H "Idempotency-Key:e2e-s15-$TS" \ -d "{\"workOrderIds\":[$WO_ID],\"periodStart\":\"2026-05-01\",\"periodEnd\":\"2026-05-31\"}") has_data "$RESP" "生成结算单" SET_ID=$(echo "$RESP" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('data',{}).get('id',1))" 2>/dev/null || echo "1") curl -s -X POST "$BASE/settlements/$SET_ID/approve" $HDRS -H "X-User-Role:SETTLER" \ -H "Idempotency-Key:e2e-s16-$TS" | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "审核通过" || fail "审核" "" # 状态机: 已审核 → PAYMENT_PENDING → 支付成功 → 已支付 docker exec hss-postgres psql -U hss -d hss_home_service -qc "UPDATE hss_settlements SET status = 'PAYMENT_PENDING' WHERE id = $SET_ID" 2>/dev/null curl -s -X POST "$BASE/settlements/payment-callback" $HDRS \ -H "Idempotency-Key:e2e-s17-$TS" \ -d "{\"settlementId\":$SET_ID,\"transactionId\":\"TXN_E2E_$TS\",\"amount\":105.00,\"channel\":\"WECHAT\",\"callbackData\":\"{}\"}" | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "支付回调" || fail "支付" "" curl -s -X POST "$BASE/settlements/$SET_ID/archive" $HDRS -H "X-User-Role:SETTLER" \ -H "Idempotency-Key:e2e-s18-$TS" | python3 -c "import sys,json;assert json.load(sys.stdin)['code']==200" 2>/dev/null && ok "归档完成" || fail "归档" "" # ── 异常流程 ── echo ""; echo "── 异常流程 ──" curl -s -X POST "$BASE/applications/$APP_ID/accept" $HDRS -H "X-User-Role:RECEPTIONIST" \ -H "Idempotency-Key:e2e-err1-$TS" | python3 -c "import sys,json;d=json.load(sys.stdin);assert d['code']!=200" 2>/dev/null && ok "拦截非法状态转换" || fail "非法状态" "" curl -s -X POST "$BASE/service-plans/$PLAN_ID/reject" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-err2-$TS" -d '{"reason":""}' | python3 -c "import sys,json;d=json.load(sys.stdin);assert d['code']!=200" 2>/dev/null && ok "拒绝空原因" || fail "空原因" "" # ── 幂等 ── echo ""; echo "── 幂等验证 ──" R1=$(curl -s -X POST "$BASE/applications" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-idem-$TS" \ -d "{\"patientId\":\"90001\",\"serviceType\":\"BATH_ASSIST\",\"channel\":\"PHONE\",\"contactName\":\"幂等\",\"contactPhone\":\"13900000000\",\"address\":\"梅县区\",\"regionCode\":\"441403001\"}" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['id'])") sleep 0.5 R2=$(curl -s -X POST "$BASE/applications" $HDRS -H "X-User-Role:PATIENT" \ -H "Idempotency-Key:e2e-idem-$TS" \ -d "{\"patientId\":\"90001\",\"serviceType\":\"BATH_ASSIST\",\"channel\":\"PHONE\",\"contactName\":\"幂等\",\"contactPhone\":\"13900000000\",\"address\":\"梅县区\",\"regionCode\":\"441403001\"}" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['id'])") [ "$R1" = "$R2" ] && ok "幂等:重复提交返回同一ID($R1)" || fail "幂等" "R1=$R1 R2=$R2" # ── 脱敏 ── echo ""; echo "── 数据脱敏 ──" A_ADMIN=$(curl -s "$BASE/applications/$APP_ID" $HDRS -H "X-User-Role:ADMIN" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['address'])" 2>/dev/null) A_STAFF=$(curl -s "$BASE/applications/$APP_ID" $HDRS -H "X-User-Role:STAFF" | python3 -c "import sys,json;print(json.load(sys.stdin)['data']['address'])" 2>/dev/null) echo "$A_STAFF" | grep -q "\*\*\*\*" && ok "STAFF地址脱敏" || fail "脱敏:STAFF" "$A_STAFF" echo "$A_ADMIN" | grep -qv "\*\*\*\*" && ok "ADMIN地址完整" || fail "脱敏:ADMIN" "$A_ADMIN" # ── 全部模块可达性 ── echo ""; echo "── 模块可达性 ──" for ep in \ "delivery/workbench:STAFF" \ "admin/dashboard:ADMIN" \ "supervision/spot-checks:SUPERVISOR" \ "compliance/consents/patients/$PID:PATIENT" \ "master/price-rules:PATIENT" \ "master/service-items:PATIENT" \ "master/staff:DISPATCHER" \ "capacity/grids:DISPATCHER" \ "analytics/quality:SUPERVISOR" \ "analytics/heatmap:SUPERVISOR" \ "analytics/summary:ADMIN" \ "performance/ranking:ADMIN" \ "smart-assistant/staff/1/reminders:STAFF"; do path="${ep%%:*}"; role="${ep##*:}" code=$(curl -s -o /dev/null -w "%{http_code}" "$BASE/$path" $HDRS -H "X-User-Role:$role") [ "$code" = "200" ] && ok "$path" || fail "$path" "HTTP $code" done echo "" echo "╔════════════════════════════════════════════╗" printf "║ E2E 测试完成: ${G}%2d 通过${N} / ${R}%2d 失败${N} ║\n" $PASS $FAIL echo "╚════════════════════════════════════════════╝"