ai-engineering-init 1.16.3 → 1.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/.claude/skills/{leniu-java-amount-handling/SKILL.md → leniu-report-scenario/references/amount-handling.md} +0 -13
  2. package/.claude/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  3. package/{.cursor/skills/leniu-mealtime/SKILL.md → .claude/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  4. package/{.codex/skills/leniu-java-report-query-param/SKILL.md → .claude/skills/leniu-report-scenario/references/query-param.md} +0 -17
  5. package/.claude/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  6. package/{.codex/skills/leniu-java-total-line/SKILL.md → .claude/skills/leniu-report-scenario/references/total-line.md} +0 -17
  7. package/.codex/skills/leniu-report-scenario/references/amount-handling.md +448 -0
  8. package/.codex/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  9. package/{.claude/skills/leniu-mealtime/SKILL.md → .codex/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  10. package/{.cursor/skills/leniu-java-report-query-param/SKILL.md → .codex/skills/leniu-report-scenario/references/query-param.md} +0 -17
  11. package/.codex/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  12. package/{.cursor/skills/leniu-java-total-line/SKILL.md → .codex/skills/leniu-report-scenario/references/total-line.md} +0 -17
  13. package/.cursor/skills/leniu-report-scenario/references/amount-handling.md +448 -0
  14. package/.cursor/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  15. package/{.codex/skills/leniu-mealtime/SKILL.md → .cursor/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  16. package/{.claude/skills/leniu-java-report-query-param/SKILL.md → .cursor/skills/leniu-report-scenario/references/query-param.md} +0 -17
  17. package/.cursor/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  18. package/{.claude/skills/leniu-java-total-line/SKILL.md → .cursor/skills/leniu-report-scenario/references/total-line.md} +0 -17
  19. package/package.json +1 -1
  20. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  21. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  22. package/.claude/skills/leniu-report-customization/SKILL.md +0 -415
  23. package/.claude/skills/leniu-report-standard-customization/SKILL.md +0 -391
  24. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  25. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  26. package/.codex/skills/leniu-report-customization/SKILL.md +0 -415
  27. package/.codex/skills/leniu-report-standard-customization/SKILL.md +0 -391
  28. package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  29. package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  30. package/.cursor/skills/leniu-report-customization/SKILL.md +0 -415
  31. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +0 -391
  32. /package/.claude/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  33. /package/.claude/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  34. /package/.claude/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
  35. /package/.codex/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  36. /package/.codex/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  37. /package/.codex/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
  38. /package/.cursor/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  39. /package/.cursor/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  40. /package/.cursor/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
@@ -1,16 +1,3 @@
1
- ---
2
- name: leniu-java-amount-handling
3
- description: |
4
- leniu-tengyun-core 项目金额处理规范。金融系统中金额以分(Long 类型)存储,展示时转换为元。
5
-
6
- 触发场景:
7
- - VO/Entity 类含金额字段(amountFen/amountYuan)
8
- - MyBatis XML 查询含金额字段
9
- - Excel 导出含金额列,需分转元
10
- - 报表合计查询含金额汇总
11
-
12
- 触发词:金额处理、分转元、元转分、Long金额、money、fen、BigDecimal金额、金额字段
13
- ---
14
1
 
15
2
  # leniu-tengyun-core 金额处理规范
16
3
 
@@ -1,20 +1,3 @@
1
- ---
2
- name: leniu-java-export
3
- description: |
4
- leniu-tengyun-core / leniu-yunshitang 项目数据导出规范。当实现数据导出功能时使用此skill,包括Excel异步导出和分页导出方案。
5
-
6
- 触发场景:
7
- - 实现Excel数据导出(exportApi.startExcelExportTaskByPage)
8
- - 实现异步导出(数据量大时)
9
- - 实现分页导出(防内存溢出)
10
- - 导出API接口设计(@PostMapping("/export"))
11
-
12
- 适用项目:
13
- - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
14
- - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
15
-
16
- 触发词:导出、Excel导出、异步导出、分页导出、@ExcelProperty、exportApi、数据导出
17
- ---
18
1
 
19
2
  # leniu-tengyun-core 数据导出规范
20
3
 
@@ -1,21 +1,3 @@
1
- ---
2
- name: leniu-mealtime
3
- description: |
4
- leniu-tengyun-core 项目餐次处理规范。当编写涉及餐次(早餐/午餐/下午茶/晚餐/夜宵)的报表或查询代码时使用。
5
-
6
- 触发场景:
7
- - 在 Param 查询参数类中添加餐次筛选字段(mealtimeTypes)
8
- - 在 VO 返回类中定义餐次字段和 MealtimeTypeConverter 转换器
9
- - 编写 MyBatis XML 中的餐次 IN 查询条件
10
- - 处理餐次枚举转换(AllocMealtimeTypeEnum)
11
- - 区分正餐(早/午/晚)与非正餐(下午茶/夜宵)
12
-
13
- 适用项目:
14
- - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
15
- - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
16
-
17
- 触发词:餐次、mealtime、mealtimeType、早餐、午餐、晚餐、下午茶、夜宵、AllocMealtimeTypeEnum
18
- ---
19
1
 
20
2
  # leniu-tengyun-core 餐次处理规范
21
3
 
@@ -1,20 +1,3 @@
1
- ---
2
- name: leniu-java-report-query-param
3
- description: |
4
- leniu-tengyun-core / leniu-yunshitang 项目报表查询入参规范。当创建报表Controller接口的查询入参Param类时使用此skill。
5
-
6
- 触发场景:
7
- - 创建报表查询入参Param类(分页、时间范围、组织筛选)
8
- - 设计报表接口的基类继承结构
9
- - 配置导出列开关(exportColumns)
10
- - 报表查询参数标准化
11
-
12
- 适用项目:
13
- - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
14
- - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
15
-
16
- 触发词:报表查询入参、Param类、分页参数、时间范围查询、ReportBaseParam、exportCols
17
- ---
18
1
 
19
2
  # leniu-tengyun-core 报表查询入参规范
20
3
 
@@ -0,0 +1,112 @@
1
+
2
+ # leniu 标准版定制报表开发指南
3
+
4
+ > 详细字段说明见 `references/table-fields.md`,经营分析详情见 `references/analysis-module.md`
5
+
6
+ ## 版本识别(必读)
7
+
8
+ **本 skill 仅适用于标准版(core-report 独立模块)**。
9
+
10
+ | 判断方式 | 标准版(本指南) | v5.29 版本 |
11
+ |---------|--------------|-----------|
12
+ | 目录结构 | `core-report/` 独立模块 | `sys-canteen/` 内嵌 |
13
+ | 退款存储 | 独立 `report_refund` 表(**正数**) | 合并入 `report_order_info`(`consumeType=2`,**负数**) |
14
+ | 第二阶段 | `fix()` 按日重算 | `batchConsume()` 增量累加 |
15
+ | consumeType | **无此字段** | 1=消费,2=退款 |
16
+
17
+ > v5.29 报表请使用 `leniu-report-customization`。钱包/交易类型枚举同 v5.29,参见该 skill。
18
+
19
+
20
+ ## 二、核心基础表概要
21
+
22
+ ### 2.1 report_order_info(仅存正向订单,无 consumeType)
23
+
24
+ 关键字段:`orderId`(主键), `canteenId/stallId`, `mealtimeType`, `orderType`, `payableAmount/realAmount/refundAmount`(分), `accPayAmount/outPayAmount`, `payTime/orderDate`, `orderRefundState`(1未退/2全退/3部分退), `status`(0未消费/1已消费), `nuClearMode`, `psnType`, `ageType/holidayType`
25
+
26
+ ### 2.2 report_order_detail(菜品明细)
27
+
28
+ 关键字段:`detailId`, `orderId`, `goodsDishesId/goodsDishesName`, `price/totalAmount/realAmount`(分), `quantity`, `salesMode`(1按份/2称重), `detailState`(1正常/2全退/3部分退), `goodsRefundNum`, `refundAmount`, `detailType`
29
+
30
+ ### 2.3 report_refund / report_refund_detail(标准版特有,退款为正数)
31
+
32
+ **report_refund**:`orderRefundId`(主键), `orderId`(原订单), `realRefundAmount`(**正数**), `applyType`(1退单/2纠错), `checkTime`
33
+
34
+ **report_refund_detail**:`orderRefundId`, `detailId`, `realQuantity`, `realRefundAmount`(**正数**)
35
+
36
+ ### 2.4 其他基础表
37
+
38
+ - **report_order_pay**:`orderId`, `payType/payChannel`, `payAmount/refundAmount`
39
+ - **report_order_discount**:`orderId`, `changeAmount`, `changeType`(1上浮/2优惠), `changeDetailType`
40
+ - **report_order_info_snapshot**:订单交易快照
41
+
42
+
43
+ ## 四、账户流水报表
44
+
45
+ ### 4.1 report_account_flow(流水主表)
46
+
47
+ 核心字段:`flowId`, `custId/orgId`, `payTime`, `flowType`(AccTradeTypeEnum), `flowRealAmount/flowAmount`, `manageCost`, `accTotalBal/accAllBal`, `status`
48
+
49
+ ### 4.2 report_account_summary(用户账户日结表)
50
+
51
+ 联合主键:`statisticDate` + `custId`。期末余额 = 期初 + 充值 - 撤销充值 + 补贴 - 撤销补贴 + 红包 + 赠送 - 消费 - 补扣 + 退款 - 提现 - 清空 - 管理费
52
+
53
+ ### 4.3 AccountConsumeService 实现
54
+
55
+ | ORDER | 类 | 汇总表 |
56
+ |-------|---|-------|
57
+ | 1/2 | Flow/FlowDetail | 基础表 |
58
+ | 13 | AccountSummary | 日结 |
59
+ | 14 | AccountOperator | 操作员 |
60
+ | 15-17 | ConsumeSummary/Org/Type | 消费维度 |
61
+ | 18 | WalletConsume | 钱包 |
62
+ | 19 | SumRechargeMer | 商户充值 |
63
+
64
+
65
+ ## 六、汇总模型速查
66
+
67
+ | 表 | 维度 | 金额 |
68
+ |----|------|------|
69
+ | report_sum_mealtime | date/canteen/stall/org/age/mealtime/psn/machine/source | custNum/consumeNum/realAmount/refundAmount |
70
+ | report_sum_pay | date/mealtime/canteen/stall/org/age/payChannel/payType | payNum/realAmount/refundAmount |
71
+ | report_sum_dishes | date/area/canteen/stall/reportOrderType/mealtime/cook/device/dishes/salesMode/detailType | quantity/realAmount |
72
+ | report_sum_pay_mer | tenantId/date/payChannel/payType | custNum/payNum/realAmount/refundAmount |
73
+
74
+
75
+ ## 八、公共模块
76
+
77
+ - **报表错误日志**(report_error_log):`reportErrorType`(1账户/2订单), `reportErrorState`(1已创建/2已处理)。定时任务 `@XxlJob("reportExceptionHandle")` 自动修复。
78
+ - **金额范围设置**:`POST /report/alloc/amount-scope/save`
79
+ - **数据修复**:`POST /summary/fix/order|account`(限31天,Redisson 锁 120 分钟)
80
+
81
+ ### 核心枚举
82
+
83
+ | 枚举 | 值 |
84
+ |------|---|
85
+ | ReportClassifyEnum | 1组织/2类别/3食堂/4设备/5收入/6渠道/7餐次 |
86
+ | ReportPayTypeEnum | 1微信/2支付宝/3系统账户/9现金/20其他 |
87
+
88
+
89
+ ## 十、开发检查清单
90
+
91
+ ### 建表
92
+ - [ ] 分组维度 + 金额汇总 + 审计字段(crby/crtime/upby/uptime/del_flag),无 tenant_id
93
+
94
+ ### 实现
95
+ - [ ] 实现 `ReportOrderConsumeService`,设 `getOrder()`
96
+ - [ ] `fix()` 先删后插(**标准版核心模式**),`consume()` 留空
97
+
98
+ ### 退款(标准版特有)
99
+ - [ ] 退款在独立 `report_refund` 表(**正数金额**)
100
+ - [ ] 净消费 = `real_amount - IFNULL(refund_amount, 0)`
101
+ - [ ] **不要使用 consumeType 字段**
102
+
103
+ ### 查询
104
+ - [ ] ReportBaseTotalVO + CompletableFuture 并行 + MgrUserAuthPO 权限
105
+ - [ ] GROUP BY / ORDER BY 表达式与 SELECT 完全一致(only_full_group_by)
106
+
107
+
108
+ ## 注意
109
+
110
+ - 标准版退款为独立表(正数金额),**不要使用 consumeType 字段**
111
+ - 标准版第二阶段用 `fix()` 按日重算,**不要使用 batchConsume() 增量模式**
112
+ - CRUD 用 `leniu-crud-development`,MyBatis 用 `leniu-java-mybatis`,入参用 `leniu-java-report-query-param`,合计行用 `leniu-java-total-line`,餐次用 `leniu-mealtime`
@@ -1,20 +1,3 @@
1
- ---
2
- name: leniu-java-total-line
3
- description: |
4
- leniu-tengyun-core / leniu-yunshitang 项目合计行查询规范。当实现报表分页查询需要合计行功能时使用此skill。
5
-
6
- 触发场景:
7
- - 实现报表分页查询合计行(Service层合计查询)
8
- - 编写Mapper XML合计SQL(只返回数值字段)
9
- - Controller层合计行数据组装
10
- - 合计查询开关控制
11
-
12
- 适用项目:
13
- - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
14
- - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
15
-
16
- 触发词:合计行、totalLine、报表合计、SUM合计、ReportBaseTotalVO、合计查询
17
- ---
18
1
 
19
2
  # leniu-tengyun-core 合计行(Total Line)规范
20
3
 
@@ -0,0 +1,448 @@
1
+
2
+ # leniu-tengyun-core 金额处理规范
3
+
4
+ ## 两种金额存储模式
5
+
6
+ 项目中存在两种金额存储模式,根据业务模块选择:
7
+
8
+ ### 模式 A:Long(分)→ BigDecimal(元)[钱包/账户模块]
9
+
10
+ 适用于余额、充值、补贴等精度要求高的模块:
11
+
12
+ - **数据库**:存储为 Long(分)
13
+ - **Entity**:使用 Long 类型
14
+ - **VO**:使用 BigDecimal 类型(MyBatis 自动转换分→元)
15
+ - **Excel 导出**:使用 `CustomNumberConverter` 转换器
16
+
17
+ ### 模式 B:BigDecimal(分)[订单/报表模块]
18
+
19
+ 适用于订单金额、报表汇总等复杂计算模块:
20
+
21
+ - **数据库**:存储为 BigDecimal(值为分,如 10000 = 100.00元)
22
+ - **Entity**:直接使用 BigDecimal 类型(以分为单位)
23
+ - **VO**:同样使用 BigDecimal(字段注释需标注单位)
24
+ - **SQL SUM**:直接 `SUM(amount)`,不需要 /100
25
+
26
+ > 参考:`OrderInfo.payableAmount`(BigDecimal,以分为单位)
27
+
28
+ ## 金额类型速查
29
+
30
+ | 类型 | 用途 | 示例 |
31
+ |------|------|------|
32
+ | `Long` | 钱包/账户存储(分) | `private Long orderAmount; // 10000 = 100.00元` |
33
+ | `BigDecimal` | 订单/报表存储(分) | `private BigDecimal payableAmount; // 10000 = 100.00元` |
34
+ | `BigDecimal` | VO 展示(元,模式A) | `private BigDecimal amount; // 100.00` |
35
+
36
+ ## Entity(模式A:Long存储)
37
+
38
+ ```java
39
+ import com.baomidou.mybatisplus.annotation.*;
40
+ import io.swagger.annotations.ApiModel;
41
+ import io.swagger.annotations.ApiModelProperty;
42
+ import lombok.Data;
43
+
44
+ @Data
45
+ @TableName(value = "wallet_table", autoResultMap = true)
46
+ public class WalletEntity {
47
+
48
+ @TableId
49
+ @ApiModelProperty(value = "钱包ID")
50
+ private Long id;
51
+
52
+ @ApiModelProperty(value = "余额(分)")
53
+ private Long balance;
54
+
55
+ @ApiModelProperty(value = "充值金额(分)")
56
+ private Long rechargeAmount;
57
+ }
58
+ ```
59
+
60
+ **要点**:
61
+ - Entity 使用 `Long` 类型存储金额(分)
62
+ - 字段注释明确标注"(分)"
63
+
64
+ ## Entity(模式B:BigDecimal存储,适用订单模块)
65
+
66
+ ```java
67
+ @Data
68
+ @TableName("order_info")
69
+ public class OrderInfo {
70
+
71
+ @ApiModelProperty(value = "应付金额(分)")
72
+ private BigDecimal payableAmount;
73
+
74
+ @ApiModelProperty(value = "实付金额(分)")
75
+ private BigDecimal realAmount;
76
+
77
+ @ApiModelProperty(value = "优惠金额(分)")
78
+ private BigDecimal discountsAmount;
79
+ }
80
+ ```
81
+
82
+ **要点**:
83
+ - 字段类型为 BigDecimal,但值以分为单位存储
84
+ - 不需要 MyBatis 类型转换
85
+
86
+ ## VO(响应层)
87
+
88
+ ```java
89
+ import com.alibaba.excel.annotation.ExcelProperty;
90
+ import io.swagger.annotations.ApiModel;
91
+ import io.swagger.annotations.ApiModelProperty;
92
+ import lombok.Data;
93
+ import lombok.experimental.Accessors;
94
+ import net.xnzn.core.common.export.converter.CustomNumberConverter;
95
+
96
+ import java.math.BigDecimal;
97
+
98
+ @Data
99
+ @Accessors(chain = true)
100
+ @ApiModel("订单信息")
101
+ public class OrderVO {
102
+
103
+ @ApiModelProperty("订单ID")
104
+ private Long id;
105
+
106
+ @ApiModelProperty("订单金额(元)")
107
+ @ExcelProperty(value = "订单金额(元)", converter = CustomNumberConverter.class)
108
+ private BigDecimal orderAmount;
109
+
110
+ @ApiModelProperty("优惠金额(元)")
111
+ @ExcelProperty(value = "优惠金额(元)", converter = CustomNumberConverter.class)
112
+ private BigDecimal discountAmount;
113
+
114
+ @ApiModelProperty("实付金额(元)")
115
+ @ExcelProperty(value = "实付金额(元)", converter = CustomNumberConverter.class)
116
+ private BigDecimal payAmount;
117
+ }
118
+ ```
119
+
120
+ **要点**:
121
+ - VO 使用 `BigDecimal` 类型(元)
122
+ - Excel 导出必须使用 `converter = CustomNumberConverter.class`
123
+ - MyBatis 会自动将 Long 分转换为 BigDecimal 元
124
+
125
+ ## MyBatis XML 查询
126
+
127
+ ### 列表查询
128
+
129
+ ```xml
130
+ <select id="listOrders" resultType="OrderVO">
131
+ SELECT
132
+ order_id,
133
+ order_amount, -- ❌ 不要 /100.0
134
+ discount_amount, -- ❌ 不要 /100.0
135
+ pay_amount -- ❌ 不要 /100.0
136
+ FROM order_table
137
+ <where>...</where>
138
+ ORDER BY crtime DESC
139
+ </select>
140
+ ```
141
+
142
+ ### 合计查询
143
+
144
+ ```xml
145
+ <select id="getOrderTotal" resultType="OrderVO">
146
+ SELECT
147
+ SUM(order_amount) AS order_amount, -- ❌ 不要 /100.0
148
+ SUM(discount_amount) AS discount_amount,-- ❌ 不要 /100.0
149
+ SUM(pay_amount) AS pay_amount -- ❌ 不要 /100.0
150
+ FROM order_table
151
+ <where>...</where>
152
+ </select>
153
+ ```
154
+
155
+ **重要**:
156
+ - 永远不要在 SQL 中使用 `/100.0`
157
+ - 合计查询只返回数值字段,不返回非数值字段(如日期、名称)
158
+
159
+ ## 金额工具类
160
+
161
+ ### AmountUtil(分转元)
162
+
163
+ ```java
164
+ import java.math.BigDecimal;
165
+ import java.math.RoundingMode;
166
+
167
+ /**
168
+ * 分与元金额转换
169
+ */
170
+ public class AmountUtil {
171
+
172
+ /**
173
+ * 分转元(String)
174
+ */
175
+ public static String fen2YuanStr(Integer fen) {
176
+ if (fen == null) {
177
+ return "0.00";
178
+ }
179
+ BigDecimal amountFen = new BigDecimal(fen);
180
+ BigDecimal amountYuan = amountFen.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
181
+ return amountYuan.toPlainString();
182
+ }
183
+
184
+ /**
185
+ * 分转元(String 重载)
186
+ */
187
+ public static String fen2YuanStr(String fen) {
188
+ if (fen == null || fen.isEmpty()) {
189
+ return "0.00";
190
+ }
191
+ BigDecimal amountFen = new BigDecimal(fen);
192
+ BigDecimal amountYuan = amountFen.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
193
+ return amountYuan.toPlainString();
194
+ }
195
+
196
+ /**
197
+ * 分转元(BigDecimal)
198
+ */
199
+ public static BigDecimal fen2Yuan(Integer fen) {
200
+ if (fen == null) {
201
+ return BigDecimal.ZERO;
202
+ }
203
+ BigDecimal amountFen = new BigDecimal(fen);
204
+ return amountFen.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
205
+ }
206
+
207
+ /**
208
+ * 分转元(Long)
209
+ */
210
+ public static BigDecimal fen2Yuan(Long fen) {
211
+ if (fen == null) {
212
+ return BigDecimal.ZERO;
213
+ }
214
+ BigDecimal amountFen = new BigDecimal(fen);
215
+ return amountFen.divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
216
+ }
217
+
218
+ /**
219
+ * 元转分
220
+ */
221
+ public static Integer yuan2Fen(String yuan) {
222
+ if (yuan == null || yuan.isEmpty()) {
223
+ return 0;
224
+ }
225
+ BigDecimal balance = new BigDecimal(yuan);
226
+ return Integer.parseInt(balance.multiply(new BigDecimal("100"))
227
+ .setScale(0, RoundingMode.HALF_UP)
228
+ .toPlainString());
229
+ }
230
+
231
+ /**
232
+ * 元转分(BigDecimal)
233
+ */
234
+ public static Integer yuan2Fen(BigDecimal yuan) {
235
+ if (yuan == null) {
236
+ return 0;
237
+ }
238
+ return yuan.multiply(new BigDecimal("100"))
239
+ .setScale(0, RoundingMode.HALF_UP)
240
+ .intValue();
241
+ }
242
+ }
243
+ ```
244
+
245
+ ### 使用示例
246
+
247
+ ```java
248
+ // 分转元
249
+ Integer fenAmount = 10000; // 10000分
250
+ BigDecimal yuanAmount = AmountUtil.fen2Yuan(fenAmount); // 100.00元
251
+
252
+ // 元转分
253
+ BigDecimal yuan = new BigDecimal("99.99");
254
+ Integer fenAmount = AmountUtil.yuan2Fen(yuan); // 9999分
255
+ ```
256
+
257
+ ## 常见场景
258
+
259
+ ### 场景1:订单报表 VO
260
+
261
+ ```java
262
+ @Data
263
+ @Accessors(chain = true)
264
+ @ApiModel("订单报表")
265
+ public class OrderReportVO {
266
+
267
+ @ExcelProperty(value = "月度", order = 1)
268
+ @ApiModelProperty("月度")
269
+ private String dateMonth;
270
+
271
+ @ExcelProperty(value = "订单总额(元)", order = 2, converter = CustomNumberConverter.class)
272
+ @ApiModelProperty("订单总额(元)")
273
+ private BigDecimal totalAmount;
274
+
275
+ @ExcelProperty(value = "订单数量", order = 3)
276
+ @ApiModelProperty("订单数量")
277
+ private Integer totalCount;
278
+
279
+ @ExcelProperty(value = "平均金额(元)", order = 4, converter = CustomNumberConverter.class)
280
+ @ApiModelProperty("平均金额(元)")
281
+ private BigDecimal avgAmount;
282
+ }
283
+ ```
284
+
285
+ ### 场景2:工资汇总 VO
286
+
287
+ ```java
288
+ @Data
289
+ @Accessors(chain = true)
290
+ @ApiModel("工资汇总")
291
+ public class SalarySummaryVO {
292
+
293
+ @ExcelProperty(value = "月度", order = 1)
294
+ @ApiModelProperty("月度")
295
+ private String dateMonth;
296
+
297
+ @ExcelProperty(value = "基本工资(元)", order = 2, converter = CustomNumberConverter.class)
298
+ @ApiModelProperty("基本工资(元)")
299
+ private BigDecimal basicSalary;
300
+
301
+ @ExcelProperty(value = "绩效工资(元)", order = 3, converter = CustomNumberConverter.class)
302
+ @ApiModelProperty("绩效工资(元)")
303
+ private BigDecimal performanceSalary;
304
+
305
+ @ExcelProperty(value = "应发工资(元)", order = 4, converter = CustomNumberConverter.class)
306
+ @ApiModelProperty("应发工资(元)")
307
+ private BigDecimal totalSalary;
308
+ }
309
+ ```
310
+
311
+ ### 场景3:合计行 SQL
312
+
313
+ ```xml
314
+ <!-- 列表查询 -->
315
+ <select id="pageSalary" resultType="SalarySummaryVO">
316
+ SELECT
317
+ date_month,
318
+ staff_count,
319
+ basic_salary,
320
+ performance_salary,
321
+ total_salary
322
+ FROM salary_summary
323
+ <where>...</where>
324
+ ORDER BY date_month DESC
325
+ </select>
326
+
327
+ <!-- 合计查询:只返回数值字段 -->
328
+ <select id="getSalaryTotal" resultType="SalarySummaryVO">
329
+ SELECT
330
+ SUM(staff_count) AS staff_count,
331
+ SUM(basic_salary) AS basic_salary,
332
+ SUM(performance_salary) AS performance_salary,
333
+ SUM(total_salary) AS total_salary
334
+ FROM salary_summary
335
+ <where>...</where>
336
+ </select>
337
+ ```
338
+
339
+ ### 场景4:平均值计算
340
+
341
+ ```xml
342
+ <!-- 平均值计算(除零处理) -->
343
+ CASE
344
+ WHEN SUM(staff_count) = 0 THEN 0
345
+ ELSE SUM(total_salary) / SUM(staff_count)
346
+ END AS avgSalary
347
+
348
+ <!-- 按维度平均 -->
349
+ CASE
350
+ WHEN SUM(staff_count) = 0 THEN 0
351
+ ELSE SUM(total_salary) / COUNT(DISTINCT tenant_id)
352
+ END AS avgSalary
353
+ ```
354
+
355
+ ## 数据流
356
+
357
+ ```
358
+ 数据库(Long/分) → MyBatis → VO(BigDecimal/元) → 前端(自动 /100)
359
+
360
+ Excel(converter /100)
361
+ ```
362
+
363
+ ## Excel 导出
364
+
365
+ ### 转换器
366
+
367
+ ```java
368
+ @ExcelProperty(value = "订单金额(元)", converter = CustomNumberConverter.class)
369
+ private BigDecimal orderAmount;
370
+ ```
371
+
372
+ ### 必要的导入
373
+
374
+ ```java
375
+ import com.alibaba.excel.annotation.ExcelProperty;
376
+ import net.xnzn.core.common.export.converter.CustomNumberConverter;
377
+ ```
378
+
379
+ ## 常见错误
380
+
381
+ ### 错误1:在 SQL 中使用 /100.0
382
+
383
+ ```xml
384
+ <!-- ❌ 错误:在 SQL 中除以 100 -->
385
+ SELECT
386
+ order_amount / 100.0 AS order_amount
387
+ FROM order_table
388
+
389
+ <!-- ✅ 正确:不进行除法,MyBatis 自动转换 -->
390
+ SELECT
391
+ order_amount AS order_amount
392
+ FROM order_table
393
+ ```
394
+
395
+ ### 错误2:钱包模块 Entity 使用 BigDecimal(模式A适用)
396
+
397
+ ```java
398
+ // ❌ 错误:钱包/账户模块 Entity 使用 BigDecimal(应用 Long)
399
+ @Data
400
+ public class WalletEntity {
401
+ @ApiModelProperty("余额(元)")
402
+ private BigDecimal balance;
403
+ }
404
+
405
+ // ✅ 正确:钱包/账户模块 Entity 使用 Long(分)
406
+ @Data
407
+ public class WalletEntity {
408
+ @ApiModelProperty("余额(分)")
409
+ private Long balance;
410
+ }
411
+ ```
412
+
413
+ > **注意**:订单模块(OrderInfo)使用 BigDecimal 存储金额(以分为单位),这是模式B,属于正确用法,不是错误。
414
+
415
+ ### 错误3:VO 不使用转换器
416
+
417
+ ```java
418
+ // ❌ 错误:Excel 导出不使用转换器
419
+ @ExcelProperty(value = "订单金额(元)")
420
+ private BigDecimal orderAmount;
421
+
422
+ // ✅ 正确:使用 CustomNumberConverter
423
+ @ExcelProperty(value = "订单金额(元)", converter = CustomNumberConverter.class)
424
+ private BigDecimal orderAmount;
425
+ ```
426
+
427
+ ### 错误4:合计查询返回非数值字段
428
+
429
+ ```xml
430
+ <!-- ❌ 错误:合计查询返回非数值字段 -->
431
+ <select id="getOrderTotal" resultType="OrderVO">
432
+ SELECT
433
+ SUM(order_amount) AS order_amount,
434
+ order_date -- ❌ 非数值字段
435
+ FROM order_table
436
+ </select>
437
+
438
+ <!-- ✅ 正确:只返回数值字段 -->
439
+ <select id="getOrderTotal" resultType="OrderVO">
440
+ SELECT
441
+ SUM(order_amount) AS order_amount
442
+ FROM order_table
443
+ </select>
444
+ ```
445
+
446
+ ## 参考文档
447
+
448
+ 详见:[leniu-tengyun-core 源码](/Users/xujiajun/Developer/gongsi_proj/core/leniu-tengyun-core)
@@ -1,20 +1,3 @@
1
- ---
2
- name: leniu-java-export
3
- description: |
4
- leniu-tengyun-core / leniu-yunshitang 项目数据导出规范。当实现数据导出功能时使用此skill,包括Excel异步导出和分页导出方案。
5
-
6
- 触发场景:
7
- - 实现Excel数据导出(exportApi.startExcelExportTaskByPage)
8
- - 实现异步导出(数据量大时)
9
- - 实现分页导出(防内存溢出)
10
- - 导出API接口设计(@PostMapping("/export"))
11
-
12
- 适用项目:
13
- - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
14
- - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
15
-
16
- 触发词:导出、Excel导出、异步导出、分页导出、@ExcelProperty、exportApi、数据导出
17
- ---
18
1
 
19
2
  # leniu-tengyun-core 数据导出规范
20
3