From 110817abba34bb760a57a397079c469eef622d01 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Thu, 23 Apr 2026 09:45:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=8B=9B=E8=81=98=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=8F=8CSheet=E5=AF=BC=E5=85=A5=E5=AE=9E=E6=96=BD?= =?UTF-8?q?=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ual-sheet-import-backend-implementation.md | 320 ++++++++++++++++++ ...al-sheet-import-frontend-implementation.md | 264 +++++++++++++++ ...cruitment-dual-sheet-import-plan-record.md | 19 ++ 3 files changed, 603 insertions(+) create mode 100644 docs/plans/backend/2026-04-23-staff-recruitment-dual-sheet-import-backend-implementation.md create mode 100644 docs/plans/frontend/2026-04-23-staff-recruitment-dual-sheet-import-frontend-implementation.md create mode 100644 docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-plan-record.md diff --git a/docs/plans/backend/2026-04-23-staff-recruitment-dual-sheet-import-backend-implementation.md b/docs/plans/backend/2026-04-23-staff-recruitment-dual-sheet-import-backend-implementation.md new file mode 100644 index 00000000..817104de --- /dev/null +++ b/docs/plans/backend/2026-04-23-staff-recruitment-dual-sheet-import-backend-implementation.md @@ -0,0 +1,320 @@ +# Staff Recruitment Dual-Sheet Import Backend Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将招聘信息管理后端导入链路收口为“招聘信息 + 历史工作经历”双 Sheet 单任务模式,并补齐失败 Sheet、失败行号、失败原因。 + +**Architecture:** 保留现有 `ccdi:staffRecruitment:*` 权限、`/ccdi/staffRecruitment/*` 路径和现有主从表结构,只收口控制器、服务接口和异步导入编排。导入任务先处理主信息 Sheet,再按 `recruitId` 分组处理工作经历 Sheet,工作经历匹配“本次主 Sheet 成功数据 + 数据库已有主信息”,若数据库中已存在旧工作经历则直接整组失败,不做覆盖。 + +**Tech Stack:** Java 21, Spring Boot 3, MyBatis-Plus, EasyExcel, Redis, JUnit 5, Mockito + +--- + +## File Map + +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java` + - 收口双 Sheet 模板下载与统一导入入口,移除独立工作经历导入接口 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentService.java` + - 暴露统一的双 Sheet 提交入口 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentImportService.java` + - 暴露统一的双 Sheet 异步导入入口 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java` + - 初始化统一 Redis 任务状态并提交统一异步任务 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentImportServiceImpl.java` + - 实现主 Sheet 与工作经历 Sheet 两阶段编排、行号上下文、已有工作经历报错规则 +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/RecruitmentImportFailureVO.java` + - 新增 `sheetName`、`sheetRowNum` +- Create: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java` + - 锁定控制器与接口的双 Sheet 契约 +- Create: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentImportServiceImplTest.java` + - 锁定异步导入编排、已有工作经历报错和失败定位行为 +- Modify: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/utils/EasyExcelUtilTemplateTest.java` + - 补模板双 Sheet 约束回归 +- Create: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md` + - 记录最终实施内容、验证结果与浏览器实测结论 + +### Task 1: 锁定双 Sheet 导入接口契约 + +**Files:** +- Create: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentService.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentImportService.java` + +- [ ] **Step 1: 写控制器与接口契约失败测试** + +```java +@Test +void shouldExposeSingleDualSheetImportEntry() throws Exception { + String controller = Files.readString( + Path.of("src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java") + ); + assertTrue(controller.contains("\"招聘信息\"")); + assertTrue(controller.contains("\"历史工作经历\"")); + assertFalse(controller.contains("workImportTemplate")); + assertFalse(controller.contains("importWorkData")); +} +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest test` + +Expected: FAIL,提示控制器仍存在 `workImportTemplate` / `importWorkData` 或接口签名仍为双入口 + +- [ ] **Step 3: 最小化修改控制器与服务接口** + +```java +String importRecruitment( + List recruitmentList, + List workList +); + +void importRecruitmentAsync( + List recruitmentList, + List workList, + String taskId, + String userName +); +``` + +- [ ] **Step 4: 重跑契约测试** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest test` + +Expected: PASS,控制器仅保留双 Sheet 模板与单导入入口 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentService.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentImportService.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java +git commit -m "收口招聘双Sheet导入接口" +``` + +### Task 2: 接入双 Sheet 模板与失败 VO 字段 + +**Files:** +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/RecruitmentImportFailureVO.java` +- Modify: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java` +- Modify: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/utils/EasyExcelUtilTemplateTest.java` + +- [ ] **Step 1: 先补失败 VO 与模板契约测试** + +```java +assertHasField( + "com.ruoyi.info.collection.domain.vo.RecruitmentImportFailureVO", + "sheetName" +); +assertHasField( + "com.ruoyi.info.collection.domain.vo.RecruitmentImportFailureVO", + "sheetRowNum" +); +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest,EasyExcelUtilTemplateTest test` + +Expected: FAIL,提示 `RecruitmentImportFailureVO` 缺字段或模板断言未通过 + +- [ ] **Step 3: 最小化实现字段与模板导出** + +```java +EasyExcelUtil.importTemplateWithDictDropdown( + response, + CcdiStaffRecruitmentExcel.class, + "招聘信息", + CcdiStaffRecruitmentWorkExcel.class, + "历史工作经历", + "招聘信息管理导入模板" +); +``` + +- [ ] **Step 4: 重跑测试** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest,EasyExcelUtilTemplateTest test` + +Expected: PASS,模板输出双 Sheet,失败 VO 具备新字段 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/RecruitmentImportFailureVO.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/utils/EasyExcelUtilTemplateTest.java +git commit -m "补齐招聘双Sheet模板与失败字段" +``` + +### Task 3: 收口服务层任务初始化与统一状态统计 + +**Files:** +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java` +- Modify: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java` + +- [ ] **Step 1: 先写服务层统一任务初始化失败测试** + +```java +@Test +void shouldInitializeSingleRedisTaskForTwoSheets() throws Exception { + String service = Files.readString( + Path.of("src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java") + ); + assertTrue(service.contains("recruitmentList.size() + workList.size()")); + assertFalse(service.contains("importRecruitmentWork(")); +} +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest test` + +Expected: FAIL,提示服务层仍保留独立工作经历任务初始化 + +- [ ] **Step 3: 实现统一提交入口** + +```java +public String importRecruitment( + List recruitmentList, + List workList +) { + int totalCount = recruitmentList.size() + workList.size(); + recruitmentImportService.importRecruitmentAsync(recruitmentList, workList, taskId, userName); + return taskId; +} +``` + +- [ ] **Step 4: 重跑契约测试** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest test` + +Expected: PASS,任务总数按双 Sheet 合并统计,接口只剩统一入口 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java +git commit -m "统一招聘双Sheet任务初始化" +``` + +### Task 4: 实现异步导入两阶段编排与失败定位 + +**Files:** +- Create: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentImportServiceImplTest.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentImportServiceImpl.java` + +- [ ] **Step 1: 先写异步导入失败测试** + +```java +@Test +void shouldFailWholeWorkGroupWhenExistingHistoryExists() { + // arrange: recruitmentWorkMapper.countByRecruitId("RC001") -> 1 + // act: importRecruitmentAsync(mainRows, workRows, taskId, "admin") + // assert: recruitmentWorkMapper.insert(...) not called + // assert: failure.sheetName == "历史工作经历" + // assert: failure.sheetRowNum == "2" +} +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentImportServiceImplTest test` + +Expected: FAIL,提示当前实现仍会删除旧工作经历或未记录 `sheetName` / `sheetRowNum` + +- [ ] **Step 3: 以最小改动实现两阶段编排** + +```java +List indexedMainRows = buildMainImportRows(recruitmentList); +List indexedWorkRows = buildWorkImportRows(workList); +Map importedRecruitmentMap = importMainSheet(indexedMainRows, failures, userName); +importWorkSheet(indexedWorkRows, importedRecruitmentMap, failures, userName); +``` + +- [ ] **Step 4: 补充工作经历“已有旧记录即失败”与行号上下文** + +```java +if (hasExistingWorkHistory(recruitId)) { + throw buildValidationException( + "历史工作经历", + extractWorkRowNums(workRows), + String.format("招聘记录编号[%s]已存在历史工作经历,不允许重复导入", recruitId) + ); +} +``` + +- [ ] **Step 5: 重跑测试** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentImportServiceImplTest test` + +Expected: PASS,已有工作经历不覆盖,失败记录带 Sheet 与行号 + +- [ ] **Step 6: 提交这一小步** + +```bash +git add \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentImportServiceImpl.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentImportServiceImplTest.java +git commit -m "实现招聘双Sheet异步导入编排" +``` + +### Task 5: 做后端回归、补实施记录并交付联调入口 + +**Files:** +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentImportServiceImpl.java` +- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/RecruitmentImportFailureVO.java` +- Create: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md` + +- [ ] **Step 1: 运行后端定向测试** + +Run: `mvn -pl ccdi-info-collection -Dtest=CcdiStaffRecruitmentDualImportContractTest,CcdiStaffRecruitmentImportServiceImplTest,EasyExcelUtilTemplateTest test` + +Expected: PASS,双 Sheet 契约、异步编排、模板约束全部通过 + +- [ ] **Step 2: 运行模块编译** + +Run: `mvn -pl ccdi-info-collection,ruoyi-admin -am -DskipTests compile` + +Expected: BUILD SUCCESS + +- [ ] **Step 3: 启动后端供前端联调** + +Run: `sh bin/restart_java_backend.sh` + +Expected: 后端正常重启,`/ccdi/staffRecruitment/importTemplate` 与 `/importData` 可访问 + +- [ ] **Step 4: 补实施记录** + +```md +- 导入入口收口为双 Sheet 单任务 +- 工作经历导入改为“已有旧记录时报错” +- 失败记录补齐失败 Sheet、失败行号、失败原因 +- 已完成后端编译与定向测试 +``` + +- [ ] **Step 5: 提交后端收尾** + +```bash +git add \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiStaffRecruitmentController.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentService.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiStaffRecruitmentImportService.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentServiceImpl.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiStaffRecruitmentImportServiceImpl.java \ + ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/RecruitmentImportFailureVO.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentDualImportContractTest.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiStaffRecruitmentImportServiceImplTest.java \ + ccdi-info-collection/src/test/java/com/ruoyi/info/collection/utils/EasyExcelUtilTemplateTest.java \ + docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md +git commit -m "完成招聘双Sheet导入后端改造" +``` diff --git a/docs/plans/frontend/2026-04-23-staff-recruitment-dual-sheet-import-frontend-implementation.md b/docs/plans/frontend/2026-04-23-staff-recruitment-dual-sheet-import-frontend-implementation.md new file mode 100644 index 00000000..29952fb9 --- /dev/null +++ b/docs/plans/frontend/2026-04-23-staff-recruitment-dual-sheet-import-frontend-implementation.md @@ -0,0 +1,264 @@ +# Staff Recruitment Dual-Sheet Import Frontend Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 将招聘信息管理前端导入交互改为单入口双 Sheet 模式,统一任务轮询与失败弹窗,并在失败列表中展示失败 Sheet、失败行号、失败原因。 + +**Architecture:** 前端只保留一个导入按钮和一个上传弹窗,统一使用 `/ccdi/staffRecruitment/importTemplate` 与 `/importData`。页面本地状态从“按导入类型区分任务”收口为“按唯一任务 ID 轮询”,失败记录统一通过一个弹窗展示,并用 `sheetName`、`sheetRowNum` 区分失败来源。 + +**Tech Stack:** Vue 2, Element UI, axios request wrapper, Node 14.21.3 via nvm, source-inspection unit tests, Playwright browser validation + +--- + +## File Map + +- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` + - 删除独立“导入工作经历”入口,收口上传弹窗、轮询状态和失败列表 +- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js` + - 去掉独立工作经历导入模板/上传调用,保留统一导入 API +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js` + - 锁定顶部工具栏已收口为单入口 +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js` + - 锁定统一任务状态与轮询逻辑 +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js` + - 锁定失败弹窗列定义与 `sheetRowNum` 展示格式 +- Modify: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md` + - 追加前端改造与真实页面验证结果 + +### Task 1: 收口工具栏与上传 API + +**Files:** +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js` +- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` +- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js` + +- [ ] **Step 1: 先写工具栏与 API 契约失败测试** + +```js +[ + "handleImport()", + '"/ccdi/staffRecruitment/importData"', + "招聘信息管理导入模板" +].forEach((token) => { + assert(source.includes(token), `招聘导入入口缺少统一双Sheet能力: ${token}`) +}) + +[ + "handleWorkImport", + "importWorkData", + "workImportTemplate" +].forEach((token) => { + assert(!source.includes(token), `招聘页不应继续保留独立工作经历导入: ${token}`) +}) +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js` + +Expected: FAIL,提示页面仍保留“导入工作经历”按钮或 API 仍存在旧入口 + +- [ ] **Step 3: 最小化修改页面与 API** + +```js +export function importTemplate() { + return request({ + url: "/ccdi/staffRecruitment/importTemplate", + method: "post" + }) +} +``` + +- [ ] **Step 4: 重跑测试** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js` + +Expected: PASS,页面只剩一个导入入口,API 只调用统一模板与上传接口 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \ + ruoyi-ui/src/api/ccdiStaffRecruitment.js \ + ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js +git commit -m "收口招聘双Sheet导入前端入口" +``` + +### Task 2: 收口上传弹窗文案与统一任务状态 + +**Files:** +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js` +- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` + +- [ ] **Step 1: 先写统一状态失败测试** + +```js +[ + "模板包含“招聘信息”和“历史工作经历”两个 Sheet。", + "this.currentTaskId = taskId", + "this.showFailureButton = false", + "this.startImportStatusPolling(taskId)" +].forEach((token) => { + assert(source.includes(token), `招聘导入状态未统一到单任务: ${token}`) +}) + +[ + "currentImportType", + "upload.importType", + "getImportTypeLabel" +].forEach((token) => { + assert(!source.includes(token), `招聘导入状态不应再按类型拆分: ${token}`) +}) +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js` + +Expected: FAIL,提示页面仍保留类型切换状态 + +- [ ] **Step 3: 最小化实现统一轮询状态** + +```js +this.saveImportTaskToStorage({ + taskId, + status: "PROCESSING", + hasFailures: false +}) +this.currentTaskId = taskId +this.startImportStatusPolling(taskId) +``` + +- [ ] **Step 4: 重跑测试** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js` + +Expected: PASS,弹窗文案改为双 Sheet,页面状态只围绕一个任务 ID 轮询 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \ + ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js +git commit -m "统一招聘双Sheet导入轮询状态" +``` + +### Task 3: 调整统一失败弹窗列定义 + +**Files:** +- Create: `ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js` +- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` + +- [ ] **Step 1: 先写失败弹窗失败测试** + +```js +[ + 'label="失败Sheet"', + 'label="失败行号"', + "scope.row.sheetName", + "scope.row.sheetRowNum", + "失败原因" +].forEach((token) => { + assert(source.includes(token), `招聘失败弹窗缺少双Sheet定位列: ${token}`) +}) +``` + +- [ ] **Step 2: 运行测试确认失败** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js` + +Expected: FAIL,提示弹窗仍按旧类型列展示 + +- [ ] **Step 3: 实现统一失败表格** + +```vue + + + + +``` + +- [ ] **Step 4: 重跑测试** + +Run: `node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js` + +Expected: PASS,失败弹窗明确展示失败 Sheet、失败行号、失败原因 + +- [ ] **Step 5: 提交这一小步** + +```bash +git add \ + ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \ + ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js +git commit -m "完善招聘双Sheet失败弹窗展示" +``` + +### Task 4: 做前端构建、真实页面验证与实施记录 + +**Files:** +- Modify: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue` +- Modify: `ruoyi-ui/src/api/ccdiStaffRecruitment.js` +- Modify: `docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md` + +- [ ] **Step 1: 切换 Node 版本并执行前端静态回归** + +Run: `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && node ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js && node ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js && node ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js` + +Expected: PASS,三个静态契约测试全部通过 + +- [ ] **Step 2: 执行前端构建** + +Run: `source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null && cd ruoyi-ui && npm run build:prod` + +Expected: BUILD SUCCESS + +- [ ] **Step 3: 启动真实页面并做浏览器验证** + +Run: + +```bash +source ~/.nvm/nvm.sh && nvm use 14.21.3 >/dev/null +cd ruoyi-ui +npm run dev -- --port 8080 +``` + +Expected: 前端开发服务启动成功,真实页面 `http://localhost:8080` 可访问 + +Playwright 验证最少覆盖: + +- 进入真实 `招聘信息管理` 页面,不使用 prototype 页面 +- 从页面下载双 Sheet 模板 +- 只导 `招聘信息` Sheet +- 只导 `历史工作经历` Sheet +- 双 Sheet 同时导入 +- 已存在工作经历时报错 +- 失败弹窗显示 `失败Sheet / 失败行号 / 失败原因` + +- [ ] **Step 4: 补前端实施记录** + +```md +- 页面导入入口收口为一个按钮 +- 上传弹窗提示调整为双 Sheet 文案 +- 页面状态收口为单任务轮询 +- 失败弹窗新增失败 Sheet、失败行号、失败原因 +- 已完成真实页面 Playwright 验证 +``` + +- [ ] **Step 5: 关闭测试进程并提交前端收尾** + +Run: 关闭本轮 `npm run dev` 与后端联调用到的进程,确保无残留端口占用 + +```bash +git add \ + ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue \ + ruoyi-ui/src/api/ccdiStaffRecruitment.js \ + ruoyi-ui/tests/unit/staff-recruitment-import-toolbar.test.js \ + ruoyi-ui/tests/unit/staff-recruitment-import-state.test.js \ + ruoyi-ui/tests/unit/staff-recruitment-import-failure-dialog.test.js \ + docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-implementation.md +git commit -m "完成招聘双Sheet导入前端改造" +``` diff --git a/docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-plan-record.md b/docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-plan-record.md new file mode 100644 index 00000000..4e388e16 --- /dev/null +++ b/docs/reports/implementation/2026-04-23-staff-recruitment-dual-sheet-import-plan-record.md @@ -0,0 +1,19 @@ +# 招聘信息管理双 Sheet 导入实施计划记录 + +## 本次产出 + +- 后端实施计划: + - `docs/plans/backend/2026-04-23-staff-recruitment-dual-sheet-import-backend-implementation.md` +- 前端实施计划: + - `docs/plans/frontend/2026-04-23-staff-recruitment-dual-sheet-import-frontend-implementation.md` + +## 计划基线 + +- 设计文档: + - `docs/design/2026-04-23-staff-recruitment-dual-sheet-import-design.md` + +## 计划结论 + +- 后端按“控制器收口 -> 统一任务初始化 -> 两阶段异步编排 -> 失败定位补齐”推进 +- 前端按“单入口上传 -> 单任务轮询 -> 单失败弹窗 -> 真实页面验证”推进 +- 实施过程中必须补实施记录,并在真实页面完成 Playwright 导入验证