#!/bin/bash BASE_URL="http://localhost:8080/api" TOKEN="" PROJECT_ID="" PASS_COUNT=0 FAIL_COUNT=0 TOTAL_COUNT=0 assert_eq() { local actual="$1" local expected="$2" local name="$3" TOTAL_COUNT=$((TOTAL_COUNT + 1)) if [ "$actual" = "$expected" ]; then echo " PASS: $name" PASS_COUNT=$((PASS_COUNT + 1)) else echo " FAIL: $name (expected=$expected, actual=$actual)" FAIL_COUNT=$((FAIL_COUNT + 1)) fi } assert_contains() { local haystack="$1" local needle="$2" local name="$3" TOTAL_COUNT=$((TOTAL_COUNT + 1)) if echo "$haystack" | grep -q "$needle"; then echo " PASS: $name" PASS_COUNT=$((PASS_COUNT + 1)) else echo " FAIL: $name (expected to contain: $needle)" FAIL_COUNT=$((FAIL_COUNT + 1)) fi } login() { echo "=== 登录获取Token ===" local response=$(curl -s -X POST "$BASE_URL/auth/login" \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"admin123"}') TOKEN=$(echo "$response" | grep -o '"token":"[^"]*"' | cut -d'"' -f4) if [ -z "$TOKEN" ]; then echo "FATAL: 登录失败,无法获取Token" exit 1 fi echo "Token获取成功" } setup_project() { echo "=== 创建测试项目 ===" local response=$(curl -s -X POST "$BASE_URL/mdm/projects" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: " \ -d "{\"code\":\"TEST-OPS-$$\",\"name\":\"OPS测试项目\",\"projectType\":\"RESIDENTIAL\"}") PROJECT_ID=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) if [ -z "$PROJECT_ID" ]; then echo "FATAL: 创建测试项目失败" exit 1 fi echo "测试项目创建成功: $PROJECT_ID" } test_create_work_order() { echo "--- TC-OPS-001: 创建工单 ---" local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"title\":\"测试工单-$$\",\"source\":\"MANUAL\",\"type\":\"REPAIR\",\"priority\":\"MEDIUM\",\"description\":\"API测试工单\"}") local status=$(echo "$response" | tail -n 1) local body=$(echo "$response" | head -n -1) assert_eq "$status" "201" "TC-OPS-001: 创建工单返回201" assert_contains "$body" '"workNo"' "TC-OPS-001: 响应包含workNo" WORK_ORDER_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) } test_query_work_orders() { echo "--- TC-OPS-002: 查询工单列表 ---" local response=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/wo/work-orders?projectId=$PROJECT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-002: 查询工单列表返回200" } test_assign_work_order() { echo "--- TC-OPS-003: 工单派单 ---" if [ -z "$WORK_ORDER_ID" ]; then echo " SKIP: TC-OPS-003 (无工单ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders/$WORK_ORDER_ID/assign" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d '{"assignedTo":"test_maintainer","assignedDate":"2026-05-18"}') local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-003: 工单派单返回200" } test_start_work_order() { echo "--- TC-OPS-004: 工单开始执行 ---" if [ -z "$WORK_ORDER_ID" ]; then echo " SKIP: TC-OPS-004 (无工单ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders/$WORK_ORDER_ID/start" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-004: 工单开始执行返回200" } test_complete_work_order() { echo "--- TC-OPS-005: 工单完成 ---" if [ -z "$WORK_ORDER_ID" ]; then echo " SKIP: TC-OPS-005 (无工单ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders/$WORK_ORDER_ID/complete" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d '{"faultCause":"测试故障原因","solution":"测试解决方案","result":"已修复","laborCost":100,"partsCost":50}') local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-005: 工单完成返回200" } test_verify_work_order() { echo "--- TC-OPS-006: 工单验收 ---" if [ -z "$WORK_ORDER_ID" ]; then echo " SKIP: TC-OPS-006 (无工单ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders/$WORK_ORDER_ID/verify" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d '{"verifiedBy":"admin","rating":5,"remark":"验收通过"}') local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-006: 工单验收返回200" } test_cancel_completed_work_order() { echo "--- TC-OPS-007: 工单取消-已完成拒绝 ---" if [ -z "$WORK_ORDER_ID" ]; then echo " SKIP: TC-OPS-007 (无工单ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/work-orders/$WORK_ORDER_ID/cancel" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "400" "TC-OPS-007: 已完成工单取消返回400" } test_work_order_statistics() { echo "--- TC-OPS-008: 工单统计 ---" local response=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/wo/work-orders/statistics?projectId=$PROJECT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-008: 工单统计返回200" } test_create_maintenance_task() { echo "--- TC-OPS-009: 创建维保任务 ---" local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/wo/maintenance-tasks" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"equipmentId\":\"00000000-0000-0000-0000-000000000000\",\"taskType\":\"PREVENTIVE\",\"triggerType\":\"MANUAL\",\"title\":\"测试维保任务-$$\"}") local status=$(echo "$response" | tail -n 1) local body=$(echo "$response" | head -n -1) if [ "$status" = "201" ] || [ "$status" = "200" ]; then assert_eq "$status" "$status" "TC-OPS-009: 创建维保任务成功" MAINT_TASK_ID=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) else assert_contains "$body" "equipment" "TC-OPS-009: 创建维保任务(设备不存在时返回错误)" fi } test_spare_part_crud() { echo "--- TC-OPS-010: 备件CRUD ---" local create_response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/spare-parts" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"partCode\":\"TEST-SP-$$_$RANDOM\",\"partName\":\"测试备件\",\"unit\":\"个\",\"safeStock\":10,\"currentStock\":100,\"unitPrice\":25.00}") local create_status=$(echo "$create_response" | tail -n 1) local create_body=$(echo "$create_response" | head -n -1) assert_eq "$create_status" "201" "TC-OPS-010: 创建备件返回201" SPARE_PART_ID=$(echo "$create_body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) } test_spare_part_in_stock() { echo "--- TC-OPS-011: 备件入库 ---" if [ -z "$SPARE_PART_ID" ]; then echo " SKIP: TC-OPS-011 (无备件ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/spare-parts/in-stock" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"sparePartId\":\"$SPARE_PART_ID\",\"quantity\":50,\"operator\":\"admin\",\"remark\":\"API测试入库\"}") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-011: 备件入库返回200" } test_spare_part_out_stock() { echo "--- TC-OPS-012: 备件出库 ---" if [ -z "$SPARE_PART_ID" ]; then echo " SKIP: TC-OPS-012 (无备件ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/spare-parts/out-stock" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"sparePartId\":\"$SPARE_PART_ID\",\"quantity\":10,\"operator\":\"admin\",\"remark\":\"API测试出库\"}") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-012: 备件出库返回200" } test_spare_part_out_stock_insufficient() { echo "--- TC-OPS-013: 备件出库-库存不足 ---" if [ -z "$SPARE_PART_ID" ]; then echo " SKIP: TC-OPS-013 (无备件ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/spare-parts/out-stock" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"sparePartId\":\"$SPARE_PART_ID\",\"quantity\":99999,\"operator\":\"admin\",\"remark\":\"库存不足测试\"}") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "400" "TC-OPS-013: 库存不足出库返回400" } test_low_stock_warning() { echo "--- TC-OPS-014: 低库存预警 ---" local response=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/ops/spare-parts/low-stock?projectId=$PROJECT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-014: 低库存预警返回200" } test_energy_consumption() { echo "--- TC-OPS-015: 能耗抄表录入 ---" local meter_response=$(curl -s -X POST "$BASE_URL/ops/energy/meters" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"meterName\":\"测试计量点-$$\",\"energyType\":\"LIGHTING\",\"unitPrice\":0.85}") local meter_id=$(echo "$meter_response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) if [ -z "$meter_id" ]; then echo " SKIP: TC-OPS-015 (无法创建计量点)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/energy/consumption" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"meterId\":\"$meter_id\",\"currentReading\":100,\"recordedBy\":\"admin\"}") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "201" "TC-OPS-015: 能耗抄表录入返回201" ENERGY_METER_ID="$meter_id" } test_energy_decreasing_reading() { echo "--- TC-OPS-016: 能耗抄表-读数递减 ---" if [ -z "$ENERGY_METER_ID" ]; then echo " SKIP: TC-OPS-016 (无计量点ID)" return fi local response=$(curl -s -w "\n%{http_code}" -X POST "$BASE_URL/ops/energy/consumption" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID" \ -d "{\"meterId\":\"$ENERGY_METER_ID\",\"currentReading\":50,\"recordedBy\":\"admin\"}") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "400" "TC-OPS-016: 读数递减返回400" } test_maintenance_plan() { echo "--- TC-OPS-017: 维保计划管理 ---" local response=$(curl -s -w "\n%{http_code}" -X GET "$BASE_URL/wo/maintenance-plans?projectId=$PROJECT_ID" \ -H "Authorization: Bearer $TOKEN" \ -H "X-Project-ID: $PROJECT_ID") local status=$(echo "$response" | tail -n 1) assert_eq "$status" "200" "TC-OPS-017: 查询维保计划返回200" } cleanup() { echo "=== 清理测试数据 ===" if [ -n "$PROJECT_ID" ]; then curl -s -X DELETE "$BASE_URL/mdm/projects/$PROJECT_ID" \ -H "Authorization: Bearer $TOKEN" > /dev/null 2>&1 fi } print_summary() { echo "" echo "==========================================" echo " Ops Domain API Test Summary" echo "==========================================" echo " Total: $TOTAL_COUNT" echo " Passed: $PASS_COUNT" echo " Failed: $FAIL_COUNT" if [ "$TOTAL_COUNT" -gt 0 ]; then local rate=$(echo "scale=1; $PASS_COUNT * 100 / $TOTAL_COUNT" | bc) echo " Rate: ${rate}%" fi echo "==========================================" if [ "$FAIL_COUNT" -gt 0 ]; then return 1 fi return 0 } WORK_ORDER_ID="" MAINT_TASK_ID="" SPARE_PART_ID="" ENERGY_METER_ID="" echo "==========================================" echo " Ether PMS - Ops Domain API Tests" echo "==========================================" echo "" login setup_project test_create_work_order test_query_work_orders test_assign_work_order test_start_work_order test_complete_work_order test_verify_work_order test_cancel_completed_work_order test_work_order_statistics test_create_maintenance_task test_spare_part_crud test_spare_part_in_stock test_spare_part_out_stock test_spare_part_out_stock_insufficient test_low_stock_warning test_energy_consumption test_energy_decreasing_reading test_maintenance_plan cleanup print_summary