feat: 初始化居家上门服务系统完整项目代码
- Spring Boot 后端服务 (hss-home-service) - delivery-miniapp 配送小程序 - website 官网 (Nuxt) - docs 架构设计文档 - Docker 容器化部署配置 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
182
hss-home-service/test-api.sh
Executable file
182
hss-home-service/test-api.sh
Executable file
@@ -0,0 +1,182 @@
|
||||
#!/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 "============================================"
|
||||
Reference in New Issue
Block a user