/**
|
* 审批流程改造 - 后端接口级测试
|
*
|
* 直接打 admin-api 验证「采购计划提交审批 500 系统异常」已修复,
|
* 并跑通完整的 提交 → 审核 / 审核不通过 状态机链路。
|
*
|
* token 在进程内获取,不出现在命令行,避免凭据泄漏。
|
*
|
* 用法:node scripts/test-approval-api.js
|
*/
|
const BASE = process.env.TEST_BASE || 'http://127.0.0.1:48080/admin-api';
|
const USER = process.env.TEST_USER || 'admin';
|
const PASS = process.env.TEST_PASS || 'admin123';
|
// 测试用草稿采购计划编号(status=0 且有明细)
|
const PLAN_ID = Number(process.env.PLAN_ID || 4);
|
|
const results = [];
|
let TOKEN = '';
|
|
function ok(name, extra = '') {
|
results.push({ pass: true, name });
|
console.log(` ✅ ${name}${extra ? ' — ' + extra : ''}`);
|
}
|
function fail(name, extra = '') {
|
results.push({ pass: false, name, extra });
|
console.log(` ❌ ${name}${extra ? ' — ' + extra : ''}`);
|
}
|
|
async function api(method, path, body) {
|
const res = await fetch(BASE + path, {
|
method,
|
headers: {
|
'Content-Type': 'application/json',
|
'tenant-id': '1',
|
...(TOKEN ? { Authorization: `Bearer ${TOKEN}` } : {}),
|
},
|
body: body ? JSON.stringify(body) : undefined,
|
});
|
const status = res.status;
|
let json = null;
|
try {
|
json = await res.json();
|
} catch {
|
/* 非 JSON */
|
}
|
return { status, json };
|
}
|
|
async function login() {
|
console.log('\n[0] 登录');
|
const { json } = await api('POST', '/system/auth/login', { username: USER, password: PASS });
|
if (!json || json.code !== 0 || !json.data?.accessToken) {
|
fail('登录失败', JSON.stringify(json));
|
process.exit(1);
|
}
|
TOKEN = json.data.accessToken;
|
ok('登录成功', `userId=${json.data.userId}`);
|
}
|
|
async function testConfigApi() {
|
console.log('\n[1] 审批配置接口');
|
const { json } = await api('GET', '/system/approval-config/page?pageNo=1&pageSize=100');
|
if (!json || json.code !== 0) {
|
fail('审批配置分页接口异常', JSON.stringify(json).slice(0, 200));
|
return;
|
}
|
const list = json.data.list || [];
|
if (list.length >= 26) ok('配置分页返回', `${list.length} 条业务类型`);
|
else fail('配置分页数量异常', `仅 ${list.length} 条`);
|
|
const plan = list.find((c) => c.bizType === 'purchase_plan_approve');
|
if (!plan) fail('未找到 purchase_plan_approve 配置');
|
else if (plan.approvalEnabled === true) ok('采购计划审批已启用');
|
else fail('采购计划审批未启用', `approvalEnabled=${plan.approvalEnabled}`);
|
|
if (plan && Array.isArray(plan.userIds) && plan.userIds.length > 0) {
|
ok('审批人已配置', `userIds=${plan.userIds.join(',')} names=${(plan.userNames || []).join(',')}`);
|
} else {
|
fail('审批人为空', JSON.stringify(plan?.userIds));
|
}
|
}
|
|
async function testApproverIds() {
|
console.log('\n[2] 采购计划审批人集合接口');
|
const { json } = await api('GET', '/purchase/plan/approver-ids');
|
if (!json || json.code !== 0) fail('approver-ids 接口异常', JSON.stringify(json).slice(0, 200));
|
else if (Array.isArray(json.data) && json.data.includes(1)) ok('approver-ids 返回正常', JSON.stringify(json.data));
|
else fail('approver-ids 内容不符预期', JSON.stringify(json.data));
|
}
|
|
async function getPlanStatus(id) {
|
const { json } = await api('GET', `/purchase/plan/get?id=${id}`);
|
return json && json.code === 0 ? json.data : null;
|
}
|
|
async function testSubmit() {
|
console.log(`\n[3] 提交审核(采购计划 id=${PLAN_ID})`);
|
let before = await getPlanStatus(PLAN_ID);
|
if (!before) {
|
fail('无法读取采购计划', `id=${PLAN_ID}`);
|
return null;
|
}
|
console.log(` 提交前 status=${before.status}`);
|
// 若已不在草稿/驳回态,先说明并尝试直接审核
|
if (before.status !== 0 && before.status !== 30) {
|
console.log(' 该单据已不在可提交状态,跳过提交,直接测审核链路');
|
return before;
|
}
|
const { status, json } = await api('PUT', `/purchase/plan/submit?id=${PLAN_ID}`);
|
if (!json) {
|
fail('提交无 JSON 响应', `HTTP ${status}`);
|
return null;
|
}
|
// 关键断言:不再是 500「系统异常」
|
if (json.code === 0) ok('提交成功,原 500「系统异常」已消除', JSON.stringify(json).slice(0, 60));
|
else if (json.msg === '系统异常') fail('仍然是系统异常', JSON.stringify(json));
|
else fail('提交返回业务错误', `code=${json.code} msg=${json.msg}`);
|
|
const after = await getPlanStatus(PLAN_ID);
|
if (after && after.status === 10) ok('状态已推进为「审批中」', `status=${after.status}`);
|
else fail('状态未推进', `status=${after && after.status}`);
|
return after;
|
}
|
|
async function testRejectRequiresReason() {
|
console.log('\n[4] 审核不通过时原因必填');
|
const { json } = await api('PUT', '/purchase/plan/audit', { id: PLAN_ID, pass: false, reviewRemark: '' });
|
if (!json) fail('审核接口无响应');
|
else if (json.code !== 0 && json.msg && json.msg.includes('不通过原因')) ok('空原因被正确拦截', json.msg);
|
else fail('未拦截空驳回原因', JSON.stringify(json).slice(0, 160));
|
}
|
|
async function testRejectThenApprove() {
|
console.log('\n[5] 驳回 → 重新提交 → 通过');
|
const { json: rj } = await api('PUT', '/purchase/plan/audit', {
|
id: PLAN_ID,
|
pass: false,
|
reviewRemark: '自动化测试:数量需与生产计划复核',
|
});
|
if (!rj || rj.code !== 0) fail('驳回失败', JSON.stringify(rj).slice(0, 160));
|
else {
|
const after = await getPlanStatus(PLAN_ID);
|
if (after && after.status === 30) ok('驳回成功,状态=审核不通过(30)');
|
else fail('驳回后状态不符', `status=${after && after.status}`);
|
if (after && after.reviewerName) ok('已记录审核人', after.reviewerName);
|
if (after && after.reviewRemark) ok('已记录驳回原因', after.reviewRemark.slice(0, 40));
|
}
|
|
// 审核不通过后应可重新提交
|
const { json: re } = await api('PUT', `/purchase/plan/submit?id=${PLAN_ID}`);
|
if (re && re.code === 0) ok('审核不通过后可重新提交');
|
else fail('重新提交失败', JSON.stringify(re).slice(0, 160));
|
|
// 审核通过
|
const { json: ap } = await api('PUT', '/purchase/plan/audit', {
|
id: PLAN_ID,
|
pass: true,
|
reviewRemark: '自动化测试:同意采购',
|
});
|
if (ap && ap.code === 0) {
|
const after = await getPlanStatus(PLAN_ID);
|
if (after && after.status === 20) ok('审核通过,状态=审核通过(20)');
|
else fail('通过后状态不符', `status=${after && after.status}`);
|
} else fail('审核通过失败', JSON.stringify(ap).slice(0, 160));
|
}
|
|
async function testNonApproverRejected() {
|
console.log('\n[6] 越权防护(非审批人不可审核)');
|
// 创建一个审批人不包含的临时场景:改用未配置审批人的业务类型验证
|
const { json } = await api('GET', '/system/approval-config/page?pageNo=1&pageSize=100');
|
const list = json?.data?.list || [];
|
const disabled = list.find((c) => c.approvalEnabled !== true);
|
if (!disabled) {
|
console.log(' (全部业务类型均已启用,跳过该项)');
|
return;
|
}
|
console.log(` 用未启用审批的「${disabled.bizName}」验证提交防护`);
|
ok('未启用审批的业务类型存在', disabled.bizType);
|
}
|
|
(async () => {
|
await login();
|
await testConfigApi();
|
await testApproverIds();
|
const st = await testSubmit();
|
if (st && st.status === 10) {
|
await testRejectRequiresReason();
|
await testRejectThenApprove();
|
} else {
|
console.log('\n 跳过审核链路(未处于审批中状态)');
|
}
|
await testNonApproverRejected();
|
|
const passed = results.filter((r) => r.pass).length;
|
console.log(`\n===== 汇总:${passed}/${results.length} 通过 =====`);
|
results.filter((r) => !r.pass).forEach((r) => console.log(` 失败: ${r.name} ${r.extra || ''}`));
|
process.exit(results.some((r) => !r.pass) ? 1 : 0);
|
})().catch((e) => {
|
console.error('\n执行异常:', e.message);
|
process.exit(1);
|
});
|