15 KiB
Project Detail Transaction Query Backend Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Build the backend query, filter options, detail, and export endpoints for the project detail bank statement page using local ccdi_bank_statement data.
Architecture: Extend the existing ccdi-project module with a dedicated bank statement controller and service, while reusing the current CcdiBankStatementMapper plus XML dynamic SQL. Keep DTO/VO layering strict, compute direction and display amount in the query layer, and export through ruoyi-common ExcelUtil so the page and export share one query contract.
Tech Stack: Java 21, Spring Boot 3, MyBatis Plus, MyBatis XML, Mockito + JUnit 5, RuoYi ExcelUtil
Task 1: Scaffold DTO/VO contracts and the first failing service test
Files:
- Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankStatementQueryDTO.java - Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementOptionVO.java - Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementFilterOptionsVO.java - Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementListVO.java - Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementDetailVO.java - Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java - Create:
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
Step 1: Write the failing test
@ExtendWith(MockitoExtension.class)
class CcdiBankStatementServiceImplTest {
@InjectMocks
private CcdiBankStatementServiceImpl service;
@Mock
private CcdiBankStatementMapper bankStatementMapper;
@Test
void getFilterOptions_shouldReturnProjectWideOptions() {
CcdiBankStatementFilterOptionsVO options = new CcdiBankStatementFilterOptionsVO();
options.setOurSubjectOptions(List.of(new CcdiBankStatementOptionVO("主体A", "主体A")));
when(bankStatementMapper.selectFilterOptions(100L)).thenReturn(options);
CcdiBankStatementFilterOptionsVO result = service.getFilterOptions(100L);
assertEquals(1, result.getOurSubjectOptions().size());
}
}
Step 2: Run test to verify it fails
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: FAIL with missing CcdiBankStatementServiceImpl, DTO, or mapper methods.
Step 3: Write minimal implementation
public interface ICcdiBankStatementService {
CcdiBankStatementFilterOptionsVO getFilterOptions(Long projectId);
}
@Data
public class CcdiBankStatementOptionVO {
private String label;
private String value;
public CcdiBankStatementOptionVO(String label, String value) {
this.label = label;
this.value = value;
}
}
Add the remaining DTO/VO classes with only the fields already confirmed in the design.
Step 4: Run test to verify it still fails only on the missing service implementation
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: FAIL with CcdiBankStatementServiceImpl not found or method not implemented.
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/dto/CcdiBankStatementQueryDTO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementOptionVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementFilterOptionsVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementListVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/vo/CcdiBankStatementDetailVO.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
git commit -m "新增流水明细查询后端契约与测试骨架"
Task 2: Implement mapper query methods for options, list, detail, and export
Files:
- Modify:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java - Modify:
ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml - Test:
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
Step 1: Write the failing test
Add tests that assert the service delegates to mapper methods with normalized arguments:
@Test
void pageQuery_shouldNormalizeSortFieldAndDirection() {
Page<CcdiBankStatementListVO> page = new Page<>(1, 10);
CcdiBankStatementQueryDTO queryDTO = new CcdiBankStatementQueryDTO();
queryDTO.setProjectId(100L);
queryDTO.setOrderBy("amount");
queryDTO.setOrderDirection("desc");
service.selectStatementPage(page, queryDTO);
verify(bankStatementMapper).selectStatementPage(eq(page), same(queryDTO));
}
Step 2: Run test to verify it fails
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: FAIL because mapper methods selectStatementPage, selectStatementListForExport, selectStatementDetailById, selectFilterOptions are missing.
Step 3: Write minimal implementation
In CcdiBankStatementMapper.java, add:
Page<CcdiBankStatementListVO> selectStatementPage(Page<CcdiBankStatementListVO> page,
@Param("query") CcdiBankStatementQueryDTO query);
List<CcdiBankStatementListVO> selectStatementListForExport(@Param("query") CcdiBankStatementQueryDTO query);
CcdiBankStatementDetailVO selectStatementDetailById(@Param("bankStatementId") Long bankStatementId);
CcdiBankStatementFilterOptionsVO selectFilterOptions(@Param("projectId") Long projectId);
In CcdiBankStatementMapper.xml, add:
- a reusable SQL fragment for common filters
- a reusable expression for parsed transaction time
- a reusable expression for signed display amount
- separate selects for page, export, detail, and distinct project options
Step 4: Run test to verify it passes or fails only on service logic
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: mapper-signature errors gone; remaining failures only relate to unimplemented service methods.
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/CcdiBankStatementMapper.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
git commit -m "补充流水明细查询Mapper与动态SQL"
Task 3: Implement service normalization, page query, and project-wide filter options
Files:
- Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java - Modify:
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
Step 1: Write the failing test
Add focused service tests for:
- default
tabType=all - invalid
orderByfalling back totrxDate - invalid
orderDirectionfalling back todesc - blank string fields trimming to
null
@Test
void normalizeQuery_shouldFallbackToSafeDefaults() {
CcdiBankStatementQueryDTO queryDTO = new CcdiBankStatementQueryDTO();
queryDTO.setProjectId(100L);
queryDTO.setOrderBy("drop table");
queryDTO.setOrderDirection("sideways");
service.selectStatementPage(new Page<>(1, 10), queryDTO);
assertEquals("trxDate", queryDTO.getOrderBy());
assertEquals("desc", queryDTO.getOrderDirection());
assertEquals("all", queryDTO.getTabType());
}
Step 2: Run test to verify it fails
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: FAIL because service normalization logic is missing.
Step 3: Write minimal implementation
In CcdiBankStatementServiceImpl.java:
@Service
public class CcdiBankStatementServiceImpl implements ICcdiBankStatementService {
@Resource
private CcdiBankStatementMapper bankStatementMapper;
@Override
public Page<CcdiBankStatementListVO> selectStatementPage(Page<CcdiBankStatementListVO> page,
CcdiBankStatementQueryDTO queryDTO) {
normalizeQuery(queryDTO);
return bankStatementMapper.selectStatementPage(page, queryDTO);
}
}
Implement normalizeQuery(queryDTO) to enforce the white-listed sort fields and normalize blank strings and booleans before delegating to mapper.
Step 4: Run test to verify it passes
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: PASS
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
git commit -m "实现流水明细查询服务层规范化逻辑"
Task 4: Add detail lookup and export model with a failing export test
Files:
- Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiBankStatementExcel.java - Modify:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java - Modify:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java - Modify:
ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
Step 1: Write the failing test
Add a service test for export mapping:
@Test
void selectStatementListForExport_shouldMapDisplayColumns() {
CcdiBankStatementListVO row = new CcdiBankStatementListVO();
row.setTrxDate("2024-02-01 10:33:44");
row.setLeAccountNo("6222");
row.setLeAccountName("张三");
row.setDisplayAmount(new BigDecimal("-8.00"));
when(bankStatementMapper.selectStatementListForExport(any())).thenReturn(List.of(row));
List<CcdiBankStatementExcel> result = service.selectStatementListForExport(new CcdiBankStatementQueryDTO());
assertEquals(1, result.size());
assertEquals("6222", result.get(0).getLeAccountNo());
}
Step 2: Run test to verify it fails
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: FAIL because export model and export service method do not exist.
Step 3: Write minimal implementation
Create CcdiBankStatementExcel.java with @Excel annotations for:
trxDateleAccountNoleAccountNamecustomerAccountNamecustomerAccountNouserMemocashTypedisplayAmount
Add service methods:
List<CcdiBankStatementExcel> selectStatementListForExport(CcdiBankStatementQueryDTO queryDTO);
CcdiBankStatementDetailVO getStatementDetail(Long bankStatementId);
Map export rows from CcdiBankStatementListVO to CcdiBankStatementExcel.
Step 4: Run test to verify it passes
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest
Expected: PASS
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/excel/CcdiBankStatementExcel.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/ICcdiBankStatementService.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java
git commit -m "实现流水明细导出模型与详情查询"
Task 5: Add controller endpoints and the first failing controller test
Files:
- Create:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java - Create:
ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java
Step 1: Write the failing test
@ExtendWith(MockitoExtension.class)
class CcdiBankStatementControllerTest {
@InjectMocks
private CcdiBankStatementController controller;
@Mock
private ICcdiBankStatementService bankStatementService;
@Test
void options_shouldReturnAjaxResultSuccess() {
when(bankStatementService.getFilterOptions(100L)).thenReturn(new CcdiBankStatementFilterOptionsVO());
AjaxResult result = controller.getOptions(100L);
assertEquals(200, result.get("code"));
}
}
Step 2: Run test to verify it fails
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementControllerTest
Expected: FAIL because controller class and methods do not exist.
Step 3: Write minimal implementation
Add endpoints:
GET /ccdi/project/bank-statement/listGET /ccdi/project/bank-statement/optionsGET /ccdi/project/bank-statement/detail/{bankStatementId}POST /ccdi/project/bank-statement/export
Use TableSupport.buildPageRequest() for paging and ExcelUtil<CcdiBankStatementExcel> for export:
ExcelUtil<CcdiBankStatementExcel> util = new ExcelUtil<>(CcdiBankStatementExcel.class);
util.exportExcel(response, list, "流水明细");
Guard permissions with:
- query/list/detail/options:
ccdi:project:query - export:
ccdi:project:export
Step 4: Run test to verify it passes
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementControllerTest
Expected: PASS
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java
git commit -m "新增流水明细查询控制器接口"
Task 6: Verify the backend end-to-end inside the module
Files:
- Modify if needed after failures:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java - Modify if needed after failures:
ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java - Modify if needed after failures:
ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml
Step 1: Run the focused backend tests
Run: mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest,CcdiBankStatementControllerTest
Expected: PASS
Step 2: Run the module compile
Run: mvn clean compile -pl ccdi-project -am
Expected: BUILD SUCCESS
Step 3: Smoke-check the mapper XML and endpoint contracts
Run:
mvn test -pl ccdi-project -Dtest=CcdiBankStatementServiceImplTest -q
Expected: PASS without XML binding errors or missing mapper statements.
Step 4: Fix any compile or binding failures with the smallest possible patch
Typical fixes:
- wrong
@Param("query")names - missing VO field aliases in SQL
Page<>generic mismatchExcelUtilexport field annotation issues
Step 5: Commit
git add ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementController.java ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImpl.java ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/service/impl/CcdiBankStatementServiceImplTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/controller/CcdiBankStatementControllerTest.java
git commit -m "完成流水明细查询后端实现与校验"