新增员工资产信息设计与实施计划
This commit is contained in:
@@ -0,0 +1,335 @@
|
|||||||
|
# Employee Asset Maintenance Backend Implementation Plan
|
||||||
|
|
||||||
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** Add backend support for employee asset maintenance, including aggregated employee detail/save, employee-asset cascade delete, and asynchronous asset import based on employee ID card linkage.
|
||||||
|
|
||||||
|
**Architecture:** Keep employee maintenance on the existing `CcdiBaseStaff` aggregate, and introduce a new `CcdiAssetInfo` resource in `ccdi-info-collection`. Employee add/edit/detail/delete will orchestrate asset persistence inside transactions, while asset import will mirror the existing employee import flow with a dedicated controller, service, Redis task keys, and failure records.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 21, Spring Boot 3, MyBatis Plus, XML mapper SQL, EasyExcel, Redis, Maven
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Add the asset table SQL and document the data contract
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `sql/2026-03-12_ccdi_asset_info.sql`
|
||||||
|
- Review: `assets/资产信息表.csv`
|
||||||
|
- Review: `docs/plans/2026-03-12-employee-asset-maintenance-design.md`
|
||||||
|
|
||||||
|
**Step 1: Write the SQL script**
|
||||||
|
|
||||||
|
Create `ccdi_asset_info` with:
|
||||||
|
|
||||||
|
- `asset_id BIGINT` auto increment primary key
|
||||||
|
- `person_id VARCHAR(18)` storing employee `id_card`
|
||||||
|
- business columns from the approved design
|
||||||
|
- audit columns `create_by`, `create_time`, `update_by`, `update_time`
|
||||||
|
- indexes `idx_person_id` and `idx_asset_main_type`
|
||||||
|
|
||||||
|
**Step 2: Verify the script matches the approved constraints**
|
||||||
|
|
||||||
|
Confirm the script:
|
||||||
|
|
||||||
|
- does not include `asset_id` in any import-facing note
|
||||||
|
- keeps the field name `person_id`
|
||||||
|
- links by employee `id_card`, not `staff_id`
|
||||||
|
|
||||||
|
**Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add sql/2026-03-12_ccdi_asset_info.sql docs/plans/2026-03-12-employee-asset-maintenance-design.md
|
||||||
|
git commit -m "新增员工资产信息设计与建表脚本"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Task 2: Add the asset domain, DTO, VO, Excel, and mapper skeletons
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiAssetInfo.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiAssetInfoDTO.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiAssetInfoVO.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/excel/CcdiAssetInfoExcel.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/AssetImportFailureVO.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapper.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml`
|
||||||
|
|
||||||
|
**Step 1: Create the entity**
|
||||||
|
|
||||||
|
Model `CcdiAssetInfo` with MyBatis Plus annotations and audit fields consistent with existing `CcdiBaseStaff`.
|
||||||
|
|
||||||
|
**Step 2: Create request and response objects**
|
||||||
|
|
||||||
|
Add one DTO for nested employee-form submission and one VO for employee detail response.
|
||||||
|
|
||||||
|
**Step 3: Create the Excel and failure record objects**
|
||||||
|
|
||||||
|
`CcdiAssetInfoExcel` must exclude `asset_id` and include `person_id`.
|
||||||
|
|
||||||
|
**Step 4: Create mapper methods**
|
||||||
|
|
||||||
|
Define methods for:
|
||||||
|
|
||||||
|
- query by `person_id`
|
||||||
|
- delete by `person_id`
|
||||||
|
- delete by `person_id` list
|
||||||
|
- batch insert
|
||||||
|
- import lookup by employee `id_card`
|
||||||
|
|
||||||
|
### Task 3: Extend employee DTO and VO aggregation
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiBaseStaffAddDTO.java`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiBaseStaffEditDTO.java`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CcdiBaseStaffVO.java`
|
||||||
|
- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceAssetAggregationTest.java`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Add a focused service test that asserts employee detail, add DTO, and edit DTO support `assetInfoList`.
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiBaseStaffServiceAssetAggregationTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because `assetInfoList` does not exist yet.
|
||||||
|
|
||||||
|
**Step 3: Add the aggregate fields**
|
||||||
|
|
||||||
|
Add `List<CcdiAssetInfoDTO>` to add/edit DTOs and `List<CcdiAssetInfoVO>` to the employee VO.
|
||||||
|
|
||||||
|
**Step 4: Run test to verify it passes**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiBaseStaffServiceAssetAggregationTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 4: Add asset service interfaces and focused persistence methods
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoService.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoServiceImpl.java`
|
||||||
|
- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoServiceImplTest.java`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Cover these behaviors:
|
||||||
|
|
||||||
|
- query all assets by `person_id`
|
||||||
|
- replace all assets for one employee
|
||||||
|
- delete assets by one or more employee ID cards
|
||||||
|
- ignore empty rows when replacing assets
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because the asset service does not exist yet.
|
||||||
|
|
||||||
|
**Step 3: Implement minimal service logic**
|
||||||
|
|
||||||
|
Implement methods:
|
||||||
|
|
||||||
|
- `selectByPersonId`
|
||||||
|
- `replaceByPersonId`
|
||||||
|
- `deleteByPersonId`
|
||||||
|
- `deleteByPersonIds`
|
||||||
|
|
||||||
|
Use batch delete + batch insert.
|
||||||
|
|
||||||
|
**Step 4: Run test to verify it passes**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 5: Update employee service to aggregate asset query and transactional save
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiBaseStaffService.java`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java`
|
||||||
|
- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiBaseStaffServiceImplTest.java`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Add or extend service tests for:
|
||||||
|
|
||||||
|
- add employee with multiple assets
|
||||||
|
- edit employee and replace assets
|
||||||
|
- edit employee with changed `id_card`
|
||||||
|
- detail query returns `assetInfoList`
|
||||||
|
- delete employee cascades asset deletion
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiBaseStaffServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because employee service does not yet coordinate asset persistence.
|
||||||
|
|
||||||
|
**Step 3: Implement the aggregate behavior**
|
||||||
|
|
||||||
|
Update `CcdiBaseStaffServiceImpl` to:
|
||||||
|
|
||||||
|
- inject `ICcdiAssetInfoService`
|
||||||
|
- load assets during detail query
|
||||||
|
- save employee first, then replace assets by current employee `id_card`
|
||||||
|
- capture old `id_card` during edit and clean old asset records when it changes
|
||||||
|
- delete asset rows before deleting employee rows
|
||||||
|
|
||||||
|
**Step 4: Run the focused test again**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiBaseStaffServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 6: Add asset import controller and async import service
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiAssetInfoController.java`
|
||||||
|
- Create: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.java`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiAssetInfoImportService.java`
|
||||||
|
- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiAssetInfoImportServiceImplTest.java`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Cover:
|
||||||
|
|
||||||
|
- import template entity excludes `asset_id`
|
||||||
|
- import fails when `person_id` does not match an employee `id_card`
|
||||||
|
- import stores failure records only for bad rows
|
||||||
|
- import status and failure list use dedicated asset task keys
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoImportServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because no asset import pipeline exists yet.
|
||||||
|
|
||||||
|
**Step 3: Implement the import flow**
|
||||||
|
|
||||||
|
Mirror the employee import design with asset-specific names:
|
||||||
|
|
||||||
|
- Redis key prefix `import:assetInfo:`
|
||||||
|
- asynchronous import execution
|
||||||
|
- failure record caching for 7 days
|
||||||
|
- controller endpoints for template, upload, status, and failures
|
||||||
|
|
||||||
|
**Step 4: Run the focused test again**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoImportServiceImplTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 7: Add mapper XML SQL for batch asset operations
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiAssetInfoMapper.xml`
|
||||||
|
- Test: `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiAssetInfoMapperTest.java`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
|
||||||
|
- select by `person_id`
|
||||||
|
- batch insert multiple assets
|
||||||
|
- delete by one `person_id`
|
||||||
|
- delete by multiple `person_id` values
|
||||||
|
- employee existence lookup by `id_card`
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoMapperTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because the mapper XML SQL is incomplete.
|
||||||
|
|
||||||
|
**Step 3: Implement minimal XML SQL**
|
||||||
|
|
||||||
|
Add:
|
||||||
|
|
||||||
|
- result map
|
||||||
|
- select by `person_id`
|
||||||
|
- delete by `person_id`
|
||||||
|
- delete by `person_id` list
|
||||||
|
- batch insert
|
||||||
|
- employee existence lookup
|
||||||
|
|
||||||
|
**Step 4: Run the mapper test**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiAssetInfoMapperTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 8: Verify the backend changes end to end
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/`
|
||||||
|
- Modify: `ccdi-info-collection/src/main/resources/mapper/info/collection/`
|
||||||
|
- Modify: `sql/2026-03-12_ccdi_asset_info.sql`
|
||||||
|
|
||||||
|
**Step 1: Run focused backend tests**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn test -Dtest=CcdiBaseStaffServiceAssetAggregationTest,CcdiAssetInfoServiceImplTest,CcdiBaseStaffServiceImplTest,CcdiAssetInfoImportServiceImplTest,CcdiAssetInfoMapperTest
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all focused tests pass.
|
||||||
|
|
||||||
|
**Step 2: Run module compile verification**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: compile succeeds without Java or mapper XML errors.
|
||||||
|
|
||||||
|
**Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add sql/2026-03-12_ccdi_asset_info.sql ccdi-info-collection/src/main/java/com/ruoyi/info/collection ccdi-info-collection/src/main/resources/mapper/info/collection docs/plans/2026-03-12-employee-asset-maintenance-backend-implementation.md
|
||||||
|
git commit -m "新增员工资产信息后端实施计划"
|
||||||
|
```
|
||||||
259
docs/plans/2026-03-12-employee-asset-maintenance-design.md
Normal file
259
docs/plans/2026-03-12-employee-asset-maintenance-design.md
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
# 员工资产信息维护设计
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
员工信息维护页面当前仅支持维护员工基础信息,不支持维护员工名下资产信息。现需在现有员工维护页面中补齐资产信息的新增、编辑、删除、详情展示和导入能力,并与现有员工导入交互保持一致。
|
||||||
|
|
||||||
|
本次设计基于 `2026-03-12` 确认了以下业务约束:
|
||||||
|
|
||||||
|
- 资产表保留字段名 `person_id`
|
||||||
|
- `person_id` 存员工身份证号,关联 `ccdi_base_staff.id_card`
|
||||||
|
- `asset_id` 改为数据库自增主键,不出现在导入模板中
|
||||||
|
- 资产导入失败记录入口需与员工导入失败记录入口明确区分,按钮文案为“查看员工资产导入失败记录”
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
- 在员工信息维护页新增员工资产信息维护能力
|
||||||
|
- 在员工新增和编辑弹窗中支持员工资产信息的添加、编辑、删除
|
||||||
|
- 在员工详情弹窗中展示该员工全部资产信息
|
||||||
|
- 在员工列表页新增“导入资产信息”入口,并复用现有异步导入交互
|
||||||
|
- 删除员工时同步删除该员工全部资产信息
|
||||||
|
|
||||||
|
## 非目标
|
||||||
|
- 不新增独立的“员工资产信息管理”菜单页面
|
||||||
|
- 不在资产维护中暴露 `asset_id`
|
||||||
|
- 不允许用户在前端手工填写 `person_id`
|
||||||
|
- 不改造现有员工列表查询条件和分页结构
|
||||||
|
|
||||||
|
## 现状
|
||||||
|
当前员工维护能力主要集中在以下位置:
|
||||||
|
|
||||||
|
- 前端页面:`ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- 前端接口:`ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||||
|
- 后端控制器:`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.java`
|
||||||
|
- 后端服务:`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffServiceImpl.java`
|
||||||
|
- 导入服务:`ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiBaseStaffImportServiceImpl.java`
|
||||||
|
|
||||||
|
现有员工导入采用异步任务 + Redis 状态轮询 + 失败记录分页查询的模式,前端使用本地存储保存最近一次导入任务信息。
|
||||||
|
|
||||||
|
## 方案对比
|
||||||
|
|
||||||
|
### 方案一:员工页内嵌资产子表聚合维护
|
||||||
|
- 员工新增、编辑、详情接口返回员工主信息和资产列表聚合结果
|
||||||
|
- 员工弹窗内部直接维护资产子表
|
||||||
|
- 资产导入使用独立接口,但交互完全复用员工导入模式
|
||||||
|
|
||||||
|
优点:
|
||||||
|
- 最符合当前需求和现有页面使用习惯
|
||||||
|
- 用户一次打开员工弹窗即可维护完整信息
|
||||||
|
- 删除员工时做事务级联最直接
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
- 员工 DTO、VO、Service、前端表单都需要联动调整
|
||||||
|
|
||||||
|
### 方案二:资产作为独立模块实现,员工页只嵌入调用
|
||||||
|
- 后端和前端都按独立资源建设资产模块
|
||||||
|
- 员工页通过额外接口拉取并嵌入展示
|
||||||
|
|
||||||
|
优点:
|
||||||
|
- 模块边界更清晰
|
||||||
|
- 未来扩展独立资产菜单更容易
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
- 本次需求明显超出必要范围
|
||||||
|
- 页面状态和接口交互更复杂
|
||||||
|
|
||||||
|
### 方案三:员工页只加资产查看和导入,编辑改为二级弹窗
|
||||||
|
- 员工弹窗中通过二级弹窗维护资产
|
||||||
|
|
||||||
|
优点:
|
||||||
|
- 对现有员工表单侵入较小
|
||||||
|
|
||||||
|
缺点:
|
||||||
|
- 交互割裂
|
||||||
|
- 不符合“新增和编辑弹窗中支持添加、编辑、删除”的要求
|
||||||
|
|
||||||
|
## 最终方案
|
||||||
|
采用方案一:在员工信息维护页内嵌资产信息子表,员工接口作为聚合接口返回和保存资产列表;资产导入保持独立入口和独立后端接口,但沿用现有员工导入的上传、异步处理、结果通知、失败记录查询交互。
|
||||||
|
|
||||||
|
## 数据模型设计
|
||||||
|
|
||||||
|
### 数据表
|
||||||
|
新增 `ccdi_asset_info` 表,字段来源于 `assets/资产信息表.csv`,并做以下调整:
|
||||||
|
|
||||||
|
- `asset_id`:`BIGINT` 自增主键
|
||||||
|
- `person_id`:`VARCHAR(18)`,保存员工身份证号,关联 `ccdi_base_staff.id_card`
|
||||||
|
- 审计字段沿用当前项目规范,由后端自动填充
|
||||||
|
|
||||||
|
建议字段如下:
|
||||||
|
|
||||||
|
- `asset_id`
|
||||||
|
- `person_id`
|
||||||
|
- `asset_main_type`
|
||||||
|
- `asset_sub_type`
|
||||||
|
- `asset_name`
|
||||||
|
- `ownership_ratio`
|
||||||
|
- `purchase_eval_date`
|
||||||
|
- `original_value`
|
||||||
|
- `current_value`
|
||||||
|
- `valuation_date`
|
||||||
|
- `asset_status`
|
||||||
|
- `remarks`
|
||||||
|
- `create_by`
|
||||||
|
- `create_time`
|
||||||
|
- `update_by`
|
||||||
|
- `update_time`
|
||||||
|
|
||||||
|
建议索引:
|
||||||
|
|
||||||
|
- `idx_person_id(person_id)`
|
||||||
|
- `idx_asset_main_type(asset_main_type)`
|
||||||
|
|
||||||
|
## 后端设计
|
||||||
|
|
||||||
|
### 新增资产信息模块对象
|
||||||
|
在 `ccdi-info-collection` 中新增:
|
||||||
|
|
||||||
|
- `domain/CcdiAssetInfo.java`
|
||||||
|
- `domain/dto/CcdiAssetInfoDTO.java`
|
||||||
|
- `domain/vo/CcdiAssetInfoVO.java`
|
||||||
|
- `domain/excel/CcdiAssetInfoExcel.java`
|
||||||
|
- `domain/vo/AssetImportFailureVO.java`
|
||||||
|
- `mapper/CcdiAssetInfoMapper.java`
|
||||||
|
- `service/ICcdiAssetInfoService.java`
|
||||||
|
- `service/ICcdiAssetInfoImportService.java`
|
||||||
|
- `service/impl/CcdiAssetInfoServiceImpl.java`
|
||||||
|
- `service/impl/CcdiAssetInfoImportServiceImpl.java`
|
||||||
|
- `controller/CcdiAssetInfoController.java`
|
||||||
|
- `resources/mapper/info/collection/CcdiAssetInfoMapper.xml`
|
||||||
|
|
||||||
|
### 扩展员工聚合接口
|
||||||
|
扩展现有员工 DTO 和 VO:
|
||||||
|
|
||||||
|
- `CcdiBaseStaffAddDTO.assetInfoList`
|
||||||
|
- `CcdiBaseStaffEditDTO.assetInfoList`
|
||||||
|
- `CcdiBaseStaffVO.assetInfoList`
|
||||||
|
|
||||||
|
后端聚合规则:
|
||||||
|
|
||||||
|
- 查询员工详情时,按员工 `id_card` 查询全部资产并组装到 `assetInfoList`
|
||||||
|
- 新增员工时,先保存员工,再以员工 `id_card` 回填并保存资产列表
|
||||||
|
- 编辑员工时,更新员工主信息,再按员工身份证号重建资产列表
|
||||||
|
- 删除员工时,先删资产再删员工,整个过程置于同一事务内
|
||||||
|
|
||||||
|
### 编辑时的资产处理策略
|
||||||
|
编辑员工时不要求前端传递资产行状态,直接按“当前完整列表”覆盖:
|
||||||
|
|
||||||
|
- 获取编辑前员工旧身份证号
|
||||||
|
- 更新员工主信息
|
||||||
|
- 如果身份证号变更,按旧身份证号删除旧资产
|
||||||
|
- 按当前最新身份证号删除对应资产
|
||||||
|
- 将前端提交的资产列表统一设置 `person_id = 当前员工身份证号`
|
||||||
|
- 批量插入最新资产列表
|
||||||
|
|
||||||
|
该策略实现简单,能直接覆盖资产新增、编辑、删除三种变化。
|
||||||
|
|
||||||
|
### 资产导入接口
|
||||||
|
新增独立资产导入接口:
|
||||||
|
|
||||||
|
- `POST /ccdi/assetInfo/importTemplate`
|
||||||
|
- `POST /ccdi/assetInfo/importData`
|
||||||
|
- `GET /ccdi/assetInfo/importStatus/{taskId}`
|
||||||
|
- `GET /ccdi/assetInfo/importFailures/{taskId}`
|
||||||
|
|
||||||
|
导入交互沿用现有员工导入设计:
|
||||||
|
|
||||||
|
- 异步任务执行
|
||||||
|
- Redis 保存任务状态
|
||||||
|
- 失败记录单独缓存
|
||||||
|
- 前端轮询状态并显示通知
|
||||||
|
|
||||||
|
### 校验规则
|
||||||
|
|
||||||
|
#### 员工保存时
|
||||||
|
- 员工基础信息仍沿用现有校验规则
|
||||||
|
- `assetInfoList` 中空行自动过滤
|
||||||
|
- 资产必填项:`assetMainType`、`assetSubType`、`assetName`、`currentValue`、`assetStatus`
|
||||||
|
- 数值和日期格式必须合法
|
||||||
|
- 后端强制回填 `person_id = 员工 id_card`
|
||||||
|
|
||||||
|
#### 资产导入时
|
||||||
|
- `person_id` 必填,且必须能匹配到员工 `id_card`
|
||||||
|
- 模板中不包含 `asset_id`
|
||||||
|
- 允许同一员工导入多条资产
|
||||||
|
- 失败记录仅返回失败数据,不返回成功数据
|
||||||
|
|
||||||
|
## 前端设计
|
||||||
|
|
||||||
|
### 列表页
|
||||||
|
在 `ruoyi-ui/src/views/ccdiBaseStaff/index.vue` 的按钮区新增:
|
||||||
|
|
||||||
|
- “导入资产信息”按钮
|
||||||
|
- “查看员工资产导入失败记录”按钮
|
||||||
|
|
||||||
|
资产导入相关状态全部独立维护:
|
||||||
|
|
||||||
|
- 独立弹窗状态
|
||||||
|
- 独立轮询定时器
|
||||||
|
- 独立任务 ID
|
||||||
|
- 独立 localStorage key
|
||||||
|
- 独立失败记录弹窗和分页状态
|
||||||
|
|
||||||
|
### 新增和编辑弹窗
|
||||||
|
在现有员工弹窗的“基本信息”下方新增“资产信息”分区,采用可编辑子表形式。
|
||||||
|
|
||||||
|
子表字段:
|
||||||
|
|
||||||
|
- 资产大类
|
||||||
|
- 资产小类
|
||||||
|
- 资产名称
|
||||||
|
- 产权占比
|
||||||
|
- 购买/评估日期
|
||||||
|
- 资产原值
|
||||||
|
- 当前估值
|
||||||
|
- 估值截止日期
|
||||||
|
- 资产状态
|
||||||
|
- 备注
|
||||||
|
- 操作
|
||||||
|
|
||||||
|
交互规则:
|
||||||
|
|
||||||
|
- 分区右侧提供“新增资产”按钮
|
||||||
|
- 每行支持删除
|
||||||
|
- 编辑时回显 `assetInfoList`
|
||||||
|
- 前端不展示 `asset_id`、`person_id`
|
||||||
|
|
||||||
|
### 详情弹窗
|
||||||
|
在“员工详情”弹窗中新增“资产信息”区域,使用只读表格展示全部资产。
|
||||||
|
|
||||||
|
若无资产数据,显示“暂无资产信息”空状态。
|
||||||
|
|
||||||
|
## 数据流
|
||||||
|
|
||||||
|
### 员工新增
|
||||||
|
前端提交员工基础信息和 `assetInfoList`,后端保存员工后按 `id_card` 批量保存资产。
|
||||||
|
|
||||||
|
### 员工编辑
|
||||||
|
前端拉取员工详情回显基础信息和资产列表,用户修改后提交完整列表,后端按最新员工身份证号重建资产明细。
|
||||||
|
|
||||||
|
### 员工详情
|
||||||
|
后端查询员工主信息,再按 `id_card` 查询资产列表并一并返回。
|
||||||
|
|
||||||
|
### 员工删除
|
||||||
|
后端先按员工身份证号删除资产,再删除员工主记录。
|
||||||
|
|
||||||
|
### 资产导入
|
||||||
|
前端上传资产 Excel,后端异步校验并批量插入资产数据,失败记录通过独立入口查看。
|
||||||
|
|
||||||
|
## 异常处理
|
||||||
|
- 资产导入时,若 `person_id` 无法匹配员工,记录失败原因
|
||||||
|
- 员工编辑时若身份证号变更,必须同步处理旧资产清理和新资产重建
|
||||||
|
- 员工删除和员工编辑资产重建均使用事务,防止主从数据不一致
|
||||||
|
- 前端提示文案中明确区分“员工导入”和“员工资产导入”
|
||||||
|
|
||||||
|
## 验收标准
|
||||||
|
- 员工列表页新增“导入资产信息”按钮
|
||||||
|
- 资产导入失败时显示“查看员工资产导入失败记录”按钮
|
||||||
|
- 员工新增和编辑弹窗中可添加、编辑、删除资产信息
|
||||||
|
- 员工详情弹窗中可查看该员工全部资产信息
|
||||||
|
- 删除员工后,该员工资产信息同步删除
|
||||||
|
- 资产导入模板不包含 `asset_id`
|
||||||
|
- 资产导入使用 `person_id` 关联员工身份证号
|
||||||
@@ -0,0 +1,328 @@
|
|||||||
|
# Employee Asset Maintenance Frontend Implementation Plan
|
||||||
|
|
||||||
|
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** Extend the employee maintenance page so users can maintain employee assets inside the add/edit dialog, view all assets in the detail dialog, and import asset data with a dedicated failure-record entry.
|
||||||
|
|
||||||
|
**Architecture:** Keep the existing employee page and API module in place, and add a nested asset-table editing experience within `ccdiBaseStaff/index.vue`. Reuse the existing async import interaction model, but isolate all asset-import state, storage keys, dialogs, and button copy from the original employee-import flow.
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 2, Element UI 2, RuoYi request wrapper, scoped SFC styles, Node-based source assertions or existing frontend unit checks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Extend the frontend API layer for asset import
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||||
|
- Create: `ruoyi-ui/src/api/ccdiAssetInfo.js`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-api-contract.test.js`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Add a focused source-based test that asserts:
|
||||||
|
|
||||||
|
- employee detail save payload can include `assetInfoList`
|
||||||
|
- a dedicated asset import API module exists
|
||||||
|
- asset import API exposes template, upload, status, and failure methods
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because the asset API module does not exist yet.
|
||||||
|
|
||||||
|
**Step 3: Implement the minimal API surface**
|
||||||
|
|
||||||
|
Create `ccdiAssetInfo.js` with:
|
||||||
|
|
||||||
|
- `importAssetTemplate`
|
||||||
|
- `importAssetData`
|
||||||
|
- `getAssetImportStatus`
|
||||||
|
- `getAssetImportFailures`
|
||||||
|
|
||||||
|
Keep `ccdiBaseStaff.js` for employee CRUD.
|
||||||
|
|
||||||
|
**Step 4: Run the test again**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 2: Add a regression test for the new employee asset UI structure
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Assert the employee page contains:
|
||||||
|
|
||||||
|
- an “导入资产信息” trigger
|
||||||
|
- a “查看员工资产导入失败记录” trigger hook
|
||||||
|
- an “资产信息” section inside the add/edit dialog
|
||||||
|
- an add-asset action
|
||||||
|
- a detail-table section for assets
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because the current page lacks all asset-maintenance UI.
|
||||||
|
|
||||||
|
### Task 3: Add the editable asset section to the employee dialog
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
|
||||||
|
|
||||||
|
**Step 1: Add asset form state**
|
||||||
|
|
||||||
|
Extend `form` defaults with `assetInfoList: []`.
|
||||||
|
|
||||||
|
**Step 2: Add UI helpers**
|
||||||
|
|
||||||
|
Add methods for:
|
||||||
|
|
||||||
|
- create an empty asset row
|
||||||
|
- add one asset row
|
||||||
|
- remove one asset row
|
||||||
|
- normalize empty asset rows before submit
|
||||||
|
|
||||||
|
**Step 3: Add the asset editing table**
|
||||||
|
|
||||||
|
Render a section below basic employee info with columns for:
|
||||||
|
|
||||||
|
- `assetMainType`
|
||||||
|
- `assetSubType`
|
||||||
|
- `assetName`
|
||||||
|
- `ownershipRatio`
|
||||||
|
- `purchaseEvalDate`
|
||||||
|
- `originalValue`
|
||||||
|
- `currentValue`
|
||||||
|
- `valuationDate`
|
||||||
|
- `assetStatus`
|
||||||
|
- `remarks`
|
||||||
|
- row delete action
|
||||||
|
|
||||||
|
**Step 4: Run the layout test**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 4: Add detail dialog asset display
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
|
||||||
|
|
||||||
|
**Step 1: Add an asset table section to the detail dialog**
|
||||||
|
|
||||||
|
Render employee assets below the existing basic info card.
|
||||||
|
|
||||||
|
**Step 2: Add an empty state**
|
||||||
|
|
||||||
|
If `employeeDetail.assetInfoList` is empty, show “暂无资产信息”.
|
||||||
|
|
||||||
|
**Step 3: Keep existing employee detail fields intact**
|
||||||
|
|
||||||
|
Do not regress name, staff ID, department, ID card, phone, hire date, or status display.
|
||||||
|
|
||||||
|
### Task 5: Wire add/edit/detail requests to aggregate employee data
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Assert that:
|
||||||
|
|
||||||
|
- `handleAdd` initializes an empty `assetInfoList`
|
||||||
|
- `handleUpdate` stores returned `assetInfoList`
|
||||||
|
- `submitForm` posts the employee object with `assetInfoList`
|
||||||
|
- empty asset rows are filtered before submit
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because submit logic does not process asset rows yet.
|
||||||
|
|
||||||
|
**Step 3: Implement minimal submit behavior**
|
||||||
|
|
||||||
|
Update:
|
||||||
|
|
||||||
|
- `reset`
|
||||||
|
- `handleAdd`
|
||||||
|
- `handleUpdate`
|
||||||
|
- `handleDetail`
|
||||||
|
- `submitForm`
|
||||||
|
|
||||||
|
so they all preserve `assetInfoList`.
|
||||||
|
|
||||||
|
**Step 4: Run the submit-flow test**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 6: Add dedicated asset import UI state and upload dialog
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-import-ui.test.js`
|
||||||
|
|
||||||
|
**Step 1: Write the failing test**
|
||||||
|
|
||||||
|
Verify the page defines dedicated asset-import state:
|
||||||
|
|
||||||
|
- independent upload dialog object
|
||||||
|
- independent polling timer
|
||||||
|
- independent current task ID
|
||||||
|
- independent failure dialog state
|
||||||
|
- asset-specific localStorage key
|
||||||
|
|
||||||
|
**Step 2: Run test to verify it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: FAIL because only employee import state exists today.
|
||||||
|
|
||||||
|
**Step 3: Implement the asset import dialog**
|
||||||
|
|
||||||
|
Add:
|
||||||
|
|
||||||
|
- “导入资产信息” button
|
||||||
|
- asset upload dialog with template download
|
||||||
|
- asset-specific upload methods
|
||||||
|
- independent polling and completion handlers
|
||||||
|
|
||||||
|
**Step 4: Distinguish failure record copy**
|
||||||
|
|
||||||
|
Ensure the failure entry text is exactly `查看员工资产导入失败记录`.
|
||||||
|
|
||||||
|
**Step 5: Run the import UI test**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: PASS
|
||||||
|
|
||||||
|
### Task 7: Add the asset failure record dialog and isolate storage
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-import-ui.test.js`
|
||||||
|
|
||||||
|
**Step 1: Add an asset failure dialog**
|
||||||
|
|
||||||
|
The dialog title must be `员工资产导入失败记录`.
|
||||||
|
|
||||||
|
**Step 2: Add asset failure columns**
|
||||||
|
|
||||||
|
Show:
|
||||||
|
|
||||||
|
- `personId`
|
||||||
|
- `assetMainType`
|
||||||
|
- `assetSubType`
|
||||||
|
- `assetName`
|
||||||
|
- `errorMessage`
|
||||||
|
|
||||||
|
**Step 3: Use a dedicated localStorage key**
|
||||||
|
|
||||||
|
Keep employee import history under `employee_import_last_task` and store asset import history under a new asset-specific key.
|
||||||
|
|
||||||
|
**Step 4: Keep the original employee import flow unchanged**
|
||||||
|
|
||||||
|
Do not rename or regress the existing employee import controls and methods unless needed for clear separation.
|
||||||
|
|
||||||
|
### Task 8: Refine styles for the embedded asset table
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Test: `ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js`
|
||||||
|
|
||||||
|
**Step 1: Add focused scoped styles**
|
||||||
|
|
||||||
|
Style:
|
||||||
|
|
||||||
|
- asset section header row
|
||||||
|
- editable asset table
|
||||||
|
- empty asset state
|
||||||
|
- detail asset block
|
||||||
|
|
||||||
|
**Step 2: Preserve the existing employee dialog layout**
|
||||||
|
|
||||||
|
Ensure basic info layout remains readable on desktop and mobile widths.
|
||||||
|
|
||||||
|
### Task 9: Verify the frontend changes end to end
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `ruoyi-ui/src/views/ccdiBaseStaff/index.vue`
|
||||||
|
- Modify: `ruoyi-ui/src/api/ccdiAssetInfo.js`
|
||||||
|
- Modify: `ruoyi-ui/src/api/ccdiBaseStaff.js`
|
||||||
|
|
||||||
|
**Step 1: Run focused frontend tests**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-api-contract.test.js
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-maintenance-layout.test.js
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-submit-flow.test.js
|
||||||
|
node ruoyi-ui/tests/unit/employee-asset-import-ui.test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all focused tests pass.
|
||||||
|
|
||||||
|
**Step 2: Run production build verification**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
Workdir: `ruoyi-ui`
|
||||||
|
|
||||||
|
Expected: build succeeds without Vue template or script errors.
|
||||||
|
|
||||||
|
**Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add ruoyi-ui/src/api/ccdiAssetInfo.js ruoyi-ui/src/api/ccdiBaseStaff.js ruoyi-ui/src/views/ccdiBaseStaff/index.vue ruoyi-ui/tests/unit docs/plans/2026-03-12-employee-asset-maintenance-frontend-implementation.md
|
||||||
|
git commit -m "新增员工资产信息前端实施计划"
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user