# 员工采购交易信息管理功能实施计划

> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

**目标:** 构建完整的员工采购交易信息管理模块，支持CRUD操作、分页查询、异步导入导出、批量删除等功能

**架构:** 基于若依框架前后端分离架构，后端使用MyBatis Plus + EasyExcel + Redis实现异步导入，前端使用Vue 2 + Element UI

**技术栈:**

- 后端: Spring Boot 3.5.8, MyBatis Plus 3.5.10, EasyExcel, Redis
- 前端: Vue 2.6.12, Element UI 2.15.14, Axios
- 数据库: MySQL 8.2.0

---

## 前置条件

### 参考文档

- 员工招聘信息模块: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/**/CcdiStaffRecruitment*`
- 员工异步导入实现: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiEmployeeImportServiceImpl.java`
- 数据库表定义: `doc/docs/ccdi_purchase_transaction.csv`

### 数据库表结构

```sql
CREATE TABLE `ccdi_purchase_transaction` (
  `purchase_id` VARCHAR(32) NOT NULL COMMENT '采购事项ID',
  `purchase_category` VARCHAR(50) NOT NULL COMMENT '采购类别',
  `project_name` VARCHAR(200) DEFAULT NULL COMMENT '项目名称',
  `subject_name` VARCHAR(200) NOT NULL COMMENT '标的物名称',
  `subject_desc` TEXT COMMENT '标的物描述',
  `purchase_qty` DECIMAL(12,4) NOT NULL DEFAULT 1 COMMENT '采购数量',
  `budget_amount` DECIMAL(18,2) NOT NULL COMMENT '预算金额',
  `bid_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '中标金额',
  `actual_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '实际采购金额',
  `contract_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '合同金额',
  `settlement_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '结算金额',
  `purchase_method` VARCHAR(50) NOT NULL COMMENT '采购方式',
  `supplier_name` VARCHAR(200) DEFAULT NULL COMMENT '中标供应商名称',
  `contact_person` VARCHAR(50) DEFAULT NULL COMMENT '供应商联系人',
  `contact_phone` VARCHAR(20) DEFAULT NULL COMMENT '供应商联系电话',
  `supplier_uscc` VARCHAR(18) DEFAULT NULL COMMENT '供应商统一信用代码',
  `supplier_bank_account` VARCHAR(50) DEFAULT NULL COMMENT '供应商银行账户',
  `apply_date` DATE NOT NULL COMMENT '采购申请日期',
  `plan_approve_date` DATE DEFAULT NULL COMMENT '采购计划批准日期',
  `announce_date` DATE DEFAULT NULL COMMENT '采购公告发布日期',
  `bid_open_date` DATE DEFAULT NULL COMMENT '开标日期',
  `contract_sign_date` DATE DEFAULT NULL COMMENT '合同签订日期',
  `expected_delivery_date` DATE DEFAULT NULL COMMENT '预计交货日期',
  `actual_delivery_date` DATE DEFAULT NULL COMMENT '实际交货日期',
  `acceptance_date` DATE DEFAULT NULL COMMENT '验收日期',
  `settlement_date` DATE DEFAULT NULL COMMENT '结算日期',
  `applicant_id` VARCHAR(7) NOT NULL COMMENT '申请人工号',
  `applicant_name` VARCHAR(50) NOT NULL COMMENT '申请人姓名',
  `apply_department` VARCHAR(100) NOT NULL COMMENT '申请部门',
  `purchase_leader_id` VARCHAR(7) DEFAULT NULL COMMENT '采购负责人工号',
  `purchase_leader_name` VARCHAR(50) DEFAULT NULL COMMENT '采购负责人姓名',
  `purchase_department` VARCHAR(100) DEFAULT NULL COMMENT '采购部门',
  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `created_by` VARCHAR(50) NOT NULL COMMENT '创建人',
  `updated_by` VARCHAR(50) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`purchase_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工采购交易信息表';
```

---

## Task 1: 创建数据库表

**Files:**

- Modify: 执行SQL创建表

**Step 1: 连接数据库并创建表**

使用MCP连接MySQL数据库:

```bash
# 使用 mcp__mysql__connect_db 连接到数据库
# 然后执行上面的CREATE TABLE语句
```

**Step 2: 验证表创建**

```sql
SHOW CREATE TABLE ccdi_purchase_transaction;
```

Expected: 表结构正确创建

**Step 3: Commit**

```bash
git add sql/
git commit -m "feat: 添加员工采购交易信息表"
```

---

## Task 2: 创建实体类

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/CcdiPurchaseTransaction.java`

**Step 1: 创建实体类**

```java
package com.ruoyi.ccdi.domain;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 员工采购交易信息对象 ccdi_purchase_transaction
 *
 * @author ruoyi
 * @date 2026-02-06
 */
@Data
public class CcdiPurchaseTransaction implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 采购事项ID */
    @TableId(type = IdType.INPUT)
    private String purchaseId;

    /** 采购类别 */
    private String purchaseCategory;

    /** 项目名称 */
    private String projectName;

    /** 标的物名称 */
    private String subjectName;

    /** 标的物描述 */
    private String subjectDesc;

    /** 采购数量 */
    private BigDecimal purchaseQty;

    /** 预算金额 */
    private BigDecimal budgetAmount;

    /** 中标金额 */
    private BigDecimal bidAmount;

    /** 实际采购金额 */
    private BigDecimal actualAmount;

    /** 合同金额 */
    private BigDecimal contractAmount;

    /** 结算金额 */
    private BigDecimal settlementAmount;

    /** 采购方式 */
    private String purchaseMethod;

    /** 中标供应商名称 */
    private String supplierName;

    /** 供应商联系人 */
    private String contactPerson;

    /** 供应商联系电话 */
    private String contactPhone;

    /** 供应商统一信用代码 */
    private String supplierUscc;

    /** 供应商银行账户 */
    private String supplierBankAccount;

    /** 采购申请日期 */
    private LocalDate applyDate;

    /** 采购计划批准日期 */
    private LocalDate planApproveDate;

    /** 采购公告发布日期 */
    private LocalDate announceDate;

    /** 开标日期 */
    private LocalDate bidOpenDate;

    /** 合同签订日期 */
    private LocalDate contractSignDate;

    /** 预计交货日期 */
    private LocalDate expectedDeliveryDate;

    /** 实际交货日期 */
    private LocalDate actualDeliveryDate;

    /** 验收日期 */
    private LocalDate acceptanceDate;

    /** 结算日期 */
    private LocalDate settlementDate;

    /** 申请人工号 */
    private String applicantId;

    /** 申请人姓名 */
    private String applicantName;

    /** 申请部门 */
    private String applyDepartment;

    /** 采购负责人工号 */
    private String purchaseLeaderId;

    /** 采购负责人姓名 */
    private String purchaseLeaderName;

    /** 采购部门 */
    private String purchaseDepartment;

    /** 创建时间 */
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    /** 更新时间 */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    /** 创建人 */
    @TableField(fill = FieldFill.INSERT)
    private String createdBy;

    /** 更新人 */
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String updatedBy;
}
```

**Step 2: 验证编译**

```bash
cd ruoyi-info-collection
mvn compile -pl . -am
```

Expected: 编译成功，无错误

**Step 3: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/CcdiPurchaseTransaction.java
git commit -m "feat: 添加采购交易信息实体类"
```

---

## Task 3: 创建查询DTO

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionQueryDTO.java`

**Step 1: 创建查询DTO**

```java
package com.ruoyi.ccdi.domain.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDate;

/**
 * 采购交易信息查询DTO
 *
 * @author ruoyi
 * @date 2026-02-06
 */
@Data
@Schema(description = "采购交易信息查询条件")
public class CcdiPurchaseTransactionQueryDTO implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    /** 项目名称 */
    @Schema(description = "项目名称")
    private String projectName;

    /** 标的物名称 */
    @Schema(description = "标的物名称")
    private String subjectName;

    /** 申请人姓名 */
    @Schema(description = "申请人姓名")
    private String applicantName;

    /** 申请人工号 */
    @Schema(description = "申请人工号")
    private String applicantId;

    /** 申请日期-开始 */
    @Schema(description = "申请日期-开始")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate applyDateStart;

    /** 申请日期-结束 */
    @Schema(description = "申请日期-结束")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate applyDateEnd;
}
```

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionQueryDTO.java
git commit -m "feat: 添加采购交易查询DTO"
```

---

## Task 4: 创建新增DTO

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionAddDTO.java`

**Step 1: 创建新增DTO（包含验证注解）**

```java
package com.ruoyi.ccdi.domain.dto;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;

/**
 * 采购交易信息新增DTO
 *
 * @author ruoyi
 * @date 2026-02-06
 */
@Data
@Schema(description = "采购交易信息新增")
public class CcdiPurchaseTransactionAddDTO implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    @Schema(description = "采购事项ID")
    @NotBlank(message = "采购事项ID不能为空")
    @Size(max = 32, message = "采购事项ID长度不能超过32个字符")
    private String purchaseId;

    @Schema(description = "采购类别")
    @NotBlank(message = "采购类别不能为空")
    @Size(max = 50, message = "采购类别长度不能超过50个字符")
    private String purchaseCategory;

    @Schema(description = "项目名称")
    @Size(max = 200, message = "项目名称长度不能超过200个字符")
    private String projectName;

    @Schema(description = "标的物名称")
    @NotBlank(message = "标的物名称不能为空")
    @Size(max = 200, message = "标的物名称长度不能超过200个字符")
    private String subjectName;

    @Schema(description = "标的物描述")
    private String subjectDesc;

    @Schema(description = "采购数量")
    @NotNull(message = "采购数量不能为空")
    @DecimalMin(value = "0.0001", message = "采购数量必须大于0")
    private BigDecimal purchaseQty;

    @Schema(description = "预算金额")
    @NotNull(message = "预算金额不能为空")
    @DecimalMin(value = "0.01", message = "预算金额必须大于0")
    private BigDecimal budgetAmount;

    @Schema(description = "中标金额")
    @DecimalMin(value = "0", message = "中标金额不能为负数")
    private BigDecimal bidAmount;

    @Schema(description = "实际采购金额")
    @DecimalMin(value = "0", message = "实际采购金额不能为负数")
    private BigDecimal actualAmount;

    @Schema(description = "合同金额")
    @DecimalMin(value = "0", message = "合同金额不能为负数")
    private BigDecimal contractAmount;

    @Schema(description = "结算金额")
    @DecimalMin(value = "0", message = "结算金额不能为负数")
    private BigDecimal settlementAmount;

    @Schema(description = "采购方式")
    @NotBlank(message = "采购方式不能为空")
    @Size(max = 50, message = "采购方式长度不能超过50个字符")
    private String purchaseMethod;

    @Schema(description = "中标供应商名称")
    @Size(max = 200, message = "中标供应商名称长度不能超过200个字符")
    private String supplierName;

    @Schema(description = "供应商联系人")
    @Size(max = 50, message = "供应商联系人长度不能超过50个字符")
    private String contactPerson;

    @Schema(description = "供应商联系电话")
    @Size(max = 20, message = "供应商联系电话长度不能超过20个字符")
    private String contactPhone;

    @Schema(description = "供应商统一信用代码")
    @Size(max = 18, message = "供应商统一信用代码长度不能超过18个字符")
    private String supplierUscc;

    @Schema(description = "供应商银行账户")
    @Size(max = 50, message = "供应商银行账户长度不能超过50个字符")
    private String supplierBankAccount;

    @Schema(description = "采购申请日期")
    @NotNull(message = "采购申请日期不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate applyDate;

    @Schema(description = "采购计划批准日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate planApproveDate;

    @Schema(description = "采购公告发布日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate announceDate;

    @Schema(description = "开标日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate bidOpenDate;

    @Schema(description = "合同签订日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate contractSignDate;

    @Schema(description = "预计交货日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate expectedDeliveryDate;

    @Schema(description = "实际交货日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate actualDeliveryDate;

    @Schema(description = "验收日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate acceptanceDate;

    @Schema(description = "结算日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate settlementDate;

    @Schema(description = "申请人工号")
    @NotBlank(message = "申请人工号不能为空")
    @Pattern(regexp = "^\\d{7}$", message = "申请人工号必须为7位数字")
    private String applicantId;

    @Schema(description = "申请人姓名")
    @NotBlank(message = "申请人姓名不能为空")
    @Size(max = 50, message = "申请人姓名长度不能超过50个字符")
    private String applicantName;

    @Schema(description = "申请部门")
    @NotBlank(message = "申请部门不能为空")
    @Size(max = 100, message = "申请部门长度不能超过100个字符")
    private String applyDepartment;

    @Schema(description = "采购负责人工号")
    @Pattern(regexp = "^\\d{7}$", message = "采购负责人工号必须为7位数字")
    private String purchaseLeaderId;

    @Schema(description = "采购负责人姓名")
    @Size(max = 50, message = "采购负责人姓名长度不能超过50个字符")
    private String purchaseLeaderName;

    @Schema(description = "采购部门")
    @Size(max = 100, message = "采购部门长度不能超过100个字符")
    private String purchaseDepartment;
}
```

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionAddDTO.java
git commit -m "feat: 添加采购交易新增DTO"
```

---

## Task 5: 创建编辑DTO

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionEditDTO.java`

**Step 1: 创建编辑DTO**

复制AddDTO的内容,修改类名和类注释,所有验证注解保持不变。

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiPurchaseTransactionEditDTO.java
git commit -m "feat: 添加采购交易编辑DTO"
```

---

## Task 6: 创建VO类

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/vo/CcdiPurchaseTransactionVO.java`

**Step 1: 创建VO类**

```java
package com.ruoyi.ccdi.domain.vo;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;

import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 采购交易信息VO
 *
 * @author ruoyi
 * @date 2026-02-06
 */
@Data
@Schema(description = "采购交易信息")
public class CcdiPurchaseTransactionVO implements Serializable {

    @Serial
    private static final long serialVersionUID = 1L;

    @Schema(description = "采购事项ID")
    private String purchaseId;

    @Schema(description = "采购类别")
    private String purchaseCategory;

    @Schema(description = "项目名称")
    private String projectName;

    @Schema(description = "标的物名称")
    private String subjectName;

    @Schema(description = "标的物描述")
    private String subjectDesc;

    @Schema(description = "采购数量")
    private BigDecimal purchaseQty;

    @Schema(description = "预算金额")
    private BigDecimal budgetAmount;

    @Schema(description = "中标金额")
    private BigDecimal bidAmount;

    @Schema(description = "实际采购金额")
    private BigDecimal actualAmount;

    @Schema(description = "合同金额")
    private BigDecimal contractAmount;

    @Schema(description = "结算金额")
    private BigDecimal settlementAmount;

    @Schema(description = "采购方式")
    private String purchaseMethod;

    @Schema(description = "中标供应商名称")
    private String supplierName;

    @Schema(description = "供应商联系人")
    private String contactPerson;

    @Schema(description = "供应商联系电话")
    private String contactPhone;

    @Schema(description = "供应商统一信用代码")
    private String supplierUscc;

    @Schema(description = "供应商银行账户")
    private String supplierBankAccount;

    @Schema(description = "采购申请日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate applyDate;

    @Schema(description = "采购计划批准日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate planApproveDate;

    @Schema(description = "采购公告发布日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate announceDate;

    @Schema(description = "开标日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate bidOpenDate;

    @Schema(description = "合同签订日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate contractSignDate;

    @Schema(description = "预计交货日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate expectedDeliveryDate;

    @Schema(description = "实际交货日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate actualDeliveryDate;

    @Schema(description = "验收日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate acceptanceDate;

    @Schema(description = "结算日期")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private LocalDate settlementDate;

    @Schema(description = "申请人工号")
    private String applicantId;

    @Schema(description = "申请人姓名")
    private String applicantName;

    @Schema(description = "申请部门")
    private String applyDepartment;

    @Schema(description = "采购负责人工号")
    private String purchaseLeaderId;

    @Schema(description = "采购负责人姓名")
    private String purchaseLeaderName;

    @Schema(description = "采购部门")
    private String purchaseDepartment;

    @Schema(description = "创建时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;

    @Schema(description = "更新时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime updateTime;

    @Schema(description = "创建人")
    private String createdBy;

    @Schema(description = "更新人")
    private String updatedBy;
}
```

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/vo/CcdiPurchaseTransactionVO.java
git commit -m "feat: 添加采购交易VO类"
```

---

## Task 7: 创建Excel导入导出类

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/excel/CcdiPurchaseTransactionExcel.java`

**Step 1: 创建Excel类**

参考 `CcdiStaffRecruitmentExcel.java`，为36个字段添加 `@ExcelProperty`、`@ColumnWidth`、`@Required` 注解。必填字段：purchaseId,
purchaseCategory, subjectName, purchaseQty, budgetAmount, purchaseMethod, applyDate, applicantId, applicantName,
applyDepartment。

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/domain/excel/CcdiPurchaseTransactionExcel.java
git commit -m "feat: 添加采购交易Excel类"
```

---

## Task 8: 创建Mapper接口

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiPurchaseTransactionMapper.java`

**Step 1: 创建Mapper接口**

```java
package com.ruoyi.ccdi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.ccdi.domain.CcdiPurchaseTransaction;
import com.ruoyi.ccdi.domain.dto.CcdiPurchaseTransactionQueryDTO;
import com.ruoyi.ccdi.domain.vo.CcdiPurchaseTransactionVO;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * 采购交易信息 数据层
 *
 * @author ruoyi
 * @date 2026-02-06
 */
public interface CcdiPurchaseTransactionMapper extends BaseMapper<CcdiPurchaseTransaction> {

    /**
     * 分页查询采购交易列表
     */
    Page<CcdiPurchaseTransactionVO> selectTransactionPage(@Param("page") Page<CcdiPurchaseTransactionVO> page,
                                                          @Param("query") CcdiPurchaseTransactionQueryDTO queryDTO);

    /**
     * 查询采购交易详情
     */
    CcdiPurchaseTransactionVO selectTransactionById(@Param("purchaseId") String purchaseId);

    /**
     * 批量插入
     */
    int insertBatch(@Param("list") List<CcdiPurchaseTransaction> list);

    /**
     * 批量更新（先删除再插入）
     */
    int insertOrUpdateBatch(@Param("list") List<CcdiPurchaseTransaction> list);
}
```

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/mapper/CcdiPurchaseTransactionMapper.java
git commit -m "feat: 添加采购交易Mapper接口"
```

---

## Task 9: 创建Mapper XML文件

**Files:**

- Create: `ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiPurchaseTransactionMapper.xml`

**Step 1: 创建XML映射文件**

参考 `CcdiStaffRecruitmentMapper.xml`，编写SQL映射:

- selectTransactionPage: 分页查询,支持项目名称、标的物名称、申请人、日期范围查询
- selectTransactionById: 根据ID查询详情
- insertBatch: 批量插入
- insertOrUpdateBatch: 批量更新(先删除再插入)

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/resources/mapper/ccdi/CcdiPurchaseTransactionMapper.xml
git commit -m "feat: 添加采购交易Mapper XML"
```

---

## Task 10: 创建Service接口

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/ICcdiPurchaseTransactionService.java`

**Step 1: 创建Service接口**

参考 `ICcdiStaffRecruitmentService.java`，定义以下方法:

- selectTransactionList
- selectTransactionPage
- selectTransactionListForExport
- selectTransactionById
- insertTransaction
- updateTransaction
- deleteTransactionByIds
- importTransaction (返回taskId)

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/ICcdiPurchaseTransactionService.java
git commit -m "feat: 添加采购交易Service接口"
```

---

## Task 11: 创建异步导入Service接口

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/ICcdiPurchaseTransactionImportService.java`

**Step 1: 创建异步导入Service接口**

参考 `ICcdiEmployeeImportService.java`，定义方法:

- importTransactionAsync
- getImportStatus
- getImportFailures

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/ICcdiPurchaseTransactionImportService.java
git commit -m "feat: 添加采购交易异步导入Service接口"
```

---

## Task 12: 创建Service实现类

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiPurchaseTransactionServiceImpl.java`

**Step 1: 创建Service实现**

参考 `CcdiStaffRecruitmentServiceImpl.java`，实现所有CRUD方法和importTransaction方法(初始化Redis状态,调用异步服务)。

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiPurchaseTransactionServiceImpl.java
git commit -m "feat: 添加采购交易Service实现"
```

---

## Task 13: 创建异步导入Service实现类

**Files:**

-
Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiPurchaseTransactionImportServiceImpl.java`

**Step 1: 创建异步导入实现**

参考 `CcdiEmployeeImportServiceImpl.java`，实现:

- @Async + @Transactional 注解
- importTransactionAsync方法(接收userName参数)
- 数据分类(新记录/更新记录/失败记录)
- 批量插入和更新
- Redis状态管理
- 数据验证逻辑

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiPurchaseTransactionImportServiceImpl.java
git commit -m "feat: 添加采购交易异步导入Service实现"
```

---

## Task 14: 创建Controller控制器

**Files:**

- Create: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/controller/CcdiPurchaseTransactionController.java`

**Step 1: 创建Controller**

参考 `CcdiStaffRecruitmentController.java` 和 `CcdiEmployeeController.java`，实现接口:

- GET /list - 分页查询
- GET /{purchaseId} - 查询详情
- POST / - 新增
- PUT / - 修改
- DELETE /{purchaseIds} - 删除
- POST /export - 导出
- POST /importTemplate - 下载模板
- POST /importData - 异步导入
- GET /importStatus/{taskId} - 查询导入状态
- GET /importFailures/{taskId} - 查询失败记录

添加完整的Swagger注解。

**Step 2: Commit**

```bash
git add ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/controller/CcdiPurchaseTransactionController.java
git commit -m "feat: 添加采购交易Controller"
```

---

## Task 15: 创建前端API文件

**Files:**

- Create: `ruoyi-ui/src/api/ccdiPurchaseTransaction.js`

**Step 1: 创建API定义**

```javascript
import request from '@/utils/request'

// 查询采购交易列表
export function listTransaction(query) {
  return request({
    url: '/ccdi/purchaseTransaction/list',
    method: 'get',
    params: query
  })
}

// 查询采购交易详情
export function getTransaction(purchaseId) {
  return request({
    url: '/ccdi/purchaseTransaction/' + purchaseId,
    method: 'get'
  })
}

// 新增采购交易
export function addTransaction(data) {
  return request({
    url: '/ccdi/purchaseTransaction',
    method: 'post',
    data: data
  })
}

// 修改采购交易
export function updateTransaction(data) {
  return request({
    url: '/ccdi/purchaseTransaction',
    method: 'put',
    data: data
  })
}

// 删除采购交易
export function delTransaction(purchaseIds) {
  return request({
    url: '/ccdi/purchaseTransaction/' + purchaseIds,
    method: 'delete'
  })
}

// 导出采购交易
export function exportTransaction(query) {
  return request({
    url: '/ccdi/purchaseTransaction/export',
    method: 'post',
    params: query
  })
}

// 下载导入模板
export function importTemplate() {
  return request({
    url: '/ccdi/purchaseTransaction/importTemplate',
    method: 'post'
  })
}

// 导入采购交易
export function importData(file, updateSupport) {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('updateSupport', updateSupport)
  return request({
    url: '/ccdi/purchaseTransaction/importData',
    method: 'post',
    data: formData
  })
}

// 查询导入状态
export function getImportStatus(taskId) {
  return request({
    url: '/ccdi/purchaseTransaction/importStatus/' + taskId,
    method: 'get'
  })
}

// 查询导入失败记录
export function getImportFailures(taskId, pageNum, pageSize) {
  return request({
    url: '/ccdi/purchaseTransaction/importFailures/' + taskId,
    method: 'get',
    params: { pageNum, pageSize }
  })
}
```

**Step 2: Commit**

```bash
git add ruoyi-ui/src/api/ccdiPurchaseTransaction.js
git commit -m "feat: 添加采购交易前端API"
```

---

## Task 16: 创建前端页面组件

**Files:**

- Create: `ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue`

**Step 1: 创建页面组件**

参考 `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`，实现:

- 查询表单(项目名称、标的物名称、申请人、日期范围)
- 列表表格(13个字段 + 操作列)
- 新增/编辑对话框
- 导入对话框(支持异步导入状态轮询)
- 删除确认

**Step 2: Commit**

```bash
git add ruoyi-ui/src/views/ccdiPurchaseTransaction/index.vue
git commit -m "feat: 添加采购交易管理页面"
```

---

## Task 17: 配置菜单和权限

**Files:**

- Modify: 数据库菜单表

**Step 1: 添加菜单数据**

执行SQL:

```sql
-- 添加采购交易管理菜单
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES
('采购交易管理', (SELECT menu_id FROM sys_menu WHERE menu_name='CCDI管理' AND parent_id=0), 5, 'purchaseTransaction', 'ccdiPurchaseTransaction/index', 1, 0, 'C', '0', '0', 'ccdi:purchaseTransaction:list', 'shopping', 'admin', NOW(), '', NULL, '采购交易信息管理菜单');

-- 获取刚插入的菜单ID
SET @menu_id = LAST_INSERT_ID();

-- 添加按钮权限
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES
('采购交易查询', @menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:query', '#', 'admin', NOW(), ''),
('采购交易新增', @menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:add', '#', 'admin', NOW(), ''),
('采购交易修改', @menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:edit', '#', 'admin', NOW(), ''),
('采购交易删除', @menu_id, 4, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:remove', '#', 'admin', NOW(), ''),
('采购交易导出', @menu_id, 5, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:export', '#', 'admin', NOW(), ''),
('采购交易导入', @menu_id, 6, '', '', 1, 0, 'F', '0', '0', 'ccdi:purchaseTransaction:import', '#', 'admin', NOW(), '');
```

**Step 2: Commit**

```bash
git add sql/
git commit -m "feat: 配置采购交易管理菜单和权限"
```

---

## Task 18: 生成测试脚本

**Files:**

- Create: `test/test_purchase_transaction_api.ps1`

**Step 1: 创建测试脚本**

参考 `test/test_employee_api.ps1`，编写完整的测试脚本:

- 登录获取token
- 测试分页查询
- 测试新增
- 测试查询详情
- 测试修改
- 测试删除
- 测试导入
- 测试导出

**Step 2: Commit**

```bash
git add test/test_purchase_transaction_api.ps1
git commit -m "test: 添加采购交易API测试脚本"
```

---

## Task 19: 运行测试并生成报告

**Step 1: 运行测试脚本**

```powershell
cd test
./test_purchase_transaction_api.ps1 > test_report_$(Get-Date -Format 'yyyyMMdd_HHmmss').txt
```

**Step 2: 验证所有接口正常**

Expected: 所有测试用例通过

**Step 3: 保存测试报告**

将测试报告保存到 `doc/test-data/purchase_transaction/` 目录。

**Step 4: Commit**

```bash
git add doc/test-data/purchase_transaction/
git commit -m "test: 添加采购交易测试报告"
```

---

## Task 20: 生成API文档

**Files:**

- Create: `doc/api/ccdi_purchase_transaction_api.md`

**Step 1: 导出Swagger API文档**

访问 http://localhost:8080/swagger-ui/index.html，导出采购交易管理的API文档。

**Step 2: 整理为Markdown格式**

整理所有接口的请求参数、响应格式、示例。

**Step 3: Commit**

```bash
git add doc/api/ccdi_purchase_transaction_api.md
git commit -m "docs: 添加采购交易API文档"
```

---

## Task 21: 最终验证和清理

**Step 1: 完整功能测试**

1. 启动后端服务
2. 启动前端服务
3. 登录系统
4. 导航到采购交易管理菜单
5. 测试所有功能:
    - 列表查询
    - 新增记录
    - 编辑记录
    - 删除记录
    - 批量删除
    - 导入功能(含异步状态轮询)
    - 导出功能

**Step 2: 代码审查**

检查:

- 所有DTO验证注解完整
- 所有Swagger注解完整
- 异常处理完善
- 日志记录完整
- 审计字段正确填充

**Step 3: 性能测试**

测试批量导入1000条数据的性能。

**Step 4: 最终Commit**

```bash
git add .
git commit -m "feat: 完成采购交易信息管理功能开发"
```

---

## 实施注意事项

1. **必填字段**: purchaseId, purchaseCategory, subjectName, purchaseQty, budgetAmount, purchaseMethod, applyDate,
   applicantId, applicantName, applyDepartment
2. **异步导入**: 使用@Async + @Transactional + Redis
3. **批量操作**: 每批500条记录
4. **更新策略**: 先批量删除，再批量插入
5. **数据验证**: Jakarta Validation + 自定义业务验证
6. **审计字段**: 使用@TableField(fill = FieldFill.INSERT/INSERT_UPDATE)
7. **错误处理**: 只返回失败记录，不返回成功记录
8. **导入轮询**: 每2秒轮询一次状态

## 参考文件

- 员工招聘信息模块: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/**/CcdiStaffRecruitment*`
- 员工异步导入: `ruoyi-info-collection/src/main/java/com/ruoyi/ccdi/service/impl/CcdiEmployeeImportServiceImpl.java`
- 前端页面: `ruoyi-ui/src/views/ccdiStaffRecruitment/index.vue`
- 测试脚本: `test/test_employee_api.ps1`
