AgentSkillsCN

dto-implementation

HMIS 项目 DTO 实施指南。适用于创建或修改 DTO、编写 JPQL 构造函数查询、实现基于 DTO 的报表、将实体代码转换为 DTO 模式,或排查 DTO 查询相关问题。内容涵盖构造函数规则、外观模式方法、空值关系处理,以及导航模式等要点。

SKILL.md
--- frontmatter
name: dto-implementation
description: >
  DTO implementation guidelines for the HMIS project. Use when creating or modifying DTOs,
  writing JPQL constructor queries, implementing DTO-based reports, converting entity code
  to DTO patterns, or troubleshooting DTO query issues. Covers constructor rules, facade
  methods, null relationship handling, and navigation patterns.
user-invocable: true

DTO Implementation Guidelines

Critical Rules

  1. NEVER modify existing constructors - only add new ones
  2. Use direct DTO queries - avoid entity-to-DTO conversion loops
  3. JPQL PERSISTED FIELDS ONLY: NEVER use derived properties like nameWithTitle, age, displayName in JPQL

Direct DTO Query Pattern

java
// CORRECT - Direct DTO query from database
String jpql = "SELECT new com.divudi.core.data.dto.StockDTO("
    + "s.id, s.itemBatch.item.name, s.itemBatch.item.code, "
    + "s.itemBatch.retailsaleRate, s.stock, "
    + "s.itemBatch.dateOfExpire, s.itemBatch.batchNo) "
    + "FROM Stock s WHERE ...";

// MUST use findLightsByJpql() with cast
List<StockDTO> dtos = (List<StockDTO>) facade.findLightsByJpql(jpql, params, TemporalType.TIMESTAMP);
java
// WRONG - Never do entity-to-DTO loops
List<Stock> stocks = stockFacade.findByJpql(sql, params);
List<StockDTO> dtos = new ArrayList<>();
for (Stock stock : stocks) { dtos.add(new StockDTO(stock.getField1(), ...)); }

Navigation Pattern: Use IDs, Not Entities

java
// CORRECT - IDs and names for navigation
public class OpdSaleSummaryDTO {
    private Long categoryId;      // For navigation
    private String categoryName;  // For display
    private Long itemId;          // For navigation
    private String itemName;      // For display
    private Double total;
}

Null Relationship Handling

Accessing properties through nullable relationships causes silent query failures (0 results, no exception):

java
// WRONG - Fails silently if cancelledBill is null
"b.cancelledBill.createdAt"

// CORRECT - Use LEFT JOIN
"FROM Bill b LEFT JOIN b.cancelledBill cb "
// Then use cb.createdAt with COALESCE

Constructor Rules

  • Always use wrapper types (Boolean, Integer, Long) for null safety
  • Parameter count and order must match JPQL SELECT exactly
  • Keep existing constructors intact; add new ones

Common Non-Persisted Properties (Cannot Use in JPQL)

EntityNon-PersistedUse Instead
PersonnameWithTitlename (or title, name separately)
Personagedob (calculate in Java)
ItemdisplayNamename

For complete reference, read developer_docs/dto/implementation-guidelines.md.