411 lines
15 KiB
JavaScript
411 lines
15 KiB
JavaScript
|
|
/**
|
|||
|
|
* 资质申请管理 — 三模块完整流程联调(模拟前端 persist 链路)
|
|||
|
|
*
|
|||
|
|
* 运行: node docs/test-reports/测试用例/test-qual-filing-full-e2e.mjs
|
|||
|
|
*/
|
|||
|
|
import path from "node:path";
|
|||
|
|
import { fileURLToPath } from "node:url";
|
|||
|
|
|
|||
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|||
|
|
const ROOT = path.resolve(__dirname, "../../..");
|
|||
|
|
|
|||
|
|
const API_BASE = process.env.API_BASE || "http://127.0.0.1:8095/safety-eval";
|
|||
|
|
const ORG_INFO_ID = process.env.ORG_INFO_ID || "1";
|
|||
|
|
const DEFAULT_URL =
|
|||
|
|
"https://s1.aigei.com/prevfiles/6f2754563be2491bad7c957904cd7d2a.jpg?e=2051020800&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:CR60zg7VKDp-jUWyYls_rdvyQMM=";
|
|||
|
|
|
|||
|
|
const issues = [];
|
|||
|
|
let passed = 0;
|
|||
|
|
let failed = 0;
|
|||
|
|
const ctx = {};
|
|||
|
|
|
|||
|
|
function log(step, msg) {
|
|||
|
|
console.log(`[${step}] ${msg}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function pass(name, detail = "") {
|
|||
|
|
passed += 1;
|
|||
|
|
console.log(` ✓ ${name}${detail ? ` — ${detail}` : ""}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function fail(name, detail = "") {
|
|||
|
|
failed += 1;
|
|||
|
|
console.log(` ✗ ${name}${detail ? ` — ${detail}` : ""}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function recordIssue(module, severity, title, detail) {
|
|||
|
|
issues.push({ module, severity, title, detail });
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function api(method, apiPath, body) {
|
|||
|
|
const res = await fetch(`${API_BASE}${apiPath}`, {
|
|||
|
|
method,
|
|||
|
|
headers: { "Content-Type": "application/json", orgInfoId: ORG_INFO_ID },
|
|||
|
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|||
|
|
});
|
|||
|
|
const text = await res.text();
|
|||
|
|
try {
|
|||
|
|
return { httpStatus: res.status, ...JSON.parse(text) };
|
|||
|
|
}
|
|||
|
|
catch {
|
|||
|
|
return { httpStatus: res.status, success: false, raw: text };
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function uploadAllMaterials(filingId) {
|
|||
|
|
const detail = await api("GET", `/qual-filing/detail?id=${filingId}`);
|
|||
|
|
for (const m of detail.data?.materials || []) {
|
|||
|
|
if (!m.attachmentUrl) {
|
|||
|
|
await api("POST", "/qual-filing-material/upload", { id: m.id, attachmentUrl: DEFAULT_URL });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const FORMAL_BASIC = {
|
|||
|
|
filingTerritoryName: "渝北区",
|
|||
|
|
filingTerritoryCode: "渝北区",
|
|||
|
|
filingUnitName: "重庆渝安安全评价技术有限公司",
|
|||
|
|
filingUnitTypeCode: "本地单位",
|
|||
|
|
filingUnitTypeName: "本地单位",
|
|||
|
|
businessScope: "石油加工业,化学原料、化学品及医药制造业",
|
|||
|
|
registerAddress: "重庆市渝北区黄山大道中段88号",
|
|||
|
|
officeAddress: "重庆市渝北区黄山大道中段88号安全评价大厦12层",
|
|||
|
|
creditCode: "91500000MA5U8K9X2L",
|
|||
|
|
qualCertNo: "AQPJ-渝-2024-0018",
|
|||
|
|
legalPersonPhone: "王建国 / 023-67891234",
|
|||
|
|
contactPhone: "陈晓明 / 13912345678",
|
|||
|
|
infoDisclosureUrl: "https://www.yuan-an-safety.com/disclosure",
|
|||
|
|
fixedAssetAmount: 1580,
|
|||
|
|
workplaceArea: 1860,
|
|||
|
|
archiveRoomArea: 120,
|
|||
|
|
fulltimeEvaluatorCount: 32,
|
|||
|
|
registeredEngineerCount: 12,
|
|||
|
|
unitIntro: "本公司成立于2018年,主要从事安全评价、安全技术服务,具备独立法人资格。",
|
|||
|
|
attachmentUrl: DEFAULT_URL,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
async function persistLikeFrontend(filingId, mode = "application") {
|
|||
|
|
const saveRes = await api("POST", "/qual-filing/save-draft", { id: filingId, ...FORMAL_BASIC });
|
|||
|
|
if (!saveRes.success) {
|
|||
|
|
throw new Error(saveRes.message || "save-draft failed");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await api("POST", `/qual-filing-material/init-template?filingId=${filingId}`);
|
|||
|
|
await uploadAllMaterials(filingId);
|
|||
|
|
|
|||
|
|
const commitmentContent =
|
|||
|
|
"本人是王建国是重庆渝安安全评价技术有限公司法定代表人,现代表我单位承诺如下:本单位提交的资质备案申请材料真实、准确、完整。";
|
|||
|
|
await api("POST", "/qual-filing-commitment/save-or-update", {
|
|||
|
|
filingId,
|
|||
|
|
signDate: "2026-06-30",
|
|||
|
|
legalRepSignatureUrl: DEFAULT_URL,
|
|||
|
|
commitmentContent,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const personnelPage = await api("GET", "/org-personnel/page?current=1&size=20");
|
|||
|
|
const personnelIds = (personnelPage.data || []).map((p) => p.id).filter(Boolean);
|
|||
|
|
if (personnelIds.length) {
|
|||
|
|
await api("POST", "/qual-filing-personnel/batch-add-from-org", {
|
|||
|
|
filingId,
|
|||
|
|
sourcePersonnelIds: personnelIds,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const equipPage = await api("GET", "/org-equipment/page?current=1&size=20");
|
|||
|
|
const equipIds = (equipPage.data || []).map((e) => e.id).filter(Boolean);
|
|||
|
|
if (equipIds.length) {
|
|||
|
|
await api("POST", "/qual-filing-equipment/batch-add-from-org", {
|
|||
|
|
filingId,
|
|||
|
|
sourceEquipmentIds: equipIds,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return api("GET", `/qual-filing/detail?id=${filingId}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 模块1:资质备案申请 ──
|
|||
|
|
async function testModule1Application() {
|
|||
|
|
console.log("\n=== 模块1:资质备案申请 ===");
|
|||
|
|
|
|||
|
|
const existingPage = await api("GET", "/qual-filing/page?current=1&size=5&applyTypeCode=1&filingStatusCode=5");
|
|||
|
|
let filingId = existingPage.data?.[0]?.id;
|
|||
|
|
|
|||
|
|
if (filingId) {
|
|||
|
|
pass("复用已有暂存记录", `id=${filingId}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
const draft = await api("POST", "/qual-filing/draft?applyTypeCode=1");
|
|||
|
|
filingId = draft.data?.id;
|
|||
|
|
if (draft.success && filingId) {
|
|||
|
|
pass("新建申请草稿", `id=${filingId}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("新建申请草稿", draft.message);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ctx.applicationId = filingId;
|
|||
|
|
|
|||
|
|
const detailRes = await persistLikeFrontend(filingId);
|
|||
|
|
const detail = detailRes.data || {};
|
|||
|
|
pass("暂存-基本信息", detail.filingUnitName);
|
|||
|
|
pass("暂存-材料10项已上传", (detail.materials || []).every((m) => m.uploadStatusCode === 2));
|
|||
|
|
pass("暂存-承诺书", !!detail.commitment?.commitmentContent);
|
|||
|
|
pass("暂存-人员", `count=${(detail.personnelList || []).length}`);
|
|||
|
|
pass("暂存-装备", `count=${(detail.equipmentList || []).length}`);
|
|||
|
|
|
|||
|
|
const listRes = await api("GET", "/qual-filing/page?current=1&size=20&applyTypeCode=1");
|
|||
|
|
const inList = (listRes.data || []).some((r) => String(r.id) === String(filingId));
|
|||
|
|
if (inList && detail.filingStatusCode === 5) {
|
|||
|
|
pass("列表-暂存记录可见", detail.filingStatusName);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("列表-暂存记录可见", `status=${detail.filingStatusCode}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const reload = await api("GET", `/qual-filing/detail?id=${filingId}`);
|
|||
|
|
if (reload.data?.businessScope?.includes("化学")) {
|
|||
|
|
pass("回显-修改后业务范围");
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("回显-修改后业务范围");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const verifyMod = await import(path.join(ROOT, "src/pages/Container/QualApplication/filingVerify.js"));
|
|||
|
|
const verifyResult = verifyMod.verifyFilingPrerequisites({
|
|||
|
|
...FORMAL_BASIC,
|
|||
|
|
materials: reload.data?.materials || [],
|
|||
|
|
personnelList: reload.data?.personnelList || [],
|
|||
|
|
equipmentList: reload.data?.equipmentList || [],
|
|||
|
|
});
|
|||
|
|
if (!verifyResult.passed) {
|
|||
|
|
const failedItems = verifyResult.items.filter((i) => !i.passed).map((i) => i.label);
|
|||
|
|
recordIssue(
|
|||
|
|
"资质备案申请",
|
|||
|
|
"阻塞",
|
|||
|
|
"前端提交核验:人员数量/结构未通过",
|
|||
|
|
`当前机构人员 ${(reload.data?.personnelList || []).length} 人,核验失败项:${failedItems.join("、")}。用户要求测试阶段暂不判断此项,但前端「提交申请」确认按钮仍被禁用。`,
|
|||
|
|
);
|
|||
|
|
pass("前端核验-预期失败(人员不足)", failedItems.join(","));
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
pass("前端核验-全部通过");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const submit = await api("POST", `/qual-filing/submit?id=${filingId}`);
|
|||
|
|
if (submit.success && submit.data?.filingStatusCode === 2) {
|
|||
|
|
pass("后端提交-进入审核中", submit.data.filingStatusName);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("后端提交", submit.message);
|
|||
|
|
recordIssue("资质备案申请", "阻塞", "后端提交失败", submit.message || JSON.stringify(submit));
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const approve = await api("POST", `/qual-filing/approve?id=${filingId}`);
|
|||
|
|
if (approve.success && approve.data?.filingStatusCode === 1) {
|
|||
|
|
pass("审核通过-已备案", approve.data.filingStatusName);
|
|||
|
|
ctx.approvedApplicationId = filingId;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("审核通过", approve.message || approve.data?.filingStatusName);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 模块2:已备案资质管理 ──
|
|||
|
|
async function testModule2FiledManage() {
|
|||
|
|
console.log("\n=== 模块2:已备案资质管理 ===");
|
|||
|
|
|
|||
|
|
const filedPage = await api("GET", "/qual-filing/filed/page?current=1&size=20");
|
|||
|
|
const filedList = filedPage.data || [];
|
|||
|
|
if (filedList.length > 0) {
|
|||
|
|
pass("已备案列表有数据", `count=${filedList.length}`);
|
|||
|
|
ctx.originFiledId = filedList[0].id;
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("已备案列表有数据", "审核通过后应出现 applyType=3 记录");
|
|||
|
|
recordIssue("已备案资质管理", "阻塞", "审核通过后已备案列表为空", "需确认 approve 是否创建 filed 记录");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const origin = filedList[0];
|
|||
|
|
if (origin.applyTypeCode === 3) {
|
|||
|
|
pass("已备案记录类型", origin.applyTypeName);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("已备案记录类型", `applyTypeCode=${origin.applyTypeCode}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const filedDraft = await api("POST", "/qual-filing/filed/draft");
|
|||
|
|
const filedDraftId = filedDraft.data?.id;
|
|||
|
|
if (!filedDraft.success || !filedDraftId) {
|
|||
|
|
fail("资质备案填报-创建草稿", filedDraft.message);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
pass("资质备案填报-创建草稿", `id=${filedDraftId}`);
|
|||
|
|
ctx.filedDraftId = filedDraftId;
|
|||
|
|
|
|||
|
|
const saveRes = await persistLikeFrontend(filedDraftId);
|
|||
|
|
const detail = saveRes.data || {};
|
|||
|
|
if (detail.filingStatusCode === 5 && detail.applyTypeCode === 3) {
|
|||
|
|
pass("填报暂存", `${detail.filingStatusName} applyType=${detail.applyTypeCode}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("填报暂存", `status=${detail.filingStatusCode} type=${detail.applyTypeCode}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const filedPageDefault = await api("GET", "/qual-filing/filed/page?current=1&size=20");
|
|||
|
|
const draftInDefaultList = (filedPageDefault.data || []).some((r) => String(r.id) === String(filedDraftId));
|
|||
|
|
if (!draftInDefaultList) {
|
|||
|
|
pass("已备案列表-默认不含填报暂存");
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("已备案列表-默认不含填报暂存");
|
|||
|
|
recordIssue("已备案资质管理", "一般", "填报暂存不应出现在默认已备案列表", `draftId=${filedDraftId}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const filedPageDraftFilter = await api("GET", "/qual-filing/filed/page?current=1&size=20&filingStatusCode=5");
|
|||
|
|
const draftInFilteredList = (filedPageDraftFilter.data || []).some((r) => String(r.id) === String(filedDraftId));
|
|||
|
|
if (draftInFilteredList) {
|
|||
|
|
pass("已备案列表-显式筛选暂存可见");
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("已备案列表-显式筛选暂存可见", `draftId=${filedDraftId}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 模块3:备案变更管理 ──
|
|||
|
|
async function testModule3FilingChange() {
|
|||
|
|
console.log("\n=== 模块3:备案变更管理 ===");
|
|||
|
|
|
|||
|
|
const originId = ctx.originFiledId;
|
|||
|
|
if (!originId) {
|
|||
|
|
fail("发起变更", "无已备案 origin 记录");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const changePage = await api("GET", "/qual-filing/change/page?current=1&size=20");
|
|||
|
|
const changeList = changePage.data || [];
|
|||
|
|
if (changeList.length > 0) {
|
|||
|
|
pass("变更列表有数据", `count=${changeList.length}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("变更列表有数据", "应有已备案记录");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const origin = changeList.find((r) => String(r.id) === String(originId)) || changeList[0];
|
|||
|
|
if (!origin) {
|
|||
|
|
fail("变更源记录", "未找到");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (origin.filingStatusCode === 4) {
|
|||
|
|
recordIssue("备案变更管理", "阻塞", "已备案记录处于变更审核中", "无法再次发起变更");
|
|||
|
|
fail("可发起变更", "状态=变更审核中");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
pass("可发起变更", origin.filingStatusName);
|
|||
|
|
|
|||
|
|
const start = await api("POST", `/qual-filing-change/start?originFilingId=${originId}`);
|
|||
|
|
const draftId = start.data?.draftFilingId || start.data;
|
|||
|
|
if (!start.success || !draftId) {
|
|||
|
|
fail("发起变更", start.message);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
pass("发起变更", `draftId=${draftId}`);
|
|||
|
|
ctx.changeDraftId = draftId;
|
|||
|
|
|
|||
|
|
const changeBasic = {
|
|||
|
|
...FORMAL_BASIC,
|
|||
|
|
businessScope: "石油加工业,化学原料、化学品及医药制造业;金属、非金属矿及其他矿采选业",
|
|||
|
|
unitIntro: "变更后:新增矿山采选业安全评价业务范围。",
|
|||
|
|
};
|
|||
|
|
await api("POST", "/qual-filing/save-draft", { id: draftId, ...changeBasic });
|
|||
|
|
await uploadAllMaterials(draftId);
|
|||
|
|
await api("POST", "/qual-filing-commitment/save-or-update", {
|
|||
|
|
filingId: draftId,
|
|||
|
|
signDate: "2026-06-30",
|
|||
|
|
legalRepSignatureUrl: DEFAULT_URL,
|
|||
|
|
commitmentContent: "变更备案承诺书",
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const changeDetail = await api("GET", `/qual-filing/detail?id=${draftId}`);
|
|||
|
|
if (changeDetail.data?.businessScope?.includes("矿采选业")) {
|
|||
|
|
pass("变更草稿-业务范围已修改");
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("变更草稿-业务范围已修改", changeDetail.data?.businessScope);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const changeSubmit = await api("POST", "/qual-filing-change/submit", { draftFilingId: draftId });
|
|||
|
|
if (changeSubmit.success) {
|
|||
|
|
pass("提交变更", "success");
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("提交变更", changeSubmit.message);
|
|||
|
|
recordIssue("备案变更管理", "阻塞", "变更提交失败", changeSubmit.message);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const history = await api("GET", `/qual-filing-change/history?originFilingId=${originId}`);
|
|||
|
|
const changeCount = history.data?.changeCount ?? 0;
|
|||
|
|
if (history.success && changeCount > 0) {
|
|||
|
|
pass("变更历史", `changeCount=${changeCount}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("变更历史", `count=${changeCount}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const changeAfter = await api("GET", "/qual-filing/change/page?current=1&size=20");
|
|||
|
|
const updated = (changeAfter.data || []).find((r) => String(r.id) === String(originId));
|
|||
|
|
if (updated?.changeCount > 0) {
|
|||
|
|
pass("变更列表-变更次数更新", `${updated.filingStatusName} count=${updated.changeCount}`);
|
|||
|
|
}
|
|||
|
|
else {
|
|||
|
|
fail("变更列表-变更次数更新");
|
|||
|
|
recordIssue("备案变更管理", "一般", "变更列表变更次数未更新", `originId=${originId}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function checkDataQuality() {
|
|||
|
|
console.log("\n=== 数据质量检查 ===");
|
|||
|
|
const personnelPage = await api("GET", "/org-personnel/page?current=1&size=10");
|
|||
|
|
const missingName = (personnelPage.data || []).filter((p) => !p.personName && !p.staffName);
|
|||
|
|
if (missingName.length) {
|
|||
|
|
recordIssue(
|
|||
|
|
"通用",
|
|||
|
|
"一般",
|
|||
|
|
"机构人员主数据姓名为空",
|
|||
|
|
`${missingName.length} 条人员记录 personName/staffName 为空,表单人员列表可能显示空白。`,
|
|||
|
|
);
|
|||
|
|
pass("人员主数据-姓名缺失(已知)", `count=${missingName.length}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
console.log("资质申请管理 — 三模块完整流程联调");
|
|||
|
|
console.log(`API=${API_BASE} orgInfoId=${ORG_INFO_ID}\n`);
|
|||
|
|
|
|||
|
|
await checkDataQuality();
|
|||
|
|
await testModule1Application();
|
|||
|
|
await testModule2FiledManage();
|
|||
|
|
await testModule3FilingChange();
|
|||
|
|
|
|||
|
|
console.log(`\n=== 结果: ${passed} 通过, ${failed} 失败 ===`);
|
|||
|
|
console.log(`=== 问题记录: ${issues.length} 条 ===\n`);
|
|||
|
|
for (const [i, issue] of issues.entries()) {
|
|||
|
|
console.log(`${i + 1}. [${issue.module}] [${issue.severity}] ${issue.title}`);
|
|||
|
|
console.log(` ${issue.detail}\n`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { passed, failed, issues, ctx };
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await main();
|
|||
|
|
if (result.failed > 0) {
|
|||
|
|
process.exitCode = 1;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default result;
|