126 lines
8.1 KiB
Bash
126 lines
8.1 KiB
Bash
#!/bin/bash
|
||
# CampusNotice 接口 curl 测试脚本 v2
|
||
# 用法: ./api-curl-test.sh [BASE] [PLATFORM]
|
||
# BASE 默认 http://localhost:8101
|
||
# PLATFORM 默认 manage(CAS 仅拦 /api/* 与 /mobiapi/*,manage 为匿名通道;生产环境应封死,见安全提醒)
|
||
# 注意:中文 JSON 体必须走 UTF-8 文件(--data-binary @file),
|
||
# 直接 -d '中文' 会被 Git Bash→curl.exe 的 argv 转换变成 GBK,服务端报
|
||
# Invalid UTF-8 start byte(HttpMessageNotReadable → code=6),属测试工具问题。
|
||
BASE="${1:-http://localhost:8101}"
|
||
PF="${2:-manage}"
|
||
U="$BASE/$PF"
|
||
TMPB="$(dirname "$0")/.curl-body.json"
|
||
PASS=0; BIZ=0; FAIL=0
|
||
CREATED_IDS=()
|
||
|
||
req() { # req 名称 方法 路径 [json体]
|
||
local name="$1" method="$2" path="$3" body="$4"
|
||
local args=(-s --max-time 15 -w "\n%{http_code}" -H "Accept: application/json")
|
||
if [ -n "$body" ]; then
|
||
printf '%s' "$body" > "$TMPB" # UTF-8 原样字节落盘
|
||
args+=(-X POST -H "Content-Type: application/json; charset=utf-8" --data-binary "@$(cygpath -w "$TMPB" 2>/dev/null || echo "$TMPB")")
|
||
elif [ "$method" = "POST" ]; then
|
||
args+=(-X POST)
|
||
fi
|
||
local out; out=$(curl "${args[@]}" "$U$path" 2>/dev/null)
|
||
local http="${out##*$'\n'}"; local json="${out%$'\n'*}"
|
||
local parsed; parsed=$(node -e "
|
||
try{const j=JSON.parse(process.argv[1]);
|
||
const d=j.data;
|
||
const ds=d===null||d===undefined?'null':Array.isArray(d)?'array['+d.length+']':typeof d==='object'?'{'+Object.keys(d).slice(0,4).join(',')+'}':String(d).slice(0,30);
|
||
console.log([j.code,(j.msg||'').slice(0,46),ds].join('\u0001'));
|
||
}catch(e){console.log(['-','-','-'].join('\u0001'))}" "$json" 2>/dev/null)
|
||
local code msg data
|
||
code=$(echo "$parsed" | cut -d$'\001' -f1)
|
||
msg=$(echo "$parsed" | cut -d$'\001' -f2)
|
||
data=$(echo "$parsed" | cut -d$'\001' -f3)
|
||
local st
|
||
if [ "$code" = "0" ]; then PASS=$((PASS+1)); st="✅PASS";
|
||
elif [ "$code" = "-" ] || [ "$http" != "200" ]; then FAIL=$((FAIL+1)); st="❌FAIL";
|
||
else BIZ=$((BIZ+1)); st="🟡BIZ"; fi
|
||
printf "%-26s | HTTP %s | code=%-3s | msg=%-46s | data=%s\n" "$name" "$http" "$code" "$msg" "$data"
|
||
}
|
||
|
||
extract_id() { # 从最近一次输出无法跨函数取值,改为调用方直接用 node 从 curl 结果提取
|
||
node -e "try{console.log(JSON.parse(require('fs').readFileSync(process.argv[1],'utf8')).data ?? '')}catch(e){console.log('')}" "$1" 2>/dev/null
|
||
}
|
||
|
||
echo "================ CampusNotice curl 矩阵 (BASE=$BASE PF=$PF) ================"
|
||
|
||
echo "---- A. 静态(直连 BASE,不经 platform 前缀) ----"
|
||
for p in "/" "/api-test.html"; do
|
||
http=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$BASE$p")
|
||
if [ "$http" = "200" ]; then PASS=$((PASS+1)); printf "%-26s | HTTP %s\n" "静态 $p" "$http"; else FAIL=$((FAIL+1)); printf "%-26s | HTTP %s ❌\n" "静态 $p" "$http"; fi
|
||
done
|
||
http=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$BASE/api/notice/Statis")
|
||
if [ "$http" = "302" ]; then PASS=$((PASS+1)); printf "%-26s | HTTP 302 → CAS 登录墙(符合预期)\n" "/api 登录墙对照"; else FAIL=$((FAIL+1)); printf "%-26s | HTTP $http ❌(预期302)\n" "/api 登录墙对照"; fi
|
||
|
||
echo "---- B. 平台/基础(匿名语义) ----"
|
||
req "GetUserInfo" GET "/base/GetUserInfo"
|
||
req "GetUserMod(匿名预期拒)" GET "/base/GetUserMod"
|
||
req "GetSchoolAll" GET "/base/GetSchoolAll"
|
||
req "GetGroupUser(未接入桩)" GET "/base/GetGroupUser?gid=1&role=2"
|
||
|
||
echo "---- C. 通知查询 ----"
|
||
req "notice/List" GET "/notice/List?pageIndex=1&pageSize=3"
|
||
req "notice/SendList" GET "/notice/SendList?pageIndex=1&pageSize=3&status=1&scope=user"
|
||
req "notice/Statis" GET "/notice/Statis"
|
||
req "notice/GetNextPrev" GET "/notice/GetNextPrev?serid=1&type=0"
|
||
req "notice/DraftList" GET "/notice/DraftList?pageIndex=1&pageSize=3"
|
||
req "notice/ApproverSuggest" GET "/notice/ApproverSuggest"
|
||
req "ApprovalList(pending)" GET "/notice/ApprovalList?auditState=pending&pageIndex=1&pageSize=3"
|
||
req "ApprovalList(reviewed)" GET "/notice/ApprovalList?auditState=reviewed&pageIndex=1&pageSize=3"
|
||
|
||
echo "---- D. 推送范围(匿名预期拒) ----"
|
||
req "notice/ScopeTree" GET "/notice/ScopeTree"
|
||
req "notice/ScopePreview" GET "/notice/ScopePreview?scopes=%5B%7B%22scopeType%22%3A1%2C%22scopeId%22%3A0%2C%22scopeName%22%3A%22%E5%85%A8%E6%A0%A1%22%7D%5D"
|
||
|
||
echo "---- E. 写链路(UTF-8 文件体,测后自清理) ----"
|
||
printf '%s' '{"title":"【测试】curl直发","content":"<p>curl</p>","type":1,"deptId":"1","deptName":"校长室","topmost":0,"receiveList":"ALL","fileIds":"","timingSendTime":null,"smsFlag":0,"receiptFlag":1,"publishType":"direct"}' > "$TMPB"
|
||
resp=$(curl -s --max-time 15 -X POST -H "Content-Type: application/json; charset=utf-8" --data-binary "@$(cygpath -w "$TMPB" 2>/dev/null || echo "$TMPB")" "$U/notice/Create")
|
||
echo "$resp" > "$TMPB.out"
|
||
ID_DIRECT=$(extract_id "$TMPB.out")
|
||
if [ -n "$ID_DIRECT" ]; then PASS=$((PASS+1)); printf "%-26s | code=0 | id=%s ✅\n" "Create(direct)" "$ID_DIRECT"; else FAIL=$((FAIL+1)); printf "%-26s | ❌ 未返回id\n" "Create(direct)"; fi
|
||
CREATED_IDS+=("$ID_DIRECT")
|
||
|
||
printf '%s' '{"title":"【测试】curl草稿","content":"<p>curl</p>","type":1,"deptId":"1","deptName":"校长室","topmost":0,"receiveList":"ALL","fileIds":"","timingSendTime":null,"smsFlag":0,"receiptFlag":1}' > "$TMPB"
|
||
resp=$(curl -s --max-time 15 -X POST -H "Content-Type: application/json; charset=utf-8" --data-binary "@$(cygpath -w "$TMPB" 2>/dev/null || echo "$TMPB")" "$U/notice/SaveDraft")
|
||
echo "$resp" > "$TMPB.out"
|
||
ID_DRAFT=$(extract_id "$TMPB.out")
|
||
if [ -n "$ID_DRAFT" ]; then PASS=$((PASS+1)); printf "%-26s | code=0 | id=%s ✅\n" "SaveDraft" "$ID_DRAFT"; else FAIL=$((FAIL+1)); printf "%-26s | ❌ 未返回id\n" "SaveDraft"; fi
|
||
CREATED_IDS+=("$ID_DRAFT")
|
||
|
||
if [ -n "$ID_DRAFT" ]; then
|
||
# Submit 新契约(多步):steps 优先;匿名/无学校身份时预期 code=2(需登录态经 /api 或 manage+学校身份)
|
||
printf '%s' "{\"serid\":$ID_DRAFT,\"steps\":[{\"name\":\"校级审核\",\"vote\":\"any\",\"approvers\":[\"lijing\"]}],\"opinion\":\"请审批\"}" > "$TMPB"
|
||
resp=$(curl -s --max-time 15 -X POST -H "Content-Type: application/json; charset=utf-8" --data-binary "@$(cygpath -w "$TMPB" 2>/dev/null || echo "$TMPB")" "$U/notice/Submit")
|
||
code=$(node -e "try{console.log(JSON.parse(process.argv[1]).code)}catch(e){console.log('-')}" "$resp")
|
||
if [ "$code" = "0" ]; then PASS=$((PASS+1)); printf "%-26s | code=0 | 草稿→待审批(第1步激活) ✅\n" "Submit(steps)"; else BIZ=$((BIZ+1)); printf "%-26s | code=%s 🟡 (匿名无学校身份为预期)\n" "Submit(steps)" "$code"; fi
|
||
fi
|
||
req "Approve(匿名预期拒)" POST "/notice/Approve?serid=${ID_DRAFT:-0}&opinion=x"
|
||
req "Reject(匿名预期拒)" POST "/notice/Reject?serid=${ID_DRAFT:-0}&opinion=x"
|
||
|
||
echo "---- F. 其他模块 ----"
|
||
req "dict/ManageList" GET "/dict/ManageList"
|
||
req "template/Typelist" GET "/template/Typelist"
|
||
req "contacts/GetGroup" GET "/contacts/GetGroup"
|
||
req "sms/GetSchool" GET "/sms/GetSchool"
|
||
|
||
echo "---- G. 附件 ----"
|
||
f="$(dirname "$0")/.curl-upload-sample.txt"; echo "curl upload sample $(date +%s)" > "$f"
|
||
req "base/FileUpload" POST "/base/FileUpload" "" "file=@$(cygpath -w "$f" 2>/dev/null || echo "$f")"
|
||
rm -f "$f"
|
||
|
||
echo "---- H. 清理测试数据 ----"
|
||
if [ "${#CREATED_IDS[@]}" -gt 0 ]; then
|
||
ids=$(IFS=,; echo "${CREATED_IDS[*]}")
|
||
printf '%s' "[$ids]" > "$TMPB"
|
||
resp=$(curl -s --max-time 15 -X POST -H "Content-Type: application/json; charset=utf-8" --data-binary "@$(cygpath -w "$TMPB" 2>/dev/null || echo "$TMPB")" "$U/notice/Delete")
|
||
code=$(node -e "try{console.log(JSON.parse(process.argv[1]).code)}catch(e){console.log('-')}" "$resp")
|
||
if [ "$code" = "0" ]; then PASS=$((PASS+1)); printf "%-26s | code=0 | ids=[$ids] 已清理 ✅\n" "Delete清理"; else FAIL=$((FAIL+1)); printf "%-26s | code=%s ❌\n" "Delete清理" "$code"; fi
|
||
fi
|
||
rm -f "$TMPB" "$TMPB.out"
|
||
|
||
echo "================ 统计: PASS=$PASS BIZ(业务拒绝/环境预期)=$BIZ FAIL=$FAIL ================"
|
||
echo "提醒: /manage 匿名可访问全部业务接口——CAS 仅拦 /api/* 与 /mobiapi/*,生产环境务必封死(安全项)。"
|