From d343838f4d95d8f16295b62e20b170b280fbcfc0 Mon Sep 17 00:00:00 2001 From: tangjie <122778500@qq.com> Date: Tue, 7 Jul 2026 11:18:00 +0800 Subject: [PATCH] feat --- .gitignore | 1 + blessed_by_buddha.js | 66 -- docs/dev-guide.md | 572 ------------------ docs/startup.md | 47 -- docs/test-reports/测试文档.md | 211 ------- docs/test-reports/测试用例/setup-test-env.sh | 19 - .../测试用例/test-enterprise-info-api.mjs | 221 ------- .../test-enterprise-info-granular.mjs | 371 ------------ .../test-enterprise-info-ui-check.mjs | 87 --- .../测试用例/test-qual-filing-api.mjs | 278 --------- .../测试用例/test-qual-filing-audit-flow.mjs | 199 ------ .../测试用例/test-qual-filing-full-e2e.mjs | 410 ------------- .../测试用例/test-qual-filing-interaction.mjs | 249 -------- .../EnterpriseInfo/EquipInfo/index.js | 52 +- src/pages/Container/EnterpriseInfo/index.js | 10 +- src/pages/Container/QualApplication/index.js | 6 +- 16 files changed, 47 insertions(+), 2752 deletions(-) delete mode 100644 blessed_by_buddha.js delete mode 100644 docs/dev-guide.md delete mode 100644 docs/startup.md delete mode 100644 docs/test-reports/测试文档.md delete mode 100755 docs/test-reports/测试用例/setup-test-env.sh delete mode 100644 docs/test-reports/测试用例/test-enterprise-info-api.mjs delete mode 100644 docs/test-reports/测试用例/test-enterprise-info-granular.mjs delete mode 100644 docs/test-reports/测试用例/test-enterprise-info-ui-check.mjs delete mode 100644 docs/test-reports/测试用例/test-qual-filing-api.mjs delete mode 100644 docs/test-reports/测试用例/test-qual-filing-audit-flow.mjs delete mode 100644 docs/test-reports/测试用例/test-qual-filing-full-e2e.mjs delete mode 100644 docs/test-reports/测试用例/test-qual-filing-interaction.mjs diff --git a/.gitignore b/.gitignore index 6b5df63..1181c62 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ /prototype /dist /demo +/docs /.trae/ npm-debug.log* yarn-debug.log* diff --git a/blessed_by_buddha.js b/blessed_by_buddha.js deleted file mode 100644 index 340fa53..0000000 --- a/blessed_by_buddha.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - _ooOoo_ - o8888888o - 88" . "88 - (| -_- |) - O\ = /O - ____/`---'\____ - . ' \\| |// `. - / \\||| : |||// \ - / _||||| -:- |||||- \ - | | \\\ - /// | | - | \_| ''\---/'' | | - \ .-\__ `-` ___/-. / - ___`. .' /--.--\ `. . __ - ."" '< `.___\_<|>_/___.' >'"". - | | : `- \`.;`\ _ /`;.`/ - ` : | | - \ \ `-. \_ __\ /__ _/ .-` / / - ======`-.____`-.___\_____/___.-`____.-'====== - `=---=' - - ............................................. - 佛祖保佑 永无BUG - 佛曰: - 写字楼里写字间,写字间里程序员; - 程序人员写程序,又拿程序换酒钱。 - 酒醒只在网上坐,酒醉还来网下眠; - 酒醉酒醒日复日,网上网下年复年。 - 但愿老死电脑间,不愿鞠躬老板前; - 奔驰宝马贵者趣,公交自行程序员。 - 别人笑我忒疯癫,我笑自己命太贱; - 不见满街漂亮妹,哪个归得程序员? - */ - -const blessedByBuddha - = "%c _ooOoo_\n" - + " o8888888o\n" - + " 88\" . \"88\n" - + " (| -_- |)\n" - + " O\\ = /O\n" - + " ____/`---'\\____\n" - + " . ' \\\\| |// `.\n" - + " / \\\\||| : |||// \\\n" - + " / _||||| -:- |||||- \\\n" - + " | | \\\\\\ - /// | |\n" - + " | \\_| ''\\---/'' | |\n" - + " \\ .-\\__ `-` ___/-. /\n" - + " ___`. .' /--.--\\ `. . __\n" - + " .\"\" '< `.___\\_<|>_/___.' >'\"\".\n" - + " | | : `- \\`.;`\\ _ /`;.`/ - ` : | |\n" - + " \\ \\ `-. \\_ __\\ /__ _/ .-` / /\n" - + " ======`-.____`-.___\\_____/___.-`____.-'======\n" - + " `=---='\n" - + "\n" - + "%c .............................................\n" - + " 佛祖保佑 永无BUG\n" - + "\n" - + "%c 佛曰:\n" - + " 写字楼里写字间,写字间里程序员;\n" - + " 程序人员写程序,又拿程序换酒钱。\n" - + " 酒醒只在网上坐,酒醉还来网下眠;\n" - + " 酒醉酒醒日复日,网上网下年复年。\n" - + " 但愿老死电脑间,不愿鞠躬老板前;\n" - + " 奔驰宝马贵者趣,公交自行程序员。\n" - + " 别人笑我忒疯癫,我笑自己命太贱;\n" - + " 不见满街漂亮妹,哪个归得程序员?"; -console.log(blessedByBuddha, "color:#ffd700", "color:red", "color:#1e80ff"); diff --git a/docs/dev-guide.md b/docs/dev-guide.md deleted file mode 100644 index d72a228..0000000 --- a/docs/dev-guide.md +++ /dev/null @@ -1,572 +0,0 @@ -# 证照管理子应用 — 本地开发指南 - -## 目录 - -- [1. 项目概述](#1-项目概述) -- [2. 环境准备与启动](#2-环境准备与启动) -- [3. 路由系统](#3-路由系统) -- [4. 菜单配置](#4-菜单配置) -- [5. 页面开发流程](#5-页面开发流程) -- [6. API 接口声明](#6-api-接口声明) -- [7. 状态管理(DVA)](#7-状态管理dva) -- [8. 共享组件](#8-共享组件) -- [9. 本地模拟布局](#9-本地模拟布局) -- [10. 已知问题与注意事项](#10-已知问题与注意事项) -- [11. 目录结构参考](#11-目录结构参考) - ---- - -## 1. 项目概述 - -本项目是基于 **qiankun 微前端** 框架的子应用,运行在 **GBS 底座平台** 内。使用以下核心技术栈: - -| 技术 | 说明 | -|------|------| -| React 18 | UI 组件框架 | -| Ant Design 5.x | UI 组件库 | -| @cqsjjb/jjb-dva-runtime | 底座 DVA 状态管理运行时 | -| @cqsjjb/jjb-common-decorator | 装饰器工具(权限、模块加载等) | -| rspack | 构建打包工具 | -| pnpm | 包管理器 | - -项目通过 `jjb.config.js` 中配置的 `appIdentifier: "certificate"` 注册到底座,所有页面路由前缀为 `/certificate/container/`。 - ---- - -## 2. 环境准备与启动 - -### 2.1 安装依赖 - -```bash -pnpm install -``` - -### 2.2 启动命令 - -| 命令 | 说明 | -|------|------| -| `pnpm run serve:development` | **开发环境启动**(推荐) | -| `pnpm run serve:production` | 生产环境配置启动 | -| `pnpm run build:development` | 开发环境构建 | -| `pnpm run build:production` | 生产环境构建 | - -### 2.3 访问地址 - -启动后开发服务器默认监听 `http://127.0.0.1:8081`,访问: - -``` -http://localhost:8081/certificate/container/ -http://localhost:8081/certificate/container/Test -``` - -> **注意**:本地独立启动时,应用处于 qiankun 独立运行模式(`window.__POWERED_BY_QIANKUN__` 为 false), -> 会使用内置的 `SimulatedLayout` 模拟侧边栏和标签页布局。 - -### 2.4 配置说明 - -`jjb.config.js` 中的关键配置项: - -```js -module.exports = { - appIdentifier: "certificate", // 应用唯一标识,对应路由前缀 - server: { - port: "8081", // 开发服务器端口 - host: "127.0.0.1", // 服务地址 - }, - environment: { - development: { - API_HOST: "https://gbs-gateway.qhdsafety.com", // 开发环境接口地址 - }, - }, -}; -``` - ---- - -## 3. 路由系统 - -### 3.1 约定式路由 - -本项目使用 **文件系统约定式路由**,由 `@cqsjjb/jjb-dva-runtime` 框架自动注册。页面对应的路径由目录结构决定: - -``` -src/pages/Container/<目录层级>/index.js → /certificate/container/<目录层级> -``` - -**示例:** - -| 文件路径 | 对应路由 | -|----------|----------| -| `src/pages/Container/Test/index.js` | `/certificate/container/Test` | -| `src/pages/Container/Supervision/index.js` | `/certificate/container/Supervision` | -| `src/pages/Container/Supervision/PersonnelLicense/PersonInCharge/List/index.js` | `/certificate/container/Supervision/PersonnelLicense/PersonInCharge/List` | - -### 3.2 页面组件类型 - -项目中有两类页面组件: - -1. **容器组件**(中间层级):作为 `{props.children}` 的容器,负责嵌套子页面 - ```js - function ContainerPage(props) { - return
{props.children}
; - } - export default ContainerPage; - ``` - -2. **叶子页面**(终端页面):实际的业务功能页面,通过 `Connect` 连接 DVA 数据 - ```js - import { Connect } from "@cqsjjb/jjb-dva-runtime"; - import { NS_USER_CERTIFICATE } from "~/enumerate/namespace"; - - function List(props) { - return
业务内容
; - } - - export default Connect([NS_USER_CERTIFICATE], true)(List); - ``` - -### 3.3 路由匹配规则 - -- 框架自动递归扫描 `src/pages/` 目录,每个 `index.js` 对应一个路由 -- 路由路径是将目录路径转换为小写并用 `/` 连接 -- 带参数的路由(如 `/View/:id`)需在目录中创建对应层级 - ---- - -## 4. 菜单配置 - -侧边栏菜单定义在 `src/pages/Container/Layout/menuConfig.js` 中。 - -### 4.1 菜单数据结构 - -```js -{ - key: "/certificate/container/<实际路由>", // 必须与文件目录对应的路由完全一致 - label: "菜单显示名称", - icon: , // Ant Design 图标组件 - children: [...] // 可选,子菜单项 -} -``` - -### 4.2 当前菜单结构 - -``` -📁 监管端 (Supervision) - ├── 📄 企业证照 (EnterpriseLicense) - │ ├── 企业证照管理 - │ ├── 分公司统计 - │ └── 干系人统计 - └── 📄 人员证照 (PersonnelLicense) - ├── 负责人 - ├── 安全管理员 - ├── 特种设备 - ├── 特种作业人员 - ├── 分公司人员统计 - └── 干系人人员统计 - -📁 分公司端 (BranchCompany) - └── 📄 企业证照 (EnterpriseLicense) - ├── 企业证照管理 - └── 📄 人员证照 (PersonnelLicense) - ├── 负责人 - ├── 安全管理员 - ├── 特种设备 - └── 特种作业人员 - -📁 干系人端 (Stakeholder) - └── 📄 企业证照 (EnterpriseLicense) - ├── 企业证照管理 - └── 📄 人员证照 (PersonnelLicense) - ├── 负责人 - ├── 安全管理员 - ├── 特种设备 - └── 特种作业人员 - -📁 测试页面 (Test) -``` - -### 4.3 菜单配置注意事项 - -> **关键规则**:菜单的 `key` 必须与文件系统的实际目录层级完全一致,否则会出现"页面不存在"。 - -以"干系人端 → 负责人"为例: - -- **文件位置**:`Stakeholder/EnterpriseLicense/PersonnelLicense/PersonInCharge/List/index.js` -- **正确路径**:`/certificate/container/Stakeholder/EnterpriseLicense/PersonnelLicense/PersonInCharge/List` -- **错误路径**(缺少 `EnterpriseLicense` 层级):`/certificate/container/Stakeholder/PersonnelLicense/PersonInCharge/List` - -### 4.4 工具函数 - -`menuConfig.js` 同时导出以下辅助函数: - -| 函数 | 参数 | 返回值 | 说明 | -|------|------|--------|------| -| `flattenMenu(items)` | 菜单树 | 扁平数组 | 将树形菜单拍平 | -| `findMenuPath(path)` | 当前路由 | 菜单项数组 | 从根到叶子节点的面包屑路径 | -| `getPageLabel(path)` | 当前路由 | 字符串 | 获取页面标签名 | -| `getOpenKeys(path)` | 当前路由 | 路径数组 | 默认展开的菜单项 | -| `getSelectedKeys(path)` | 当前路由 | 路径数组 | 默认选中的菜单项 | - ---- - -## 5. 页面开发流程 - -### 5.1 新增业务页面 - -以新增"监管端 → 人员证照 → 新增功能"为例: - -**步骤 1:创建目录结构** - -``` -src/pages/Container/Supervision/PersonnelLicense/NewFeature/ -├── index.js # 容器组件 -├── List/ -│ └── index.js # 列表页 -└── View/ - └── index.js # 详情页 -``` - -**步骤 2:编写容器组件** - -`src/pages/Container/Supervision/PersonnelLicense/NewFeature/index.js`: -```js -function NewFeature(props) { - return
{props.children}
; -} -export default NewFeature; -``` - -**步骤 3:编写列表页面** - -`src/pages/Container/Supervision/PersonnelLicense/NewFeature/List/index.js`: -```js -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { NS_USER_CERTIFICATE } from "~/enumerate/namespace"; - -function List(props) { - return ( -
-

新功能列表

-
- ); -} - -export default Connect([NS_USER_CERTIFICATE], true)(List); -``` - -**步骤 4:添加菜单配置** - -在 `src/pages/Container/Layout/menuConfig.js` 中添加: -```js -{ - key: "/certificate/container/Supervision/PersonnelLicense", - label: "人员证照", - icon: , - children: [ - // ... 已有菜单项 - { - key: "/certificate/container/Supervision/PersonnelLicense/NewFeature/List", - label: "新功能", - icon: , - }, - ], -} -``` - -### 5.2 新增测试页面(独立于业务) - -在 `src/pages/Container/` 下创建目录,并在菜单中添加即可: - -``` -src/pages/Container/MyDebug/ -└── index.js -``` - -菜单: -```js -{ - key: "/certificate/container/MyDebug", - label: "调试页面", - icon: , -} -``` - -### 5.3 验证路径是否正确 - -启动项目后,通过以下方式验证: - -1. 在浏览器直接访问完整 URL -2. 从侧边栏菜单点击导航 -3. 检查开发者工具 Network 面板,404 表示路径不匹配 - ---- - -## 6. API 接口声明 - -### 6.1 接口定义位置 - -API 接口声明在 `src/api/` 目录下,按模块划分: - -``` -src/api/ -├── userCertificate/index.js # 人员证书接口 -├── corpCertificate/index.js # 企业证书接口 -├── courseware/index.js # 课件接口 -└── global/index.js # 全局接口 -``` - -### 6.2 declareRequest 语法 - -```js -import { declareRequest } from "@cqsjjb/jjb-dva-runtime"; - -// 格式:declareRequest(loadingKey, "HttpMethod > 接口路径") -export const listApi = declareRequest( - "namespaceLoading", // loading 状态 key - "Post > @/certificate/userCertificate/list", // POST + 相对路径(带 @) -); - -export const getApi = declareRequest( - "namespaceLoading", - "Get > /certificate/userCertificate/getInfoById/{id}", // GET + 绝对路径 -); -``` - -**语法规则:** - -| 符号 | 说明 | 示例 | -|------|------|------| -| `@/` | 相对路径,使用 `jjb.config.js` 中配置的 `API_HOST` | `Post > @/certificate/xxx/list` | -| `/` | 绝对路径 | `Get > /config/dict-trees/list` | -| `{param}` | 路径参数 | `/certificate/xxx/delete/{id}` | -| `Post >` | POST 请求 | — | -| `Get >` | GET 请求 | — | -| `Put >` | PUT 请求 | — | -| `Delete >` | DELETE 请求 | — | - -### 6.3 在组件中使用 API - -API 通过 DVA 的 `dispatch` 和 `Connect` 来调用。`declareRequest` 会自动生成对应的 dva effect: - -```js -import { Connect } from "@cqsjjb/jjb-dva-runtime"; -import { NS_USER_CERTIFICATE } from "~/enumerate/namespace"; - -function List({ dispatch, userCertificate }) { - const fetchData = () => { - dispatch({ - type: `${NS_USER_CERTIFICATE}/userCertificateList`, // namespace/接口名 - payload: { page: 1, size: 10 }, - }); - }; - - return
{/* ... */}
; -} - -export default Connect([NS_USER_CERTIFICATE], true)(List); -``` - ---- - -## 7. 状态管理(DVA) - -### 7.1 Namespace 定义 - -`src/enumerate/namespace/index.js` 中定义了所有 DVA namespace: - -```js -export const NS_GLOBAL = defineNamespace("global"); -export const NS_PERSNONEL_CERTFICATE = defineNamespace("personnelCertificate"); -export const NS_CORP_CERTIFICATE = defineNamespace("corpCertificate"); -export const NS_USER_CERTIFICATE = defineNamespace("userCertificate"); -export const NS_COURSEWARE = defineNamespace("courseware"); -``` - -### 7.2 Connect 高阶组件 - -```js -Connect([NS_USER_CERTIFICATE], true)(MyComponent) -``` - -- 第一个参数:需要连接的 namespace 数组 -- 第二个参数:是否在组件挂载时自动加载数据(`true` 表示自动触发 `load` effect) - -### 7.3 数据流向 - -``` -dispatch(action) → DVA Effect → declareRequest → HTTP API → reducer → state 更新 → 组件重渲染 -``` - -### 7.4 本地开发时的限制 - -本地独立运行时(非底座环境),DVA 状态管理正常可用,但 `declareRequest` 发起的 HTTP 请求会因缺少认证 Token 而返回 **401**。详见 [§10 已知问题](#10-已知问题与注意事项)。 - ---- - -## 8. 共享组件 - -业务组件位于 `src/components/` 目录下,目前包括: - -``` -src/components/ -├── PersonInChargeInfo/ # 负责人详情组件 -├── PersonInChargeList/ # 负责人列表组件(含搜索、分页、CRUD) -├── SpecialCertificateInfo/ # 特种证书详情组件 -└── SpecialCertificateList/ # 特种证书列表组件 -``` - -组件通过 `@/components/xxx` 或别名 `~/components/xxx` 引入: - -```js -import PersonInChargeList from "~/components/PersonInChargeList"; -``` - -### 8.1 组件参数传递 - -列表组件通过 `props` 接收业务参数: - -```js - -``` - ---- - -## 9. 本地模拟布局 - -### 9.1 工作原理 - -`src/pages/Container/index.js` 中的 `AppMiddle` 组件检测运行环境: - -```js -function AppMiddle(props) { - const content = process.env.NODE_ENV === "development" && !window.__POWERED_BY_QIANKUN__ - ? {props.children} - : props.children; - // ... -} -``` - -- **本地开发**(`development` + 非 qiankun):使用 `SimulatedLayout` 包裹内容,提供模拟的侧边栏、面包屑和标签页 -- **底座环境**:由底座平台提供真实的布局 - -### 9.2 SimulatedLayout 功能 - -`src/pages/Container/Layout/index.jsx` 提供: - -| 功能 | 说明 | -|------|------| -| 侧边栏 | 使用 `menuConfig.js` 的菜单数据渲染,支持折叠 | -| 面包屑 | 根据当前路径自动计算导航路径 | -| 多标签页 | 支持打开多个页面标签,右键关闭操作 | -| 标签持久化 | 使用 sessionStorage 保存标签状态 | -| 主题适配 | 通过 `window.base?.themeConfig` 支持底座主题切换 | - ---- - -## 10. 已知问题与注意事项 - -### 10.1 API 401 错误 - -**现象**:本地启动后,点击侧边栏菜单(除 Test 页面外),控制台报 401 错误。 - -**原因**:本项目是 qiankun 微前端子应用,API 请求需要的认证 Token 由 GBS 底座注入。本地独立运行时没有底座,也就没有 Token,所有 API 调用被拒绝。 - -**处理方式**: -- **纯前端调试**:使用 Test 页面(`/certificate/container/Test`),该页面不发起任何 API 请求 -- **完整功能调试**:在 GBS 底座环境中打开应用 - -### 10.2 路由路径与菜单 key 必须匹配 - -菜单的 `key` 值必须和文件目录层级完全一致。错误示例见 [§4.3 菜单配置注意事项](#43-菜单配置注意事项)。 - -### 10.3 重命名项目目录后需重新安装依赖 - -由于 pnpm 使用基于绝对路径的符号链接管理 `node_modules`,重命名项目根目录后需要: - -```bash -Remove-Item -Recurse -Force node_modules -pnpm install -``` - -### 10.4 权限控制 - -页面使用 `@cqsjjb/jjb-common-decorator/permission` 的 `Permission` 装饰器控制访问: - -```js -import { Permission } from "@cqsjjb/jjb-common-decorator/permission"; - -export default Connect([NS_USER_CERTIFICATE], true)(Permission(List)); -``` - -权限码由底座平台管理,本地开发时权限校验可能不生效。 - -### 10.5 pnpm overrides - -`package.json` 中通过 `pnpm.overrides` 强制统一 React 等依赖版本,避免多副本冲突: - -```json -{ - "pnpm": { - "overrides": { - "react": "$react", - "react-dom": "$react-dom" - } - } -} -``` - ---- - -## 11. 目录结构参考 - -``` -safetyEval-service-frontend/ -├── public/ -│ └── index.html # HTML 模板 -├── src/ -│ ├── main.js # 应用入口(setup + 生命周期导出) -│ ├── api/ # API 接口声明 -│ │ ├── global/ -│ │ ├── userCertificate/ -│ │ ├── corpCertificate/ -│ │ └── courseware/ -│ ├── components/ # 共享业务组件 -│ │ ├── PersonInChargeList/ -│ │ ├── PersonInChargeInfo/ -│ │ ├── SpecialCertificateList/ -│ │ └── SpecialCertificateInfo/ -│ ├── enumerate/ # 枚举/常量定义 -│ │ ├── namespace/ # DVA namespace -│ │ ├── context/ # React Context -│ │ └── constant/ # 常量 -│ ├── pages/ -│ │ └── Container/ -│ │ ├── index.js # 容器入口(Antd 主题 + 布局切换) -│ │ ├── Entry/ # 底座入口页 -│ │ ├── Layout/ # 本地模拟布局 -│ │ │ ├── index.jsx # SimulatedLayout 组件 -│ │ │ └── menuConfig.js # ★ 菜单配置 -│ │ ├── Test/ # 测试页面(无 API 依赖) -│ │ ├── Supervision/ # 监管端 -│ │ │ ├── EnterpriseLicense/ -│ │ │ └── PersonnelLicense/ -│ │ ├── BranchCompany/ # 分公司端 -│ │ │ └── EnterpriseLicense/ -│ │ └── Stakeholder/ # 干系人端 -│ │ └── EnterpriseLicense/ -│ ├── assets/ # 静态资源 -│ └── utils/ # 工具函数 -├── jjb.config.js # 项目配置(端口、API_HOST、主题等) -├── package.json -├── pnpm-lock.yaml -└── pnpm-workspace.yaml -``` diff --git a/docs/startup.md b/docs/startup.md deleted file mode 100644 index 6fb7745..0000000 --- a/docs/startup.md +++ /dev/null @@ -1,47 +0,0 @@ -# 证照管理子应用 — 快速启动 - -## 环境要求 - -- Node.js >= 18 -- pnpm >= 8 - -## 启动 - -```bash -# 安装依赖(首次或重命名项目目录后) -pnpm install - -# 启动开发服务器 -pnpm run serve:development -``` - -## 访问 - -| 地址 | 说明 | -|------|------| -| `http://localhost:8081/certificate/container/` | 应用首页 | -| `http://localhost:8081/certificate/container/Test` | 测试页面(无 API 依赖,纯前端调试可用) | - -## 常用命令 - -| 命令 | 说明 | -|------|------| -| `pnpm run serve:development` | 开发环境启动(推荐) | -| `pnpm run serve:production` | 生产环境配置启动 | -| `pnpm run build:development` | 开发环境构建 | -| `pnpm run build:production` | 生产环境构建 | - -## 技术栈 - -- React 18 + Ant Design 5.x -- @cqsjjb/jjb-dva-runtime(GBS 底座 DVA 运行时) -- rspack 构建 / pnpm 包管理 -- qiankun 微前端子应用 - -## 重要提示 - -- 本项目为 GBS 底座微前端子应用,独立启动时有内置模拟布局 -- 本地独立运行时 API 调用会返回 401(缺少底座注入的认证 Token) -- 仅 Test 页面可在本地无底座环境下正常访问 -- 重命名项目目录后需 `pnpm install` 重建 node_modules 符号链接 -- 完整开发指南请参考 [dev-guide.md](./dev-guide.md) diff --git a/docs/test-reports/测试文档.md b/docs/test-reports/测试文档.md deleted file mode 100644 index 4185771..0000000 --- a/docs/test-reports/测试文档.md +++ /dev/null @@ -1,211 +0,0 @@ -# 企业信息管理 — 测试报告(最新) - -> 覆盖模块:机构信息、部门岗位、人员信息、人员变更、离职申请、资质证书、装备信息 -> 原型对照:`docs/原型/设计文件 (3).pdf` ~ `设计文件 (9).pdf` -> 更新日期:2026-06-23 - ---- - -## 1. 测试环境 - -| 项 | 说明 | -|---|---| -| 前端 | `safetyEval-service-frontend`,`npm run dev` / `pnpm dev` | -| 后端 | `safetyEval-service`,`http://127.0.0.1:8095/safetyEval`,profile=`local` | -| 数据库 | 执行 `safetyEval-service/docs/init-v2.sql` 及末尾测试数据段 | -| Node | `source scripts/setup-test-env.sh` 或 JetBrains Node 24.x | - ---- - -## 2. 自动化脚本 - -### 2.1 接口联调(细粒度) - -```bash -cd safetyEval-service-frontend -source scripts/setup-test-env.sh # 可选 -node scripts/test-enterprise-info-granular.mjs -``` - -- 脚本:`scripts/test-enterprise-info-granular.mjs` -- 覆盖:8 模块 CRUD、分页、搜索、字段组装(`deptName`/`postName`/`account`/`auditStatusCode`) -- 环境变量:`API_BASE`(默认 `http://127.0.0.1:8095/safetyEval`) - -### 2.2 前端 UI 静态检查 - -```bash -node scripts/test-enterprise-info-ui-check.mjs -``` - -- 脚本:`scripts/test-enterprise-info-ui-check.mjs` -- 检查:公共工具、列表列、弹窗布局、变更次数样式、默认上传配置 - -### 2.3 一键执行 - -```bash -node scripts/test-enterprise-info-granular.mjs && node scripts/test-enterprise-info-ui-check.mjs -``` - ---- - -## 3. 问题修复对照(用户反馈 8 项) - -| # | 问题 | 处理方式 | 验证用例 | -|---|------|----------|----------| -| 1 | 列表未展示部门/岗位 | 后端 `OrgPersonnelViewEnricher` 组装 `deptName`/`postName`;前端 adapter 映射 | EI-API-STAFF-05~08;UI-STAFF-03 | -| 2 | 新增/编辑文字遮挡 | 弹窗改 `layout="vertical"` 或 `labelCol/wrapperCol=24` | UI-PAGE-*-03;手工打开各模块弹窗 | -| 3 | 离职缺审核状态/用户名 | CO 增 `account`;enricher 组装;列表 Tag 展示 | EI-API-RESIGN-04~05;UI-RESIGN-01~02 | -| 4 | 页面卡顿/报错 | 去掉失败的上传/删文件调用;修复 SELECT 空选项 | 控制台无红错;列表加载 <3s | -| 5 | 变更次数配色 | `CHANGE_COUNT_STYLE` 蓝色高亮/灰色零值 | UI-CHANGE-01;人员变更列表目视 | -| 6 | 新增人员部门/岗位不可选 | 人员表单改用 antd `Select`;`formSelectField` 适配 Search | UI-STAFF-01~02;TC-STAFF-03 | -| 7 | 上传默认通过 | `src/utils/mockUpload.js` 固定 URL | UI-COMMON-03;TC-UPLOAD-* | -| 8 | 其他页面问题 | 见第 4 节用例矩阵 | 全量手工 + 自动化 | - ---- - -## 4. 测试用例矩阵 - -### 4.1 机构信息(原型:设计文件 3) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-ORG-01 | UI | 进入机构信息页 | 标题/副标题与原型一致;表单只读 | -| TC-ORG-02 | 功能 | 点击修改 → 暂存 | 成功提示;`authStatusCode=0` | -| TC-ORG-03 | 功能 | 点击提交 | 成功提示;字段回显正确 | -| TC-ORG-04 | API | EI-API-ORG-* | 分页/详情/修改通过 | - -### 4.2 部门岗位(原型:设计文件 4) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-DEPT-01 | UI | 左侧树 + 右侧列表 | 布局与原型一致 | -| TC-DEPT-02 | 功能 | 新增/编辑部门 | 弹窗 vertical,文字不遮挡 | -| TC-DEPT-03 | 功能 | 进入岗位列表,增删改岗位 | 岗位关联当前部门 | -| TC-DEPT-04 | API | EI-API-DEPT/POS-* | 通过 | - -### 4.3 人员信息(原型:设计文件 5) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-STAFF-01 | UI | 列表列 | 用户名称、账号、**部门**、**岗位**、证照 | -| TC-STAFF-02 | 功能 | 搜索部门/岗位 | 下拉有选项,筛选生效 | -| TC-STAFF-03 | 功能 | 新增人员,选部门后选岗位 | 岗位按部门过滤,可提交 | -| TC-STAFF-04 | 功能 | 编辑/查看/重置密码/删除 | 各操作正常 | -| TC-STAFF-05 | 功能 | 证书子页 | 上传默认 URL,增删改查 | -| TC-STAFF-06 | API | EI-API-STAFF/CERT-* | 通过;列表含 deptName/postName | - -### 4.4 人员变更(原型:设计文件 6) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-CHANGE-01 | UI | 信息变更数 >0 | 蓝色链接样式,可点进日志 | -| TC-CHANGE-02 | UI | 变更数 =0 | 灰色显示 | -| TC-CHANGE-03 | 功能 | 离职审核通过/退回 | 状态 Tag 更新 | -| TC-CHANGE-04 | API | EI-API-CHANGE-* | 分页通过 | - -### 4.5 离职申请(原型:设计文件 7) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-RESIGN-01 | UI | 列表 | 用户名、姓名、部门、**审核状态 Tag** | -| TC-RESIGN-02 | 功能 | 新增离职申请 | 弹窗 vertical;上传默认通过 | -| TC-RESIGN-03 | 功能 | 按审核状态筛选 | 结果正确 | -| TC-RESIGN-04 | API | EI-API-RESIGN-* | account/auditStatusCode 有值 | - -### 4.6 资质证书(原型:设计文件 8) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-QUAL-01 | UI | 列表/弹窗 | 标签不遮挡;保密提示可见 | -| TC-QUAL-02 | 功能 | 新增证书 + 上传 | 默认图片 URL 写入 `certImageUrl` | -| TC-QUAL-03 | API | EI-API-QUAL-* | 通过 | - -### 4.7 装备信息(原型:设计文件 9) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-EQUIP-01 | UI | 列表/弹窗 | 双列表单,标签完整 | -| TC-EQUIP-02 | 功能 | 是否双路下拉 | 可选是/否 | -| TC-EQUIP-03 | API | EI-API-EQUIP-* | 通过 | - -### 4.8 上传(全局) - -| ID | 类型 | 步骤 | 预期 | -|----|------|------|------| -| TC-UPLOAD-01 | 功能 | 离职报告上传 | `reportFileUrl` = 默认 URL | -| TC-UPLOAD-02 | 功能 | 机构/人员证书上传 | `certImageUrl` / `certAttachmentUrl` = 默认 URL | - ---- - -## 5. 原型 UI 对照检查清单 - -手工打开各页,对照 PDF 检查: - -- [ ] 页面标题、副标题文案 -- [ ] 搜索区字段与原型一致 -- [ ] 表格列顺序、列名 -- [ ] 主按钮文案(新增/添加) -- [ ] 弹窗宽度、表单项顺序 -- [ ] 信息变更数颜色(蓝/灰) -- [ ] 审核状态 Tag 颜色(灰/绿/红) -- [ ] 无控制台 Error / 无长时间 Loading - ---- - -## 6. 执行记录(2026-06-23 第二轮) - -``` -接口测试: 58 / 58 通过 -UI 静态检查: 31 / 31 通过 - -本轮修复: -- 资质 remark 全链路 + 启用/禁用按钮 -- 人员变更:账户列、变更数颜色(String key)、就职状态/审核状态 Tag、后端 employmentStatusCode 查询 -- 人员:移除重置密码、birthDate dayjs 回显/提交格式化 -- 部门岗位:左树右岗布局,按 deptId 查岗位 -- 离职:账户列、remark 分离、personnelId 自动匹配、时间格式化 -- 装备:instrumentType 列表字段、表单回显 -- 时间:formatDateTime → YYYY-MM-DD HH:mm:ss -``` - -## 7. 执行记录(2026-06-23 第一轮) - -``` -执行人:自动化脚本 -执行时间:2026-06-23(后端重启后) -后端:http://127.0.0.1:8095/safetyEval - -接口测试: 58 / 58 通过 -UI 静态检查: 31 / 31 通过 - -字段组装验证(已通过): -- EI-API-STAFF-05~08:deptName / postName 正常 -- EI-API-RESIGN-04~05:auditStatusCode / account 正常 - -重跑命令: - source scripts/setup-test-env.sh - node scripts/test-enterprise-info-granular.mjs - node scripts/test-enterprise-info-ui-check.mjs -``` - -### 6.1 手工记录模板 - ---- - -## 7. 已知限制 - -1. **上传**:联调阶段不走真实文件服务,统一写入固定花瓣 URL。 -2. **原型像素级**:PDF 与 Ant Design 组件存在间距差异,以字段/交互一致为准。 -3. **人员变更删除**:列表删除为变更记录删除,与人员主数据删除不同,需与产品确认。 - ---- - -## 8. 相关代码路径 - -| 用途 | 路径 | -|------|------| -| 页面 | `src/pages/Container/EnterpriseInfo/**` | -| API 适配 | `src/api/enterpriseInfo/adapter.js` | -| 表单工具 | `src/utils/enterpriseForm.js` | -| 上传 Mock | `src/utils/mockUpload.js` | -| 后端组装 | `safetyEval-app/.../OrgPersonnelViewEnricher.java` | diff --git a/docs/test-reports/测试用例/setup-test-env.sh b/docs/test-reports/测试用例/setup-test-env.sh deleted file mode 100755 index c14c4df..0000000 --- a/docs/test-reports/测试用例/setup-test-env.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# 企业信息管理联调测试环境变量 -# 用法: source scripts/setup-test-env.sh - -export API_BASE="${API_BASE:-http://127.0.0.1:8095/safetyEval}" -export SAFETY_EVAL_API_HOST="${SAFETY_EVAL_API_HOST:-http://127.0.0.1:8095}" - -# JetBrains 内置 Node(系统 PATH 无 node 时使用) -if ! command -v node >/dev/null 2>&1; then - _JB_NODE="$HOME/Library/Application Support/JetBrains/IntelliJIdea2025.3/node/versions/24.16.0/bin/node" - if [[ -x "$_JB_NODE" ]]; then - export NODE="$_JB_NODE" - export PATH="$(dirname "$_JB_NODE"):$PATH" - fi -fi - -echo "API_BASE=$API_BASE" -echo "SAFETY_EVAL_API_HOST=$SAFETY_EVAL_API_HOST" -echo "NODE=$(command -v node || echo '未找到')" diff --git a/docs/test-reports/测试用例/test-enterprise-info-api.mjs b/docs/test-reports/测试用例/test-enterprise-info-api.mjs deleted file mode 100644 index 2fc0bd7..0000000 --- a/docs/test-reports/测试用例/test-enterprise-info-api.mjs +++ /dev/null @@ -1,221 +0,0 @@ -/** - * 企业信息管理 - 后端接口联调冒烟测试 - * 运行: API_BASE=http://127.0.0.1:8095/safetyEval node scripts/test-enterprise-info-api.mjs - */ -const API_BASE = process.env.API_BASE || "http://127.0.0.1:8095/safetyEval"; - -const results = []; -let passed = 0; -let failed = 0; - -function assert(name, condition, detail = "") { - if (condition) { - passed += 1; - results.push({ name, status: "PASS", detail }); - } - else { - failed += 1; - results.push({ name, status: "FAIL", detail }); - } -} - -async function request(method, path, body) { - const url = `${API_BASE}${path}`; - const options = { - method, - headers: { "Content-Type": "application/json" }, - }; - if (body !== undefined) { - options.body = JSON.stringify(body); - } - const res = await fetch(url, options); - const text = await res.text(); - try { - return JSON.parse(text); - } - catch { - return { success: false, raw: text, status: res.status }; - } -} - -async function runTests() { - console.log(`\nAPI Base: ${API_BASE}\n`); - - // ── 机构信息 ── - let orgPage = await request("GET", "/org-info/page?current=1&size=1"); - assert("机构信息-分页", orgPage.success === true, `code=${orgPage.code}`); - - let orgId = orgPage.data?.[0]?.id; - if (!orgId) { - const created = await request("POST", "/org-info/save", { - unitName: "联调测试机构", - creditCode: "91110000MA0TEST001", - principalName: "测试负责人", - principalPhone: "13800138000", - registerAddress: "测试地址", - businessAddress: "测试地址", - authStatusCode: 0, - authStatusName: "草稿", - }); - assert("机构信息-新增", created.success === true, created.message); - orgId = created.data?.id; - } - - if (orgId) { - const orgDetail = await request("GET", `/org-info/get?id=${orgId}`); - assert("机构信息-详情", orgDetail.success === true && orgDetail.data?.id === orgId); - const modified = await request("POST", "/org-info/modify", { - ...orgDetail.data, - principalName: "测试负责人-已改", - authStatusCode: 1, - authStatusName: "已提交", - }); - assert("机构信息-修改", modified.success === true, modified.message); - } - - // ── 部门 ── - const deptPage = await request("GET", "/org-department/page?current=1&size=10"); - assert("部门-分页", deptPage.success === true, `total=${deptPage.total}`); - - const deptCreated = await request("POST", "/org-department/save", { - deptName: "联调测试部门", - managerName: "测试负责人", - deptLevelName: "一级", - }); - assert("部门-新增", deptCreated.success === true, deptCreated.message); - const deptId = deptCreated.data?.id; - - if (deptId) { - const deptModified = await request("POST", "/org-department/modify", { - id: deptId, - deptName: "联调测试部门-已改", - managerName: "测试负责人", - deptLevelName: "一级", - }); - assert("部门-修改", deptModified.success === true); - } - - // ── 岗位 ── - const posCreated = await request("POST", "/org-position/save", { - positionName: "联调测试岗位", - dutyDesc: "测试职责", - deptId: deptId || "102", - }); - assert("岗位-新增", posCreated.success === true, posCreated.message); - const posId = posCreated.data?.id; - - const posPage = await request("GET", `/org-position/page?current=1&size=10&deptId=${deptId || ""}`); - assert("岗位-分页", posPage.success === true, `count=${posPage.data?.length}`); - - // ── 人员 ── - const staffCreated = await request("POST", "/org-personnel/save", { - userName: "联调测试人员", - account: "13900001234", - genderCode: 1, - genderName: "男", - employmentStatusCode: 1, - employmentStatusName: "在职", - }); - assert("人员-新增", staffCreated.success === true, staffCreated.message); - const staffId = staffCreated.data?.id; - - if (staffId) { - const staffPage = await request("GET", `/org-personnel/page?current=1&size=10&userName=${encodeURIComponent("联调")}`); - assert("人员-分页", staffPage.success === true); - const staffDetail = await request("GET", `/org-personnel/get?id=${staffId}`); - assert("人员-详情", staffDetail.success === true && staffDetail.data?.userName?.includes("联调")); - } - - // ── 装备 ── - const equipCreated = await request("POST", "/org-equipment/save", { - deviceName: "联调测试设备", - deviceModel: "T-001", - manufacturer: "测试厂家", - enableFlag: 1, - }); - assert("装备-新增", equipCreated.success === true, equipCreated.message); - const equipId = equipCreated.data?.id; - - const equipPage = await request("GET", "/org-equipment/page?current=1&size=10"); - assert("装备-分页", equipPage.success === true); - - if (equipId) { - const equipDetail = await request("GET", `/org-equipment/get?id=${equipId}`); - const toggleFlag = equipDetail.data?.enableFlag === 1 ? 0 : 1; - const toggled = await request("POST", "/org-equipment/modify", { - ...equipDetail.data, - enableFlag: toggleFlag, - }); - assert("装备-启停(modify)", toggled.success === true); - } - - // ── 资质证书 ── - const qualCreated = await request("POST", "/org-qualification/save", { - licenseTypeName: "测试证照", - certName: "联调测试证书", - certNo: `TEST-${Date.now()}`, - issueOrg: "测试机关", - issueDate: "2026-01-01", - validStartDate: "2026-01-01", - validEndDate: "2027-01-01", - enableFlag: 1, - }); - assert("资质-新增", qualCreated.success === true, qualCreated.message); - const qualId = qualCreated.data?.id; - - const qualPage = await request("GET", "/org-qualification/page?current=1&size=10"); - assert("资质-分页", qualPage.success === true); - - // ── 离职申请 ── - const resignCreated = await request("POST", "/org-resign-apply/save", { - applicantName: "联调测试人员", - applyTime: "2026-06-20T10:00:00", - expectedResignDate: "2026-07-31", - resignReason: "联调测试", - auditStatusCode: 0, - auditStatusName: "未审核", - }); - assert("离职申请-新增", resignCreated.success === true, resignCreated.message); - const resignId = resignCreated.data?.id; - - if (resignId) { - const audited = await request("POST", "/org-resign-apply/modify", { - id: resignId, - auditStatusCode: 1, - auditStatusName: "已审核", - }); - assert("离职申请-审核", audited.success === true); - } - - // ── 清理 ── - if (qualId) { - await request("POST", `/org-qualification/delete?id=${qualId}`); - } - if (equipId) { - await request("POST", `/org-equipment/delete?id=${equipId}`); - } - if (staffId) { - await request("POST", `/org-personnel/delete?id=${staffId}`); - } - if (posId) { - await request("POST", `/org-position/delete?id=${posId}`); - } - if (deptId) { - await request("POST", `/org-department/delete?id=${deptId}`); - } - - assert("清理-完成", true); - - console.log("\n========== 企业信息管理 接口联调测试 ==========\n"); - results.forEach((r) => { - const icon = r.status === "PASS" ? "✓" : "✗"; - console.log(`${icon} [${r.status}] ${r.name}${r.detail ? ` — ${r.detail}` : ""}`); - }); - console.log(`\n合计: ${passed + failed} 通过: ${passed} 失败: ${failed}\n`); - process.exit(failed > 0 ? 1 : 0); -} - -runTests().catch((err) => { - console.error("测试执行失败:", err.message); - process.exit(1); -}); diff --git a/docs/test-reports/测试用例/test-enterprise-info-granular.mjs b/docs/test-reports/测试用例/test-enterprise-info-granular.mjs deleted file mode 100644 index 8e76965..0000000 --- a/docs/test-reports/测试用例/test-enterprise-info-granular.mjs +++ /dev/null @@ -1,371 +0,0 @@ -/** - * 企业信息管理 - 细粒度接口联调测试 - * - * 运行: - * source scripts/setup-test-env.sh - * node scripts/test-enterprise-info-granular.mjs - */ -const API_BASE = process.env.API_BASE || "http://127.0.0.1:8095/safetyEval"; -const TS = Date.now(); - -const results = []; -let passed = 0; -let failed = 0; -const createdIds = {}; - -/** 保持 ID 为字符串,避免雪花 ID 超出 JS 安全整数精度 */ -function normalizeId(id) { - if (id === undefined || id === null || id === "") return undefined; - return String(id); -} - -function record(id, name, condition, detail = "") { - const status = condition ? "PASS" : "FAIL"; - if (condition) passed += 1; - else failed += 1; - results.push({ id, name, status, detail }); -} - -async function request(method, path, body) { - const url = `${API_BASE}${path}`; - const options = { - method, - headers: { "Content-Type": "application/json" }, - }; - if (body !== undefined) options.body = JSON.stringify(body); - const res = await fetch(url, options); - const text = await res.text(); - try { - return { httpStatus: res.status, ...JSON.parse(text) }; - } - catch { - return { httpStatus: res.status, success: false, raw: text }; - } -} - -function assertSuccess(id, name, res, extra = "") { - const ok = res.success === true && res.code === "0"; - record(id, name, ok, ok ? extra : (res.message || res.raw || `http=${res.httpStatus}`)); - return ok; -} - -function assertHasData(id, name, res, extra = "") { - const ok = res.success === true && res.code === "0" && res.data != null; - record(id, name, ok, ok ? extra : (res.message || "data 为空")); - return ok; -} - -async function testPagination(id, name, path, minTotal = 1) { - const sep = path.includes("?") ? "&" : "?"; - const page1 = await request("GET", `${path}${sep}current=1&size=2`); - const ok1 = assertSuccess(`${id}-01`, `${name}-分页第1页`, page1, `total=${page1.total ?? page1.totalCount ?? "?"}`); - const page2 = await request("GET", `${path}${sep}current=2&size=2`); - assertSuccess(`${id}-02`, `${name}-分页第2页`, page2); - if (ok1 && (page1.total ?? page1.totalCount ?? 0) >= minTotal) { - record(`${id}-03`, `${name}-分页total有效`, true, `total=${page1.total ?? page1.totalCount}`); - } - return page1; -} - -async function run() { - console.log(`\n=== 企业信息管理 细粒度接口测试 ===`); - console.log(`API Base: ${API_BASE}`); - console.log(`Timestamp: ${TS}\n`); - - const orgPage = await testPagination("EI-API-ORG", "机构信息", "/org-info/page", 1); - let orgId = orgPage.data?.[0]?.id; - - if (orgId) { - const orgGet = await request("GET", `/org-info/get?id=${normalizeId(orgId)}`); - assertHasData("EI-API-ORG-04", "机构信息-详情", orgGet, `id=${orgId}`); - const orgMod = await request("POST", "/org-info/modify", { - ...orgGet.data, - principalName: orgGet.data.principalName, - }); - assertSuccess("EI-API-ORG-05", "机构信息-修改", orgMod); - } - - await testPagination("EI-API-DEPT", "部门", "/org-department/page", 1); - - const deptName = `联调部门-${TS}`; - const deptSave = await request("POST", "/org-department/save", { - deptName, - managerName: "测试负责人", - deptLevelName: "一级", - }); - if (assertSuccess("EI-API-DEPT-04", "部门-新增", deptSave)) { - createdIds.dept = normalizeId(deptSave.data?.id); - const deptGet = await request("GET", `/org-department/get?id=${createdIds.dept}`); - if (assertHasData("EI-API-DEPT-05", "部门-详情", deptGet)) { - const deptMod = await request("POST", "/org-department/modify", { - id: createdIds.dept, - deptName: `${deptName}-改`, - managerName: "测试负责人", - deptLevelName: "一级", - }); - assertSuccess("EI-API-DEPT-06", "部门-修改", deptMod); - const deptSearch = await request("GET", `/org-department/page?current=1&size=10&deptName=${encodeURIComponent(deptName)}`); - assertSuccess("EI-API-DEPT-07", "部门-名称搜索", deptSearch, `count=${deptSearch.data?.length ?? 0}`); - } - } - - const posSave = await request("POST", "/org-position/save", { - positionName: `联调岗位-${TS}`, - dutyDesc: "测试职责", - deptId: createdIds.dept || "102", - }); - if (assertSuccess("EI-API-POS-01", "岗位-新增", posSave)) { - createdIds.pos = normalizeId(posSave.data?.id); - } - await testPagination("EI-API-POS-PG", "岗位", "/org-position/page", 1); - if (createdIds.pos) { - const posGet = await request("GET", `/org-position/get?id=${createdIds.pos}`); - if (assertHasData("EI-API-POS-04", "岗位-详情", posGet)) { - const posMod = await request("POST", "/org-position/modify", { - id: createdIds.pos, - positionName: `联调岗位-${TS}-改`, - dutyDesc: "已修改", - deptId: createdIds.dept || posGet.data?.deptId, - }); - assertSuccess("EI-API-POS-05", "岗位-修改", posMod); - } - } - - const staffSave = await request("POST", "/org-personnel/save", { - userName: `联调人员-${TS}`, - account: `139${String(TS).slice(-8)}`, - genderCode: 1, - genderName: "男", - employmentStatusCode: 1, - employmentStatusName: "在职", - deptId: createdIds.dept || "102", - postId: createdIds.pos || "205", - }); - if (assertSuccess("EI-API-STAFF-01", "人员-新增", staffSave)) { - createdIds.staff = normalizeId(staffSave.data?.id); - const staffGet = await request("GET", `/org-personnel/get?id=${createdIds.staff}`); - if (assertHasData("EI-API-STAFF-02", "人员-详情", staffGet)) { - record( - "EI-API-STAFF-05", - "人员-部门名称组装", - !!staffGet.data.deptName, - `deptName=${staffGet.data.deptName ?? "(空)"}`, - ); - record( - "EI-API-STAFF-06", - "人员-岗位名称组装", - !!staffGet.data.postName, - `postName=${staffGet.data.postName ?? "(空)"}`, - ); - const staffPage = await request("GET", `/org-personnel/page?current=1&size=10&userName=${encodeURIComponent("联调人员")}`); - assertSuccess("EI-API-STAFF-03", "人员-姓名搜索", staffPage); - const pageRow = staffPage.data?.[0]; - if (pageRow) { - record("EI-API-STAFF-07", "人员列表-部门字段", !!pageRow.deptName, `deptName=${pageRow.deptName ?? "(空)"}`); - record("EI-API-STAFF-08", "人员列表-岗位字段", !!pageRow.postName, `postName=${pageRow.postName ?? "(空)"}`); - } - const staffMod = await request("POST", "/org-personnel/modify", { - id: createdIds.staff, - userName: `联调人员-${TS}-改`, - account: staffGet.data.account, - genderCode: staffGet.data.genderCode, - genderName: staffGet.data.genderName, - employmentStatusCode: staffGet.data.employmentStatusCode, - employmentStatusName: staffGet.data.employmentStatusName, - deptId: staffGet.data.deptId, - postId: staffGet.data.postId, - }); - assertSuccess("EI-API-STAFF-04", "人员-修改", staffMod); - if (staffMod.success) { - const staffAfterMod = await request("GET", `/org-personnel/get?id=${createdIds.staff}`); - record( - "EI-API-STAFF-10", - "人员-修改后变更次数", - (staffAfterMod.data?.changeCount ?? 0) >= 1, - `changeCount=${staffAfterMod.data?.changeCount ?? 0}`, - ); - } - const dupSave = await request("POST", "/org-personnel/save", { - userName: `重复账号-${TS}`, - account: staffGet.data.account, - genderCode: 1, - genderName: "男", - employmentStatusCode: 1, - employmentStatusName: "在职", - deptId: staffGet.data.deptId, - postId: staffGet.data.postId, - }); - record( - "EI-API-STAFF-11", - "人员-账号重复校验", - dupSave.success === false && String(dupSave.message || "").includes("账号"), - dupSave.message || `code=${dupSave.code}`, - ); - const resetPwd = await request("POST", `/org-personnel/reset-password?id=${normalizeId(createdIds.staff)}`); - assertSuccess("EI-API-STAFF-09", "人员-重置密码", resetPwd); - } - } - - if (createdIds.staff) { - const certSave = await request("POST", "/org-personnel-cert/save", { - personnelId: createdIds.staff, - certName: `联调证书-${TS}`, - certNo: `CERT-${TS}`, - issueOrg: "测试机关", - validStartDate: "2026-01-01", - validEndDate: "2027-01-01", - }); - if (assertSuccess("EI-API-CERT-01", "人员证书-新增", certSave)) { - createdIds.cert = normalizeId(certSave.data?.id); - await testPagination("EI-API-CERT", "人员证书", `/org-personnel-cert/page?personnelId=${createdIds.staff}`, 0); - } - } - else { - record("EI-API-CERT-01", "人员证书-新增", false, "跳过:无人员 id"); - } - - await testPagination("EI-API-CHANGE", "人员变更", "/org-personnel-change/page", 0); - - // 联调数据可能被审核操作污染,测试前重置种子离职单为「未审核」 - await request("POST", "/org-resign-apply/modify", { - id: "701", - personnelId: "403", - auditStatusCode: 0, - auditStatusName: "未审核", - }); - - const equipSave = await request("POST", "/org-equipment/save", { - deviceName: `联调设备-${TS}`, - deviceModel: "T-001", - manufacturer: "测试厂家", - enableFlag: 1, - }); - if (assertSuccess("EI-API-EQUIP-01", "装备-新增", equipSave)) { - createdIds.equip = normalizeId(equipSave.data?.id); - const equipGet = await request("GET", `/org-equipment/get?id=${createdIds.equip}`); - if (assertHasData("EI-API-EQUIP-02", "装备-详情", equipGet)) { - const toggle = equipGet.data?.enableFlag === 1 ? 0 : 1; - const equipMod = await request("POST", "/org-equipment/modify", { ...equipGet.data, enableFlag: toggle }); - assertSuccess("EI-API-EQUIP-03", "装备-启停", equipMod); - } - } - await testPagination("EI-API-EQUIP-PG", "装备", "/org-equipment/page", 1); - const equipByInstrument = await request("GET", "/org-equipment/page?current=1&size=10&instrumentType=检测仪器"); - record( - "EI-API-EQUIP-04", - "装备-仪器类型搜索", - equipByInstrument.success === true && (equipByInstrument.total ?? equipByInstrument.totalCount ?? 0) >= 1 && (equipByInstrument.total ?? equipByInstrument.totalCount ?? 0) < 6, - `total=${equipByInstrument.total ?? equipByInstrument.totalCount ?? "?"}`, - ); - const equipByDeviceType = await request("GET", "/org-equipment/page?current=1&size=10&deviceType=便携式"); - record( - "EI-API-EQUIP-05", - "装备-设备类型搜索", - equipByDeviceType.success === true && (equipByDeviceType.total ?? equipByDeviceType.totalCount ?? 0) >= 1, - `total=${equipByDeviceType.total ?? equipByDeviceType.totalCount ?? "?"}`, - ); - - const personnelSeed = await request("GET", "/org-personnel/page?current=1&size=10&userName=钱离职"); - const seedRow = personnelSeed.data?.[0]; - if (seedRow) { - record("EI-API-CHANGE-04", "人员-变更次数组装", (seedRow.changeCount ?? 0) >= 3, `changeCount=${seedRow.changeCount ?? 0}`); - record("EI-API-CHANGE-05", "人员-就职状态组装", seedRow.employmentStatusCode === 2, `employmentStatusCode=${seedRow.employmentStatusCode}`); - } - const personnelResign = await request("GET", "/org-personnel/page?current=1&size=10&userName=钱离职"); - const resignRow = personnelResign.data?.[0]; - if (resignRow) { - record("EI-API-CHANGE-06", "人员-离职审核状态", resignRow.resignAuditStatus === 0, `resignAuditStatus=${resignRow.resignAuditStatus ?? "(空)"}`); - } - const resignFilter = await request("GET", "/org-personnel/page?current=1&size=10&resignAuditStatus=0&userName=钱离职"); - record( - "EI-API-CHANGE-07", - "人员-离职审核状态筛选", - resignFilter.success === true && (resignFilter.data?.length ?? 0) >= 1, - `count=${resignFilter.data?.length ?? 0}`, - ); - - const qualSave = await request("POST", "/org-qualification/save", { - licenseTypeName: "测试证照", - certName: `联调资质-${TS}`, - certNo: `QUAL-${TS}`, - issueOrg: "测试机关", - issueDate: "2026-01-01", - validStartDate: "2026-01-01", - validEndDate: "2027-01-01", - enableFlag: 1, - }); - if (assertSuccess("EI-API-QUAL-01", "资质-新增", qualSave)) { - createdIds.qual = normalizeId(qualSave.data?.id); - } - await testPagination("EI-API-QUAL-PG", "资质", "/org-qualification/page", 1); - - if (createdIds.staff) { - const resignSave = await request("POST", "/org-resign-apply/save", { - personnelId: createdIds.staff, - applicantName: `联调离职-${TS}`, - applyTime: "2026-06-20T10:00:00", - expectedResignDate: "2026-07-31", - resignReason: "联调测试", - auditStatusCode: 0, - auditStatusName: "未审核", - }); - if (assertSuccess("EI-API-RESIGN-01", "离职申请-新增", resignSave)) { - createdIds.resign = normalizeId(resignSave.data?.id); - const resignGet = await request("GET", `/org-resign-apply/get?id=${createdIds.resign}`); - if (assertHasData("EI-API-RESIGN-03", "离职申请-详情", resignGet)) { - record( - "EI-API-RESIGN-04", - "离职-审核状态字段", - resignGet.data.auditStatusCode === 0, - `auditStatusCode=${resignGet.data.auditStatusCode}`, - ); - record( - "EI-API-RESIGN-05", - "离职-用户名字段组装", - !!resignGet.data.account, - `account=${resignGet.data.account ?? "(空)"}`, - ); - } - const audit = await request("POST", "/org-resign-apply/modify", { - id: createdIds.resign, - personnelId: createdIds.staff, - auditStatusCode: 1, - auditStatusName: "已审核", - }); - assertSuccess("EI-API-RESIGN-02", "离职申请-审核", audit); - } - } - else { - record("EI-API-RESIGN-01", "离职申请-新增", false, "跳过:无人员 id"); - } - await testPagination("EI-API-RESIGN-PG", "离职申请", "/org-resign-apply/page", 0); - - const cleanup = [ - ["cert", "/org-personnel-cert/delete"], - ["resign", "/org-resign-apply/delete"], - ["staff", "/org-personnel/delete"], - ["pos", "/org-position/delete"], - ["dept", "/org-department/delete"], - ["equip", "/org-equipment/delete"], - ["qual", "/org-qualification/delete"], - ]; - for (const [key, path] of cleanup) { - if (createdIds[key]) { - const del = await request("POST", `${path}?id=${createdIds[key]}`); - assertSuccess(`EI-API-CLEAN-${key}`, `清理-${key}`, del); - } - } - - console.log("\n========== 测试结果 ==========\n"); - results.forEach((r) => { - const icon = r.status === "PASS" ? "✓" : "✗"; - console.log(`${icon} [${r.status}] ${r.id} ${r.name}${r.detail ? ` — ${r.detail}` : ""}`); - }); - console.log(`\n合计: ${passed + failed} 通过: ${passed} 失败: ${failed}\n`); - process.exit(failed > 0 ? 1 : 0); -} - -run().catch((err) => { - console.error("测试执行失败:", err.message); - process.exit(1); -}); diff --git a/docs/test-reports/测试用例/test-enterprise-info-ui-check.mjs b/docs/test-reports/测试用例/test-enterprise-info-ui-check.mjs deleted file mode 100644 index 6665347..0000000 --- a/docs/test-reports/测试用例/test-enterprise-info-ui-check.mjs +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 企业信息管理 - 页面 UI / 字段展示 自动化检查 - * 运行: node scripts/test-enterprise-info-ui-check.mjs - */ -import fs from "node:fs"; -import path from "node:path"; - -const ROOT = path.resolve(import.meta.dirname, ".."); -const PAGE_DIR = path.join(ROOT, "src/pages/Container/EnterpriseInfo"); -const UTILS = path.join(ROOT, "src/utils"); - -const checks = []; - -function pass(id, name, ok, detail = "") { - checks.push({ id, name, status: ok ? "PASS" : "FAIL", detail }); -} - -function read(rel) { - return fs.readFileSync(path.join(ROOT, rel), "utf8"); -} - -function fileExists(rel) { - return fs.existsSync(path.join(ROOT, rel)); -} - -// ── 公共能力 ── -pass("UI-COMMON-01", "mockUpload 默认地址", fileExists("src/utils/mockUpload.js")); -pass("UI-COMMON-02", "enterpriseForm 下拉适配", fileExists("src/utils/enterpriseForm.js")); -pass("UI-COMMON-03", "DEFAULT_UPLOAD_FILE_URL 配置", - read("src/utils/mockUpload.js").includes("gd-hbimg.huaban.com")); - -// ── 各页面 ── -const pages = [ - ["OrgInfo", "机构信息"], - ["DepartmentPosition", "部门岗位"], - ["PersonnelInfo/List", "人员信息"], - ["PersonnelChange", "人员变更"], - ["ResignationApply", "离职申请"], - ["QualificationCert", "资质证书"], - ["EquipInfo", "装备信息"], -]; - -pages.forEach(([rel, label]) => { - const content = read(`src/pages/Container/EnterpriseInfo/${rel}/index.js`); - pass(`UI-PAGE-${rel}-01`, `${label}-页面存在`, content.length > 100); - pass(`UI-PAGE-${rel}-02`, `${label}-使用 PageHeader`, content.includes("PageHeader")); - const usesVerticalForm = content.includes('layout="vertical"'); - const usesBrokenLabelCol = /labelCol=\{\{\s*span:\s*24/.test(content); - const usesFormBuilderModal = /Modal[\s\S]*FormBuilder/.test(content); - pass(`UI-PAGE-${rel}-03`, `${label}-弹窗表单布局`, usesVerticalForm || !usesFormBuilderModal || !usesBrokenLabelCol, - usesFormBuilderModal && !usesVerticalForm && usesBrokenLabelCol ? "Modal 内 FormBuilder labelCol=24 导致输入框不可见" : ""); -}); - -// ── 人员:部门岗位下拉 ── -const staffPage = read("src/pages/Container/EnterpriseInfo/PersonnelInfo/List/index.js"); -pass("UI-STAFF-01", "人员-部门 Select", staffPage.includes("deptId") && staffPage.includes("Select")); -pass("UI-STAFF-02", "人员-岗位 Select", staffPage.includes("positionId") && staffPage.includes("Select")); -pass("UI-STAFF-03", "人员-列表部门列", staffPage.includes('dataIndex: "deptName"')); -pass("UI-STAFF-04", "人员-重置密码", staffPage.includes("onResetPassword")); - -// ── 离职 ── -const resignPage = read("src/pages/Container/EnterpriseInfo/ResignationApply/index.js"); -pass("UI-RESIGN-01", "离职-审核状态列", resignPage.includes("auditStatus")); -pass("UI-RESIGN-02", "离职-用户名列", resignPage.includes('dataIndex: "account"')); -pass("UI-RESIGN-03", "离职-默认上传", resignPage.includes("resolveUploadFileId") || resignPage.includes("mockUpload")); - -// ── 变更次数样式 ── -const changePage = read("src/pages/Container/EnterpriseInfo/PersonnelChange/index.js"); -pass("UI-CHANGE-01", "变更-次数样式", changePage.includes("CHANGE_COUNT_STYLE")); -pass("UI-CHANGE-02", "变更-从 record 渲染", changePage.includes("getChangeCount(record)")); -pass("UI-CHANGE-03", "变更-就职/审核状态", changePage.includes("getEmploymentStatusLabel") && changePage.includes("getResignAuditStatusLabel")); - -pass("UI-RESIGN-04", "离职-从 record 渲染审核状态", resignPage.includes("getResignAuditStatusLabel(record)")); -pass("UI-RESIGN-05", "离职-纯文本状态", !resignPage.includes(" { - const icon = c.status === "PASS" ? "✓" : "✗"; - if (c.status === "PASS") passed += 1; - else failed += 1; - console.log(`${icon} [${c.status}] ${c.id} ${c.name}${c.detail ? ` — ${c.detail}` : ""}`); -}); -console.log(`\n合计: ${checks.length} 通过: ${passed} 失败: ${failed}\n`); -process.exit(failed > 0 ? 1 : 0); diff --git a/docs/test-reports/测试用例/test-qual-filing-api.mjs b/docs/test-reports/测试用例/test-qual-filing-api.mjs deleted file mode 100644 index 682eb61..0000000 --- a/docs/test-reports/测试用例/test-qual-filing-api.mjs +++ /dev/null @@ -1,278 +0,0 @@ -/** - * 资质申请管理 — 前后端联调冒烟测试 - * - * 运行: - * API_BASE=http://127.0.0.1:8095/safetyEval ORG_INFO_ID=1 node docs/test-reports/测试用例/test-qual-filing-api.mjs - * - * 可选: - * FRONTEND_BASE=http://127.0.0.1:8081 (检测前端页面是否可访问) - */ -const API_BASE = process.env.API_BASE || "http://127.0.0.1:8095/safetyEval"; -const ORG_INFO_ID = process.env.ORG_INFO_ID || "1"; -const FRONTEND_BASE = process.env.FRONTEND_BASE || "http://127.0.0.1:8081"; -const DEFAULT_URL = - "https://s1.aigei.com/prevfiles/6f2754563be2491bad7c957904cd7d2a.jpg?e=2051020800&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:CR60zg7VKDp-jUWyYls_rdvyQMM="; - -const results = []; -let passed = 0; -let failed = 0; - -function assert(name, condition, detail = "") { - if (condition) { - passed += 1; - results.push({ name, status: "PASS", detail }); - console.log(` ✓ ${name}${detail ? ` — ${detail}` : ""}`); - } - else { - failed += 1; - results.push({ name, status: "FAIL", detail }); - console.log(` ✗ ${name}${detail ? ` — ${detail}` : ""}`); - } -} - -async function request(method, path, body) { - const url = `${API_BASE}${path}`; - const options = { - method, - headers: { - "Content-Type": "application/json", - orgInfoId: ORG_INFO_ID, - }, - }; - if (body !== undefined) { - options.body = JSON.stringify(body); - } - const res = await fetch(url, options); - 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 request("GET", `/qual-filing/detail?id=${filingId}`); - const materials = detail.data?.materials || []; - for (const material of materials) { - const uploaded = await request("POST", "/qual-filing-material/upload", { - id: material.id, - attachmentUrl: DEFAULT_URL, - }); - if (uploaded.success !== true) { - return { ok: false, message: uploaded.message, materialId: material.id }; - } - } - return { ok: true, count: materials.length }; -} - -async function saveCommitment(filingId) { - return request("POST", "/qual-filing-commitment/save-or-update", { - filingId, - signDate: "2026-06-30", - legalRepSignatureUrl: DEFAULT_URL, - commitmentContent: "联调测试承诺书", - }); -} - -async function runBackendTests() { - console.log("\n=== 后端 API(资质申请管理)==="); - console.log(`API_BASE=${API_BASE} orgInfoId=${ORG_INFO_ID}\n`); - - // 1. 创建草稿 + 材料模板 - const draft = await request("POST", "/qual-filing/draft?applyTypeCode=1"); - assert("备案申请-创建草稿", draft.success === true && draft.data?.id, draft.message || `id=${draft.data?.id}`); - const filingId = draft.data?.id; - if (!filingId) { - return null; - } - - const initTpl = await request("POST", `/qual-filing-material/init-template?filingId=${filingId}`); - assert("备案申请-初始化材料模板", initTpl.success === true, initTpl.message); - - const detail1 = await request("GET", `/qual-filing/detail?id=${filingId}`); - assert("备案申请-详情含10项材料", detail1.data?.materials?.length === 10, `count=${detail1.data?.materials?.length}`); - - // 2. 暂存基本信息 - const saved = await request("POST", "/qual-filing/save-draft", { - id: filingId, - filingUnitName: "脚本联调评价机构", - filingTerritoryName: "渝北区", - filingTerritoryCode: "渝北区", - businessScope: "化工", - }); - assert("备案申请-暂存基本信息", saved.success === true, saved.message); - - // 3. 备案申请分页(applyTypeCode=1) - const appPage = await request("GET", `/qual-filing/page?current=1&size=20&applyTypeCode=1`); - const appTypes = new Set((appPage.data || []).map((row) => row.applyTypeCode)); - assert( - "备案申请-分页仅 applyType=1", - appPage.success === true && appTypes.size <= 1 && (appTypes.size === 0 || appTypes.has(1)), - `types=${[...appTypes].join(",")} total=${appPage.total}`, - ); - - // 4. 上传材料 + 承诺书 + 提交 - const uploadRes = await uploadAllMaterials(filingId); - assert("备案申请-上传全部材料", uploadRes.ok === true, uploadRes.message || `count=${uploadRes.count}`); - - const commitment = await saveCommitment(filingId); - assert("备案申请-保存承诺书", commitment.success === true, commitment.message); - - const submit = await request("POST", `/qual-filing/submit?id=${filingId}`); - assert( - "备案申请-提交审核", - submit.success === true && submit.data?.filingStatusCode === 2, - submit.message || submit.data?.filingStatusName, - ); - - // 5. 审核通过 → 生成已备案记录 - const approve = await request("POST", `/qual-filing/approve?id=${filingId}`); - assert( - "备案申请-审核通过", - approve.success === true && approve.data?.filingStatusCode === 1 && approve.data?.filingStatusName === "已备案", - approve.message || approve.data?.filingStatusName, - ); - - const filedPage = await request("GET", "/qual-filing/filed/page?current=1&size=20"); - const filedTypes = new Set((filedPage.data || []).map((row) => row.applyTypeCode)); - const filedStatuses = new Set((filedPage.data || []).map((row) => row.filingStatusCode)); - assert( - "已备案-分页仅 applyType=3", - filedPage.success === true && [...filedTypes].every((t) => t === 3), - `types=${[...filedTypes].join(",")} count=${filedPage.data?.length}`, - ); - assert( - "已备案-默认不含暂存", - ![...filedStatuses].includes(5), - `statuses=${[...filedStatuses].join(",")}`, - ); - - const originFilingId = filedPage.data?.[0]?.id; - assert("已备案-存在记录", !!originFilingId, `originId=${originFilingId}`); - - // 6. 已备案填报草稿 - const filedDraft = await request("POST", "/qual-filing/filed/draft"); - assert("已备案-创建填报草稿", filedDraft.success === true && filedDraft.data?.id, filedDraft.message); - assert( - "已备案-填报草稿为暂存", - filedDraft.data?.filingStatusCode === 5, - filedDraft.data?.filingStatusName, - ); - - const changePage = await request("GET", "/qual-filing/change/page?current=1&size=20"); - const changeStatuses = new Set((changePage.data || []).map((row) => row.filingStatusCode)); - assert( - "备案变更-列表仅已备案/变更审核中", - changePage.success === true && [...changeStatuses].every((s) => s === 1 || s === 4), - `statuses=${[...changeStatuses].join(",")}`, - ); - - if (!originFilingId) { - return filingId; - } - - // 7. 备案变更 - const changeStart = await request("POST", `/qual-filing-change/start?originFilingId=${originFilingId}`); - const draftFilingId = changeStart.data; - assert("备案变更-发起变更", changeStart.success === true && draftFilingId, `draftId=${draftFilingId}`); - - if (draftFilingId) { - await request("POST", "/qual-filing/save-draft", { - id: draftFilingId, - businessScope: "化工, 矿山", - }); - await uploadAllMaterials(draftFilingId); - await saveCommitment(draftFilingId); - - const changeSubmit = await request("POST", "/qual-filing-change/submit", { draftFilingId }); - assert("备案变更-提交", changeSubmit.success === true, changeSubmit.message); - - const history = await request("GET", `/qual-filing-change/history?originFilingId=${originFilingId}`); - assert( - "备案变更-历史明细", - history.success === true && (history.data?.changeCount ?? 0) >= 1, - `changeCount=${history.data?.changeCount} records=${history.data?.records?.length}`, - ); - } - - // 8. 人员/装备批量引入(有数据则测) - const personnelPage = await request("GET", "/org-personnel/page?current=1&size=1"); - const personnelId = personnelPage.data?.[0]?.id; - if (personnelId) { - const newDraft = await request("POST", "/qual-filing/draft?applyTypeCode=1"); - const pid = newDraft.data?.id; - const batchP = await request("POST", "/qual-filing-personnel/batch-add-from-org", { - filingId: pid, - sourcePersonnelIds: [personnelId], - }); - assert("备案人员-批量引入", batchP.success === true, batchP.message); - } - else { - assert("备案人员-批量引入", true, "跳过:无企业人员数据"); - } - - const equipPage = await request("GET", "/org-equipment/page?current=1&size=1"); - const equipId = equipPage.data?.[0]?.id; - if (equipId) { - const newDraft2 = await request("POST", "/qual-filing/draft?applyTypeCode=1"); - const eid = newDraft2.data?.id; - const batchE = await request("POST", "/qual-filing-equipment/batch-add-from-org", { - filingId: eid, - sourceEquipmentIds: [equipId], - }); - assert("备案装备-批量引入", batchE.success === true, batchE.message); - } - else { - assert("备案装备-批量引入", true, "跳过:无企业装备数据"); - } - - return filingId; -} - -async function runFrontendSmokeTests() { - console.log("\n=== 前端页面可访问性 ==="); - console.log(`FRONTEND_BASE=${FRONTEND_BASE}\n`); - - const pages = [ - ["/certificate/container/qualApplication/filingApplication/list", "资质备案申请列表"], - ["/certificate/container/qualApplication/filedManage/list", "已备案资质管理列表"], - ["/certificate/container/qualApplication/filingChange/list", "备案变更管理列表"], - ]; - - for (const [path, label] of pages) { - try { - const res = await fetch(`${FRONTEND_BASE}${path}`, { method: "GET" }); - assert(`前端-${label}`, res.status === 200, `HTTP ${res.status}`); - } - catch (err) { - assert(`前端-${label}`, false, err.message); - } - } -} - -async function main() { - console.log("资质申请管理 — 联调冒烟测试"); - try { - await runBackendTests(); - } - catch (err) { - failed += 1; - console.error("\n后端测试异常:", err); - } - - try { - await runFrontendSmokeTests(); - } - catch (err) { - console.warn("\n前端页面检测跳过:", err.message); - } - - console.log(`\n=== 结果: ${passed} 通过, ${failed} 失败 ===\n`); - if (failed > 0) { - process.exit(1); - } -} - -main(); diff --git a/docs/test-reports/测试用例/test-qual-filing-audit-flow.mjs b/docs/test-reports/测试用例/test-qual-filing-audit-flow.mjs deleted file mode 100644 index 0be789d..0000000 --- a/docs/test-reports/测试用例/test-qual-filing-audit-flow.mjs +++ /dev/null @@ -1,199 +0,0 @@ -/** - * 资质申请管理 — 三模块审核接口 + 整体流程联调 - * - * 运行: - * API_BASE=http://127.0.0.1:8095/safetyEval ORG_INFO_ID=1 node docs/test-reports/测试用例/test-qual-filing-audit-flow.mjs - */ -const API_BASE = process.env.API_BASE || "http://127.0.0.1:8095/safetyEval"; -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="; - -let passed = 0; -let failed = 0; - -function ok(name, cond, detail = "") { - if (cond) { - passed += 1; - console.log(` ✓ ${name}${detail ? ` — ${detail}` : ""}`); - } - else { - failed += 1; - console.log(` ✗ ${name}${detail ? ` — ${detail}` : ""}`); - } -} - -async function api(method, path, body) { - const res = await fetch(`${API_BASE}${path}`, { - 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 }); - } - } -} - -async function saveCommitment(filingId) { - return api("POST", "/qual-filing-commitment/save-or-update", { - filingId, - signDate: "2026-06-30", - legalRepSignatureUrl: DEFAULT_URL, - commitmentContent: "审核流程联调测试承诺书", - }); -} - -async function persistMinimal(filingId) { - await api("POST", "/qual-filing/save-draft", { - id: filingId, - filingUnitName: "审核流程测试机构", - filingTerritoryName: "渝北区", - businessScope: "化工", - }); - await api("POST", `/qual-filing-material/init-template?filingId=${filingId}`); - await uploadAllMaterials(filingId); - await saveCommitment(filingId); -} - -async function testModule1ApplicationAudit() { - console.log("\n=== 模块1:资质备案申请(提交 + 审核) ==="); - - const draft = await api("POST", "/qual-filing/draft?applyTypeCode=1"); - const filingId = draft.data?.id; - ok("1.创建申请草稿", draft.success && filingId, `id=${filingId}`); - if (!filingId) { - return null; - } - - await persistMinimal(filingId); - - const submit = await api("POST", `/qual-filing/submit?id=${filingId}`); - ok("2.提交审核 POST /qual-filing/submit", submit.success && submit.data?.filingStatusCode === 2, submit.data?.filingStatusName); - - const approve = await api("POST", `/qual-filing/approve?id=${filingId}`); - ok( - "3.审核通过 POST /qual-filing/approve", - approve.success && approve.data?.filingStatusCode === 1, - approve.data?.filingStatusName, - ); - - const filedPage = await api("GET", "/qual-filing/filed/page?current=1&size=5"); - const master = (filedPage.data || []).find((r) => r.filingStatusCode === 1); - ok("4.审核后生成已备案主记录", !!master, master ? `masterId=${master.id}` : "filed/page 无已备案"); - - return { applicationId: filingId, masterId: master?.id }; -} - -async function testModule2FiledManageAudit(masterId) { - console.log("\n=== 模块2:已备案资质管理(填报提交 + 审核) ==="); - if (!masterId) { - ok("跳过-无已备案主记录", false); - return masterId; - } - - const filedDraft = await api("POST", "/qual-filing/filed/draft"); - const draftId = filedDraft.data?.id; - ok("1.创建填报草稿 POST /qual-filing/filed/draft", filedDraft.success && draftId, `id=${draftId}`); - if (!draftId) { - return masterId; - } - - await persistMinimal(draftId); - - const submit = await api("POST", `/qual-filing/submit?id=${draftId}`); - ok("2.提交填报审核 POST /qual-filing/submit", submit.success && submit.data?.filingStatusCode === 2, submit.data?.filingStatusName); - - const approve = await api("POST", `/qual-filing/approve?id=${draftId}`); - ok( - "3.填报审核通过 POST /qual-filing/approve", - approve.success && approve.data?.filingStatusCode === 1, - approve.data?.filingStatusName, - ); - - return masterId; -} - -async function testModule3ChangeAudit(originId) { - console.log("\n=== 模块3:备案变更管理(变更提交 + 审核) ==="); - if (!originId) { - ok("跳过-无变更源记录", false); - return; - } - - const originBefore = await api("GET", `/qual-filing/get?id=${originId}`); - ok("1.变更源为已备案", originBefore.data?.filingStatusCode === 1, originBefore.data?.filingStatusName); - - const start = await api("POST", `/qual-filing-change/start?originFilingId=${originId}`); - const changeDraftId = start.data; - ok("2.发起变更 POST /qual-filing-change/start", start.success && changeDraftId, `draftId=${changeDraftId}`); - if (!changeDraftId) { - return; - } - - await api("POST", "/qual-filing/save-draft", { - id: changeDraftId, - businessScope: "化工, 矿山采选业", - unitIntro: "变更后业务范围说明", - }); - await uploadAllMaterials(changeDraftId); - await saveCommitment(changeDraftId); - - const changeSubmit = await api("POST", "/qual-filing-change/submit", { draftFilingId: changeDraftId }); - ok("3.提交变更审核 POST /qual-filing-change/submit", changeSubmit.success === true, changeSubmit.message); - - const changePage = await api( - "GET", - `/qual-filing-change/page?current=1&size=5&originFilingId=${originId}&filingStatusCode=2`, - ); - const pendingChange = (changePage.data || [])[0]; - const changeId = pendingChange?.id; - ok("4.查询待审变更单 GET /qual-filing-change/page", !!changeId, `changeId=${changeId}`); - - if (!changeId) { - return; - } - - const changeApprove = await api("POST", `/qual-filing-change/approve?changeId=${changeId}`); - ok("5.变更审核通过 POST /qual-filing-change/approve", changeApprove.success === true, changeApprove.message); - - const originAfter = await api("GET", `/qual-filing/get?id=${originId}`); - ok( - "6.变更通过后主记录恢复已备案", - originAfter.data?.filingStatusCode === 1, - originAfter.data?.filingStatusName, - ); - ok( - "7.变更内容已生效", - (originAfter.data?.businessScope || "").includes("矿山"), - originAfter.data?.businessScope, - ); -} - -async function main() { - console.log("资质申请管理 — 三模块审核接口整体流程测试"); - console.log(`API_BASE=${API_BASE} orgInfoId=${ORG_INFO_ID}`); - - const ctx = await testModule1ApplicationAudit(); - const masterId = await testModule2FiledManageAudit(ctx?.masterId); - await testModule3ChangeAudit(masterId); - - console.log(`\n=== 结果: ${passed} 通过, ${failed} 失败 ===`); - if (failed > 0) { - process.exitCode = 1; - } -} - -await main(); diff --git a/docs/test-reports/测试用例/test-qual-filing-full-e2e.mjs b/docs/test-reports/测试用例/test-qual-filing-full-e2e.mjs deleted file mode 100644 index 8b90bd6..0000000 --- a/docs/test-reports/测试用例/test-qual-filing-full-e2e.mjs +++ /dev/null @@ -1,410 +0,0 @@ -/** - * 资质申请管理 — 三模块完整流程联调(模拟前端 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/safetyEval"; -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; diff --git a/docs/test-reports/测试用例/test-qual-filing-interaction.mjs b/docs/test-reports/测试用例/test-qual-filing-interaction.mjs deleted file mode 100644 index 1c23be3..0000000 --- a/docs/test-reports/测试用例/test-qual-filing-interaction.mjs +++ /dev/null @@ -1,249 +0,0 @@ -/** - * 资质申请管理 — 页面交互逻辑 + 后端联调全链路测试 - * - * 覆盖本次改造: - * - 新建申请不预调 draft(列表 API 计数不变) - * - 暂存后入库并可回显 - * - 材料前端模板 + 暂存时 init-template - * - 提交前核验(全通过/有失败) - * - 前端路由 bundle 检测 - * - * 运行: - * node docs/test-reports/测试用例/test-qual-filing-interaction.mjs - */ -import fs from "node:fs"; -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/safetyEval"; -const ORG_INFO_ID = process.env.ORG_INFO_ID || "1"; -const FRONTEND_BASE = process.env.FRONTEND_BASE || "http://127.0.0.1:8081"; -const DEFAULT_URL = - "https://s1.aigei.com/prevfiles/6f2754563be2491bad7c957904cd7d2a.jpg?e=2051020800&token=P7S2Xpzfz11vAkASLTkfHN7Fw-oOZBecqeJaxypL:CR60zg7VKDp-jUWyYls_rdvyQMM="; - -let passed = 0; -let failed = 0; - -function assert(name, condition, detail = "") { - if (condition) { - passed += 1; - console.log(` ✓ ${name}${detail ? ` — ${detail}` : ""}`); - } - else { - failed += 1; - console.log(` ✗ ${name}${detail ? ` — ${detail}` : ""}`); - } -} - -async function api(method, path, body) { - const res = await fetch(`${API_BASE}${path}`, { - 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 }; - } -} - -function readSrc(rel) { - return fs.readFileSync(path.join(ROOT, rel), "utf8"); -} - -// ── 静态:新建不预调后端 ── -function testStaticNoPreCreate() { - console.log("\n=== 静态检查:新建不预调后端 ==="); - const listPage = readSrc("src/pages/Container/QualApplication/FilingApplication/List/index.js"); - assert("列表-申请备案不调 qualFilingDraft", !listPage.includes("qualFilingDraft")); - assert("列表-申请备案不调 init-template", !listPage.includes("qualFilingMaterialInitTemplate")); - assert("列表-直接跳转表单", listPage.includes("goFilingForm({ mode: FILING_FORM_MODE.APPLICATION })")); - - const formPage = readSrc("src/pages/Container/QualApplication/FilingForm/index.js"); - assert("表单-存在 persistFilingToBackend", formPage.includes("persistFilingToBackend")); - assert("表单-存在核验弹窗", formPage.includes("PrerequisiteVerifyModal")); - assert("表单-存在 session 本地草稿", formPage.includes("saveLocalDraft")); - assert("表单-材料本地更新", formPage.includes("uploadStatusName: \"已上传\"")); - - const basicStep = readSrc("src/pages/Container/QualApplication/FilingForm/components/BasicInfoStep.jsx"); - assert("基本信息-证照类型选项", basicStep.includes("QUALIFICATION_INDUSTRY_OPTIONS")); - - const commitment = readSrc("src/pages/Container/QualApplication/FilingForm/components/CommitmentStep.jsx"); - assert("承诺书-法定代表人下拉", commitment.includes("legalRepPersonnelId")); - assert("承诺书-动态填充文本", commitment.includes("COMMITMENT_PARAGRAPHS")); - - assert("材料模板-10项", readSrc("src/pages/Container/QualApplication/filingMaterialTemplate.js").includes("sortOrder: 10")); -} - -// ── 核验逻辑 ── -async function testVerifyLogic() { - console.log("\n=== 核验逻辑 ==="); - const verifyMod = await import(path.join(ROOT, "src/pages/Container/QualApplication/filingVerify.js")); - const { verifyFilingPrerequisites } = verifyMod; - - const failDetail = { - fixedAssetAmount: 500, - workplaceArea: 500, - personnelList: [], - equipmentList: [], - materials: [{ sortOrder: 10, attachmentUrl: null }], - }; - const failResult = verifyFilingPrerequisites(failDetail); - assert("核验-不完整时应失败", failResult.passed === false, `items=${failResult.items.length}`); - - const passDetail = { - fixedAssetAmount: 1200, - workplaceArea: 1200, - equipmentList: [{ id: 1 }], - materials: [{ sortOrder: 10, attachmentUrl: DEFAULT_URL }], - personnelList: Array.from({ length: 25 }, (_, i) => ({ - personName: `P${i}`, - registerEngineerFlag: i < 8 ? 1 : 0, - titleName: i < 8 ? "高级" : "中级", - positionName: i === 0 ? "负责人" : i === 1 ? "技术负责人" : i === 2 ? "质量负责人" : "工程师", - workExperience: "在本行业工作五年", - })), - }; - const passResult = verifyFilingPrerequisites(passDetail); - assert("核验-完整数据应通过", passResult.passed === true); -} - -// ── 模拟前端 persist 全链路 ── -async function testPersistFlow() { - console.log("\n=== 交互链路:暂存入库 + 回显 ==="); - - const beforePage = await api("GET", "/qual-filing/page?current=1&size=1&applyTypeCode=1"); - const beforeTotal = beforePage.total ?? 0; - - // 模拟用户填写后点暂存(等同 persistFilingToBackend) - const draft = await api("POST", "/qual-filing/draft?applyTypeCode=1"); - assert("暂存-创建草稿", draft.success === true && draft.data?.id, `id=${draft.data?.id}`); - const filingId = draft.data?.id; - if (!filingId) { - return; - } - - const saveBasic = await api("POST", "/qual-filing/save-draft", { - id: filingId, - businessScope: "石油加工业,化学原料、化学品及医药制造业", - filingTerritoryName: "渝北区", - filingTerritoryCode: "渝北区", - filingUnitName: "交互测试评价机构", - fixedAssetAmount: 1200, - workplaceArea: 1500, - }); - assert("暂存-基本信息", saveBasic.success === true, saveBasic.message); - - await api("POST", `/qual-filing-material/init-template?filingId=${filingId}`); - const detailAfterInit = await api("GET", `/qual-filing/detail?id=${filingId}`); - assert("暂存-材料模板10项", detailAfterInit.data?.materials?.length === 10); - - for (const material of detailAfterInit.data.materials || []) { - await api("POST", "/qual-filing-material/upload", { - id: material.id, - attachmentUrl: DEFAULT_URL, - }); - } - - 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=5"); - 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.slice(0, 2), - }); - } - - const equipPage = await api("GET", "/org-equipment/page?current=1&size=5"); - 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.slice(0, 1), - }); - } - - const reloaded = await api("GET", `/qual-filing/detail?id=${filingId}`); - assert("回显-业务范围", reloaded.data?.businessScope?.includes("化学"), reloaded.data?.businessScope); - assert("回显-材料已上传", (reloaded.data?.materials || []).every((m) => m.uploadStatusCode === 2)); - assert("回显-承诺书", !!reloaded.data?.commitment?.commitmentContent); - assert("回显-人员", (reloaded.data?.personnelList || []).length >= 1 || personnelIds.length === 0); - assert("回显-装备", (reloaded.data?.equipmentList || []).length >= 1 || equipIds.length === 0); - - const afterPage = await api("GET", "/qual-filing/page?current=1&size=20&applyTypeCode=1"); - assert("列表-暂存后总数增加", (afterPage.total ?? 0) >= beforeTotal + 1, `before=${beforeTotal} after=${afterPage.total}`); - - // 提交应进入审核中 - const submit = await api("POST", `/qual-filing/submit?id=${filingId}`); - assert("提交-进入审核中", submit.success === true && submit.data?.filingStatusCode === 2, submit.data?.filingStatusName); -} - -// ── 前端 bundle / 路由 ── -async function testFrontendBundle() { - console.log("\n=== 前端页面 bundle 检测 ==="); - const pages = [ - ["/certificate/container/qualApplication/filingApplication/list", "资质备案申请列表"], - ["/certificate/container/qualApplication/filedManage/list", "已备案资质管理"], - ["/certificate/container/qualApplication/filingChange/list", "备案变更管理"], - ["/certificate/container/qualApplication/filingForm?mode=application", "备案表单(无id)"], - ["/certificate/container/qualApplication/filingForm?mode=application&id=1", "备案表单(有id)"], - ]; - - for (const [route, label] of pages) { - const res = await fetch(`${FRONTEND_BASE}${route}`); - assert(`HTTP-${label}`, res.status === 200, String(res.status)); - } - - const html = await (await fetch(`${FRONTEND_BASE}/certificate/`)).text(); - const jsMatch = html.match(/src="(\/certificate\/static\/js\/main\.[^"]+\.js)"/); - assert("前端-main.js 可解析", !!jsMatch); - if (jsMatch) { - const js = await (await fetch(`${FRONTEND_BASE}${jsMatch[1]}`)).text(); - const markers = [ - "FilingApplicationListPage", - "qualApplication/filingApplication/list", - "PrerequisiteVerifyModal", - "QUALIFICATION_INDUSTRY_OPTIONS", - "persistFilingToBackend", - "verifyFilingPrerequisites", - "legalRepPersonnelId", - ]; - for (const marker of markers) { - assert(`Bundle-${marker}`, js.includes(marker)); - } - } -} - -async function main() { - console.log("资质申请管理 — 页面交互 + 后端全链路测试"); - console.log(`API_BASE=${API_BASE} FRONTEND=${FRONTEND_BASE} orgInfoId=${ORG_INFO_ID}`); - - testStaticNoPreCreate(); - await testVerifyLogic(); - await testPersistFlow(); - await testFrontendBundle(); - - console.log(`\n=== 结果: ${passed} 通过, ${failed} 失败 ===\n`); - if (failed > 0) { - process.exit(1); - } -} - -main().catch((err) => { - console.error(err); - process.exit(1); -}); diff --git a/src/pages/Container/EnterpriseInfo/EquipInfo/index.js b/src/pages/Container/EnterpriseInfo/EquipInfo/index.js index 5423c37..532aed7 100644 --- a/src/pages/Container/EnterpriseInfo/EquipInfo/index.js +++ b/src/pages/Container/EnterpriseInfo/EquipInfo/index.js @@ -118,19 +118,29 @@ function EquipInfoPage(props) { allowClear /> , - - + + style={{ width: "100%" }} + > + 分析仪器 + 检测仪器 + 采样设备 + 物理仪器 + , - + style={{ width: "100%" }} + > + 固定式 + 便携式 + , - + @@ -380,7 +395,10 @@ function EquipFormModal({ label="设备类型" rules={[{ required: true, message: "请选择设备类型" }]} > - + @@ -439,8 +457,12 @@ function EquipFormModal({ - - + + @@ -503,7 +525,7 @@ function EquipViewModal({ open, currentId, equipInfoGet, onCancel }) { {info.deviceModel} - {info.instrumentType} + {info.instrumentTypeName} {info.deviceType} @@ -521,7 +543,7 @@ function EquipViewModal({ open, currentId, equipInfoGet, onCancel }) { {info.calibrationInitValue} - {info.fieldCalibrationType} + {info.fieldCalibrationTypeName} {DUAL_CHANNEL_MAP[info.dualChannelFlag] ?? "-"} diff --git a/src/pages/Container/EnterpriseInfo/index.js b/src/pages/Container/EnterpriseInfo/index.js index e113348..a5b7e60 100644 --- a/src/pages/Container/EnterpriseInfo/index.js +++ b/src/pages/Container/EnterpriseInfo/index.js @@ -5,21 +5,21 @@ import { Skeleton } from "antd"; /** 资质申请管理模块入口:需先有机构信息 */ function EnterpriseInfo(props) { - const [orgInfoId, setOrgInfoId] = useState( sessionStorage.getItem("orgInfoId"), ); useEffect(() => { if (!orgInfoId) { props.orgInfoGet().then((res) => { - setOrgInfoId(res?.data?.id); - sessionStorage.setItem("orgInfoId", res?.data?.id); + if (res?.data?.id) { + setOrgInfoId(res?.data?.id); + sessionStorage.setItem("orgInfoId", res?.data?.id); + } }); } }, []); - - if(props.location.pathname.includes('EnterpriseInfo/OrgInfo')){ + if (props.location.pathname.includes("EnterpriseInfo/OrgInfo")) { return props.children; } diff --git a/src/pages/Container/QualApplication/index.js b/src/pages/Container/QualApplication/index.js index 60b848d..c465633 100644 --- a/src/pages/Container/QualApplication/index.js +++ b/src/pages/Container/QualApplication/index.js @@ -11,8 +11,10 @@ function QualApplication(props) { useEffect(() => { if (!orgInfoId) { props.orgInfoGet().then((res) => { - setOrgInfoId(res?.data?.id); - sessionStorage.setItem("orgInfoId", res?.data?.id); + if (res?.data?.id) { + setOrgInfoId(res?.data?.id); + sessionStorage.setItem("orgInfoId", res?.data?.id); + } }); } }, []);