ai-engineering-init 1.2.2 → 1.2.3

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 (55) hide show
  1. package/.claude/hooks/skill-forced-eval.js +26 -19
  2. package/.claude/skills/leniu-api-development/SKILL.md +1 -1
  3. package/.claude/skills/leniu-architecture-design/SKILL.md +1 -1
  4. package/.claude/skills/leniu-backend-annotations/SKILL.md +1 -1
  5. package/.claude/skills/leniu-brainstorm/SKILL.md +1 -1
  6. package/.claude/skills/leniu-code-patterns/SKILL.md +48 -2
  7. package/.claude/skills/leniu-crud-development/SKILL.md +1 -1
  8. package/.claude/skills/leniu-data-permission/SKILL.md +15 -0
  9. package/.claude/skills/leniu-database-ops/SKILL.md +1 -1
  10. package/.claude/skills/leniu-error-handler/SKILL.md +1 -1
  11. package/.claude/skills/leniu-java-amount-handling/SKILL.md +1 -1
  12. package/.claude/skills/leniu-java-concurrent/SKILL.md +1 -1
  13. package/.claude/skills/leniu-java-entity/SKILL.md +1 -1
  14. package/.claude/skills/leniu-java-export/SKILL.md +1 -1
  15. package/.claude/skills/leniu-java-logging/SKILL.md +1 -1
  16. package/.claude/skills/leniu-java-mq/SKILL.md +1 -1
  17. package/.claude/skills/leniu-java-mybatis/SKILL.md +1 -1
  18. package/.claude/skills/leniu-java-report-query-param/SKILL.md +1 -1
  19. package/.claude/skills/leniu-java-task/SKILL.md +1 -1
  20. package/.claude/skills/leniu-java-total-line/SKILL.md +1 -1
  21. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +1 -1
  22. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +1 -1
  23. package/.claude/skills/leniu-mealtime/SKILL.md +1 -1
  24. package/.claude/skills/leniu-redis-cache/SKILL.md +15 -0
  25. package/.claude/skills/leniu-report-customization/SKILL.md +549 -0
  26. package/.claude/skills/leniu-security-guard/SKILL.md +1 -1
  27. package/.claude/skills/leniu-utils-toolkit/SKILL.md +1 -1
  28. package/.codex/skills/leniu-api-development/SKILL.md +1 -1
  29. package/.codex/skills/leniu-architecture-design/SKILL.md +5 -1
  30. package/.codex/skills/leniu-backend-annotations/SKILL.md +4 -2
  31. package/.codex/skills/leniu-brainstorm/SKILL.md +1 -1
  32. package/.codex/skills/leniu-code-patterns/SKILL.md +48 -2
  33. package/.codex/skills/leniu-crud-development/SKILL.md +1 -1
  34. package/.codex/skills/leniu-data-permission/SKILL.md +15 -0
  35. package/.codex/skills/leniu-database-ops/SKILL.md +1 -1
  36. package/.codex/skills/leniu-error-handler/SKILL.md +1 -1
  37. package/.codex/skills/leniu-java-amount-handling/SKILL.md +1 -1
  38. package/.codex/skills/leniu-java-concurrent/SKILL.md +1 -1
  39. package/.codex/skills/leniu-java-entity/SKILL.md +1 -1
  40. package/.codex/skills/leniu-java-export/SKILL.md +24 -23
  41. package/.codex/skills/leniu-java-logging/SKILL.md +1 -1
  42. package/.codex/skills/leniu-java-mq/SKILL.md +1 -1
  43. package/.codex/skills/leniu-java-mybatis/SKILL.md +1 -1
  44. package/.codex/skills/leniu-java-report-query-param/SKILL.md +1 -1
  45. package/.codex/skills/leniu-java-task/SKILL.md +1 -1
  46. package/.codex/skills/leniu-java-total-line/SKILL.md +21 -20
  47. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +1 -1
  48. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +1 -1
  49. package/.codex/skills/leniu-mealtime/SKILL.md +1 -1
  50. package/.codex/skills/leniu-redis-cache/SKILL.md +15 -0
  51. package/.codex/skills/leniu-report-customization/SKILL.md +549 -0
  52. package/.codex/skills/leniu-security-guard/SKILL.md +1 -1
  53. package/.codex/skills/leniu-utils-toolkit/SKILL.md +4 -2
  54. package/AGENTS.md +26 -26
  55. package/package.json +1 -1
@@ -0,0 +1,549 @@
1
+ ---
2
+ name: leniu-report-customization
3
+ description: |
4
+ leniu-tengyun-core 项目定制报表开发指南。基于 report_order_info / report_order_detail / report_account_flow 等报表基础表实现汇总报表。
5
+
6
+ 触发场景:
7
+ - 基于订单数据实现定制汇总报表
8
+ - 基于账户流水实现定制汇总报表
9
+ - 处理报表中的退款数据(正向/逆向/部分退)
10
+ - 实现报表的 MQ 消费逻辑和 fix 初始化逻辑
11
+ - 报表金额计算(消费金额减退款)
12
+
13
+ 触发词:定制报表、汇总报表、report_order_info、report_order_detail、report_account_flow、退款汇总、消费金额统计、订单报表、流水报表
14
+ ---
15
+
16
+ # leniu 定制报表开发指南
17
+
18
+ ## 概述
19
+
20
+ 本项目的定制报表基于**报表基础表**(由 MQ 消息写入)进行二次汇总。核心数据源有两类:
21
+ 1. **订单类**:`report_order_info` + `report_order_detail`(消费/退款数据)
22
+ 2. **账户流水类**:`report_account_flow` + `report_account_flow_detail`(钱包变动数据)
23
+
24
+ 定制报表的本质是:从基础表中按维度聚合数据,写入自定义汇总表。
25
+
26
+ ---
27
+
28
+ ## 一、报表系统架构
29
+
30
+ ### 1.1 两阶段消费模型
31
+
32
+ ```
33
+ MQ 消息到达(下单/退款)
34
+
35
+ 第一阶段(order < 10,同步写基础表)
36
+ ├── ORDER=1 ReportOrderInfoService → report_order_info
37
+ ├── ORDER=3 ReportOrderDetailService → report_order_detail
38
+ └── ORDER=5 ReportRefundService → report_refund / report_refund_detail
39
+
40
+ 第二阶段(order >= 10,批量写汇总表,由 Redis 计数触发)
41
+ ├── ORDER=11 ReportSumCanteenService → report_sum_canteen(食堂汇总)
42
+ └── ORDER=3x ReportSumDishesService → 菜品销售汇总
43
+ ```
44
+
45
+ ### 1.2 MQ 消息类型
46
+
47
+ | 消息 | Topic/Tag | 监听器 |
48
+ |------|-----------|--------|
49
+ | 下单 | `order / order-v3-placed` | `ReportOrderMQListener` |
50
+ | 退款 | `order / order-v3-refunded` | `ReportOrderRefundMQListener` |
51
+
52
+ ### 1.3 触发汇总消费的机制
53
+
54
+ Redis 计数器 `ORDER_REPORT_COUNT_KEY:{merchantId}`,每来一条 MQ 消息递减,达到阈值时异步触发 `consumeOrderReport()`,从 `report_order_info` 中查出 `status=0`(未消费)的数据,批量调用所有 `order >= 10` 的 Service 的 `batchConsume()`。
55
+
56
+ ---
57
+
58
+ ## 二、核心基础表字段说明
59
+
60
+ ### 2.1 report_order_info(报表订单主表)
61
+
62
+ > **关键**:v5.29 后,正向下单和逆向退款的数据**都存在这张表中**,通过 `consumeType` 区分。
63
+
64
+ | 字段 | 类型 | 说明 |
65
+ |------|------|------|
66
+ | `orderId` | Long | 主键。正向=订单ID,退款=退款单ID |
67
+ | `relationOrderId` | Long | 关联订单ID(退款记录指向原始订单) |
68
+ | `consumeType` | Integer | **1=消费,2=退款** |
69
+ | `orderRefundState` | Integer | **1=未退单,2=已退单(全退),3=部分退单** |
70
+ | `payableAmount` | BigDecimal | 应付金额(分) |
71
+ | `realAmount` | BigDecimal | 实付金额(分) |
72
+ | `refundAmount` | BigDecimal | 累计退款金额(分) |
73
+ | `realRefundAmount` | BigDecimal | 实际退款金额(分,退款记录为负数) |
74
+ | `walletAmount` | BigDecimal | 个人钱包支付金额(分) |
75
+ | `subsidyAmount` | BigDecimal | 补贴钱包支付金额(分) |
76
+ | `redEnvelopeAmount` | BigDecimal | 红包支付金额(分) |
77
+ | `accPayAmount` | BigDecimal | 账户支付金额(分) |
78
+ | `outPayAmount` | BigDecimal | 外部支付金额(分) |
79
+ | `payTime` | LocalDateTime | 支付时间(退款=审核时间) |
80
+ | `orderTime` | LocalDateTime | 下单时间 |
81
+ | `orderDate` | LocalDate | 就餐日期 |
82
+ | `status` | Integer | 消费状态:0=未消费,1=已消费 |
83
+ | `mealtimeType` | Integer | 餐次类型 |
84
+ | `orderType` | Integer | 订单类型(1当餐/2预订/3报餐/4扫码/5餐桌/6自助/11商城/12超市/21补扣/22外部) |
85
+ | `canteenId/canteenName` | Long/String | 食堂 |
86
+ | `stallId/stallName` | Long/String | 档口 |
87
+ | `areaId/areaName` | Long/String | 区域 |
88
+ | `custId/custName/custNum` | - | 用户信息 |
89
+ | `orgId/orgFullId/orgName/orgFullName` | - | 组织信息 |
90
+ | `payType` | Integer | 支付方式 |
91
+ | `payChannel` | Integer | 支付渠道 |
92
+ | `nuClearMode` | Integer | 核身方式(1刷卡/2刷脸/3扫码) |
93
+ | `sourceType` | Integer | 来源类型 |
94
+ | `ifOnline` | Integer | 是否在线订单(1是/2否) |
95
+ | `psnType/psnTypeName` | Integer/String | 用户类别 |
96
+ | `personType` | Integer | 人员归类(1职工/2患者/3陪护/4其他,医院版) |
97
+
98
+ ### 2.2 report_order_detail(报表订单菜品明细表)
99
+
100
+ | 字段 | 类型 | 说明 |
101
+ |------|------|------|
102
+ | `detailId` | Long | 明细主键 |
103
+ | `orderId` | Long | 关联订单ID |
104
+ | `goodsDishesId/goodsDishesName` | Long/String | 菜品信息 |
105
+ | `salePrice` | BigDecimal | 售卖价格(分) |
106
+ | `prefPrice` | BigDecimal | 优惠价格(分) |
107
+ | `price` | BigDecimal | 计算价格(分,最终价格) |
108
+ | `quantity` | Integer | 数量/重量 |
109
+ | `totalAmount` | BigDecimal | 订单详情总金额(分) |
110
+ | `realAmount` | BigDecimal | 实际付款金额(分) |
111
+ | `detailState` | Integer | **1=正常,2=已退菜(全退),3=部分退菜** |
112
+ | `goodsRefundNum` | Integer | 商品已退数量 |
113
+ | `refundAmount` | BigDecimal | 商品退款金额(分) |
114
+ | `detailType` | Integer | 明细类别(1菜品/2套餐/3商品/4按键/5补扣/6报餐) |
115
+ | `costPrice` | BigDecimal | 成本价格 |
116
+
117
+ ### 2.3 report_account_flow(账户变动流水主表)
118
+
119
+ | 字段 | 类型 | 说明 |
120
+ |------|------|------|
121
+ | `flowId` | Long | 主键(雪花ID) |
122
+ | `custId` | Long | 人员ID |
123
+ | `flowType` | Integer | 交易类型(AccTradeTypeEnum) |
124
+ | `flowRealAmount` | BigDecimal | 实际交易金额 |
125
+ | `flowAmount` | BigDecimal | 交易金额 |
126
+ | `accTotalBal` | BigDecimal | 所有钱包可用余额之和(不含冻结) |
127
+ | `accAllBal` | BigDecimal | 所有钱包总余额(含冻结) |
128
+ | `payTime` | LocalDateTime | 支付时间 |
129
+ | `ordTime` | LocalDateTime | 订单时间 |
130
+ | `status` | Integer | 消费状态:0=未消费,1=已消费 |
131
+ | `manageCost` | BigDecimal | 管理费 |
132
+ | `rechargeSource` | Integer | 充值来源 |
133
+ | `payChannel` | Integer | 支付渠道 |
134
+ | `payType` | Integer | 支付方式 |
135
+
136
+ > 注意:`custName`、`mobile`、`mobileSuffix` 使用 SM4 加密存储。
137
+
138
+ ### 2.4 report_account_flow_detail(账户流水钱包明细表)
139
+
140
+ | 字段 | 类型 | 说明 |
141
+ |------|------|------|
142
+ | `id` | Long | 自增主键 |
143
+ | `flowId` | Long | 关联主流水 |
144
+ | `walletId` | Long | 钱包类型(AccWalletIdEnum) |
145
+ | `flowType` | Integer | 交易类型 |
146
+ | `amount` | BigDecimal | 本钱包支付金额(转出类型取负值) |
147
+ | `walletBal` | BigDecimal | 本钱包可用余额 |
148
+ | `allWalletBal` | BigDecimal | 本钱包总余额(含冻结) |
149
+ | `frozenBalance` | BigDecimal | 冻结余额 |
150
+ | `payTime` | LocalDateTime | 支付时间 |
151
+
152
+ ---
153
+
154
+ ## 三、退款数据处理(核心重点)
155
+
156
+ ### 3.1 退款数据在 report_order_info 中的表现
157
+
158
+ 退款记录作为**独立行**写入 `report_order_info`:
159
+
160
+ ```
161
+ 正向订单:orderId=订单ID, consumeType=1, orderRefundState=1
162
+ 退款记录:orderId=退款单ID, consumeType=2, relationOrderId=原始订单ID
163
+ 金额字段全部为负数(realRefundAmount、walletAmount、subsidyAmount、redEnvelopeAmount 都乘以 -1)
164
+ payTime = 退款审核时间(checkTime)
165
+ ```
166
+
167
+ ### 3.2 orderRefundState 的三种状态
168
+
169
+ | 值 | 含义 | 说明 |
170
+ |----|------|------|
171
+ | 1 | 未退单 | 正常消费,无退款 |
172
+ | 2 | 已退单(全退) | 整单退款 |
173
+ | 3 | 部分退单 | 部分菜品退款 |
174
+
175
+ ### 3.3 汇总报表中计算"净消费金额"的标准模式
176
+
177
+ **方式一:直接利用 consumeType 和负数金额**(推荐)
178
+
179
+ 因为退款记录的金额已经是负数,所以直接 SUM 即可得到净额:
180
+
181
+ ```sql
182
+ -- 净消费金额 = SUM(所有记录的 realAmount)
183
+ -- 因为:consumeType=1 的 realAmount 为正,consumeType=2 的相关金额为负
184
+ SELECT
185
+ SUM(real_amount) AS netAmount, -- 净实付
186
+ SUM(wallet_amount) AS netWalletAmount, -- 净个人钱包消费
187
+ SUM(subsidy_amount) AS netSubsidyAmount -- 净补贴消费
188
+ FROM report_order_info
189
+ WHERE pay_time BETWEEN #{startTime} AND #{endTime}
190
+ ```
191
+
192
+ **方式二:分别统计消费和退款**
193
+
194
+ ```sql
195
+ -- 消费总额(仅正向订单)
196
+ SELECT SUM(real_amount) FROM report_order_info
197
+ WHERE consume_type = 1
198
+ AND pay_time BETWEEN #{startTime} AND #{endTime}
199
+
200
+ -- 退款总额(仅退款记录,金额为负数,取绝对值)
201
+ SELECT SUM(ABS(real_refund_amount)) FROM report_order_info
202
+ WHERE consume_type = 2
203
+ AND pay_time BETWEEN #{startTime} AND #{endTime}
204
+
205
+ -- 净额 = 消费总额 - 退款总额
206
+ ```
207
+
208
+ **方式三:排除全退订单,处理部分退**
209
+
210
+ ```sql
211
+ -- 排除全退订单,只统计有效消费
212
+ SELECT SUM(real_amount - IFNULL(refund_amount, 0)) AS netAmount
213
+ FROM report_order_info
214
+ WHERE consume_type = 1
215
+ AND order_refund_state IN (1, 3) -- 未退单 + 部分退单
216
+ AND pay_time BETWEEN #{startTime} AND #{endTime}
217
+ ```
218
+
219
+ ### 3.4 report_order_detail 的退款处理
220
+
221
+ ```
222
+ detailState=1(正常):quantity 为原始购买数量
223
+ detailState=2(已退菜全退):该菜品已完全退款
224
+ detailState=3(部分退菜):goodsRefundNum 为已退数量,refundAmount 为退款金额
225
+
226
+ 菜品净销量 = quantity - IFNULL(goods_refund_num, 0)
227
+ 菜品净金额 = total_amount - IFNULL(refund_amount, 0)
228
+ ```
229
+
230
+ ---
231
+
232
+ ## 四、钱包与交易类型枚举
233
+
234
+ ### 4.1 AccWalletIdEnum(钱包类型)
235
+
236
+ | key | 枚举 | 含义 |
237
+ |-----|------|------|
238
+ | 1 | WALLET | 个人钱包 |
239
+ | 2 | SUBSIDY | 补贴钱包 |
240
+ | 4 | LUCK_MONEY | 红包 |
241
+
242
+ ### 4.2 AccTradeTypeEnum(交易类型)
243
+
244
+ | key | 枚举 | 含义 | 金额方向 |
245
+ |-----|------|------|---------|
246
+ | 10 | RECHARGE | 充值 | 正(收入) |
247
+ | 11 | RECHARGE_GIFT | 赠送 | 正(收入) |
248
+ | 12 | REVOKE_RECHARGE_GIFT | 撤销赠送 | **负** |
249
+ | 20 | SUBSIDY | 补贴 | 正(收入) |
250
+ | 30 | WITHDRAW | 提现 | **负** |
251
+ | 40 | REVOKE_RECHARGE | 撤销充值 | **负** |
252
+ | 50 | REVOKE_SUBSIDY | 撤销补贴 | **负** |
253
+ | 60 | TRANSFER_OUT | 转出 | **负** |
254
+ | 70 | TRANSFER_IN | 转入 | 正(收入) |
255
+ | 80 | FREEZE | 冻结 | **负** |
256
+ | 90 | UN_FREEZE | 解冻 | 正(收入) |
257
+ | 100 | CLEAR | 补贴清空 | **负** |
258
+ | 110 | CONSUME | 消费 | **负** |
259
+ | 120 | CONSUME_REPAIR | 消费补扣 | **负** |
260
+ | 130 | CONSUME_REFUND | 消费退款 | 正(收入) |
261
+ | 131 | CONSUME_WITHHOLD | 账户预扣 | **负** |
262
+ | 132 | CONSUME_WITHHOLD_REFUND | 账户预扣退款 | 正(收入) |
263
+ | 140 | LUCK_MONEY | 红包 | 正(收入) |
264
+ | 141 | REVOKE_LUCK_MONEY | 撤销红包 | **负** |
265
+ | 142 | CLEAR_LUCK_MONEY | 红包清空 | **负** |
266
+
267
+ > `getConvertFlag()` 方法标记了需要转为负数显示的类型。
268
+
269
+ ### 4.3 账户流水报表常用过滤
270
+
271
+ ```java
272
+ // 统计某钱包的消费金额
273
+ // 从 report_account_flow_detail 过滤
274
+ WHERE wallet_id = #{AccWalletIdEnum.WALLET.key} // 个人钱包
275
+ AND flow_type = #{AccTradeTypeEnum.CONSUME.key} // 消费类型
276
+
277
+ // 统计消费+退款净额
278
+ WHERE wallet_id = #{walletId}
279
+ AND flow_type IN (110, 130) // CONSUME + CONSUME_REFUND
280
+ // amount 字段:消费为负,退款为正 → SUM 即为净消费额(负值)
281
+ ```
282
+
283
+ ---
284
+
285
+ ## 五、汇总表开发标准模式
286
+
287
+ ### 5.1 实现 ReportOrderConsumeService 接口
288
+
289
+ ```java
290
+ @Service
291
+ @Slf4j
292
+ public class ReportSumXxxService implements ReportOrderConsumeService {
293
+
294
+ @Override
295
+ public int getOrder() {
296
+ // < 10: 基础表(同步写入)
297
+ // >= 10 且 < 30: 普通汇总表(批量消费)
298
+ // >= 30: 菜品汇总表(需要 detail 数据)
299
+ return 15; // 自定义汇总表一般用 10-29
300
+ }
301
+
302
+ /**
303
+ * 单条消费(第一阶段,一般汇总表不用实现)
304
+ */
305
+ @Override
306
+ public void consume(OrderChangePO payload, ReportOrderInfoDTO baseInfo) {
307
+ // 汇总表通常留空,仅基础表实现
308
+ }
309
+
310
+ /**
311
+ * 批量消费(第二阶段,核心实现)
312
+ */
313
+ @Override
314
+ public void batchConsume(List<ReportOrderConsumeDTO> list) {
315
+ // 1. 按分组维度聚合(日期+食堂+档口+...)
316
+ // 2. 查询汇总表是否已有记录
317
+ // - 不存在 → INSERT
318
+ // - 存在 → UPDATE(累加金额)
319
+ // 3. 批量写入
320
+ }
321
+
322
+ /**
323
+ * fix 方法(数据修复/重新统计)
324
+ */
325
+ @Override
326
+ public void fix(ReportBaseParam param) {
327
+ // 1. 删除时间范围内的旧汇总数据
328
+ // 2. 从 report_order_info 重新聚合写入
329
+ }
330
+ }
331
+ ```
332
+
333
+ ### 5.2 batchConsume 标准实现模板
334
+
335
+ ```java
336
+ @Override
337
+ public void batchConsume(List<ReportOrderConsumeDTO> list) {
338
+ // 1. 构建分组 Key(按需选择维度)
339
+ Map<String, List<ReportOrderConsumeDTO>> grouped = list.stream()
340
+ .collect(Collectors.groupingBy(e ->
341
+ e.getStatisticDate() + "_" + e.getCanteenId() + "_" + e.getStallId()
342
+ ));
343
+
344
+ List<ReportSumXxx> insertList = new ArrayList<>();
345
+ List<ReportSumXxx> updateList = new ArrayList<>();
346
+
347
+ for (Map.Entry<String, List<ReportOrderConsumeDTO>> entry : grouped.entrySet()) {
348
+ List<ReportOrderConsumeDTO> items = entry.getValue();
349
+ ReportOrderConsumeDTO first = items.get(0);
350
+
351
+ // 2. 查询是否已存在
352
+ ReportSumXxx existing = mapper.selectOne(Wrappers.<ReportSumXxx>lambdaQuery()
353
+ .eq(ReportSumXxx::getStatisticDate, first.getStatisticDate())
354
+ .eq(ReportSumXxx::getCanteenId, first.getCanteenId())
355
+ .eq(ReportSumXxx::getStallId, first.getStallId()));
356
+
357
+ if (existing == null) {
358
+ ReportSumXxx record = new ReportSumXxx();
359
+ record.setId(Id.next());
360
+ // 设置维度字段...
361
+ // 累加金额字段...
362
+ insertList.add(record);
363
+ } else {
364
+ // 在现有记录上累加
365
+ // existing.setXxxAmount(existing.getXxxAmount().add(deltaAmount));
366
+ updateList.add(existing);
367
+ }
368
+ }
369
+
370
+ if (CollUtil.isNotEmpty(insertList)) {
371
+ // 批量插入
372
+ }
373
+ if (CollUtil.isNotEmpty(updateList)) {
374
+ // 批量更新
375
+ }
376
+ }
377
+ ```
378
+
379
+ ### 5.3 fix 方法标准模板
380
+
381
+ ```java
382
+ @Override
383
+ public void fix(ReportBaseParam param) {
384
+ LocalDateTime startTime = param.getStartTime();
385
+ LocalDateTime endTime = param.getEndTime();
386
+
387
+ // 1. 删除时间范围内的旧数据(先删后插)
388
+ mapper.delete(Wrappers.<ReportSumXxx>lambdaQuery()
389
+ .between(ReportSumXxx::getStatisticDate,
390
+ startTime.toLocalDate(), endTime.toLocalDate()));
391
+
392
+ // 2. 从 report_order_info 聚合初始化(推荐用 SQL 直接聚合)
393
+ mapper.initFix(null, startTime, endTime);
394
+ }
395
+ ```
396
+
397
+ ### 5.4 fix SQL 模板(MyBatis XML)
398
+
399
+ ```xml
400
+ <insert id="initFix">
401
+ INSERT INTO report_sum_xxx (id, statistic_date, canteen_id, canteen_name,
402
+ order_count, consume_amount, refund_amount, net_amount)
403
+ SELECT
404
+ #{id}, <!-- 或用数据库函数生成 -->
405
+ DATE(pay_time) AS statistic_date,
406
+ canteen_id,
407
+ canteen_name,
408
+ COUNT(*) AS order_count,
409
+ SUM(CASE WHEN consume_type = 1 THEN real_amount ELSE 0 END) AS consume_amount,
410
+ SUM(CASE WHEN consume_type = 2 THEN ABS(real_refund_amount) ELSE 0 END) AS refund_amount,
411
+ SUM(real_amount) + SUM(IFNULL(real_refund_amount, 0)) AS net_amount
412
+ FROM report_order_info
413
+ WHERE pay_time BETWEEN #{startTime} AND #{endTime}
414
+ GROUP BY DATE(pay_time), canteen_id, canteen_name
415
+ </insert>
416
+ ```
417
+
418
+ ---
419
+
420
+ ## 六、查询接口标准模式
421
+
422
+ ### 6.1 三并行 CompletableFuture 模式
423
+
424
+ ```java
425
+ public PageVO<XxxVO> pageSummary(XxxParam param) {
426
+ // 三个异步查询并行执行
427
+ CompletableFuture<Long> countFuture = CompletableFuture.supplyAsync(() ->
428
+ mapper.selectCount(param));
429
+
430
+ CompletableFuture<List<XxxVO>> listFuture = CompletableFuture.supplyAsync(() ->
431
+ mapper.selectPageList(param));
432
+
433
+ CompletableFuture<XxxTotalVO> totalFuture = CompletableFuture.supplyAsync(() ->
434
+ mapper.selectTotal(param));
435
+
436
+ CompletableFuture.allOf(countFuture, listFuture, totalFuture).join();
437
+
438
+ PageVO<XxxVO> pageVO = new PageVO<>();
439
+ pageVO.setTotal(countFuture.join());
440
+ pageVO.setList(listFuture.join());
441
+ pageVO.setTotalLine(totalFuture.join()); // 合计行
442
+ return pageVO;
443
+ }
444
+ ```
445
+
446
+ ---
447
+
448
+ ## 七、账户流水汇总报表模式
449
+
450
+ ### 7.1 基于 report_account_flow 的汇总
451
+
452
+ 与订单报表模式相同,但数据源不同:
453
+
454
+ ```java
455
+ // 实现 ReportAccountConsumeService 接口(类似 ReportOrderConsumeService)
456
+ // batchConsume 从 report_account_flow 查 status=0 的数据
457
+ // fix 从 report_account_flow 重新聚合
458
+ ```
459
+
460
+ ### 7.2 常见统计维度
461
+
462
+ ```sql
463
+ -- 按钱包类型统计消费金额
464
+ SELECT
465
+ d.wallet_id,
466
+ SUM(CASE WHEN d.flow_type = 110 THEN ABS(d.amount) ELSE 0 END) AS consume_amount,
467
+ SUM(CASE WHEN d.flow_type = 130 THEN d.amount ELSE 0 END) AS refund_amount
468
+ FROM report_account_flow f
469
+ JOIN report_account_flow_detail d ON f.flow_id = d.flow_id
470
+ WHERE f.pay_time BETWEEN #{startTime} AND #{endTime}
471
+ GROUP BY d.wallet_id
472
+
473
+ -- 按交易类型统计
474
+ SELECT
475
+ f.flow_type,
476
+ SUM(f.flow_real_amount) AS total_amount,
477
+ COUNT(*) AS total_count
478
+ FROM report_account_flow f
479
+ WHERE f.pay_time BETWEEN #{startTime} AND #{endTime}
480
+ GROUP BY f.flow_type
481
+ ```
482
+
483
+ ### 7.3 退款在账户流水中的体现
484
+
485
+ ```
486
+ 消费:flowType=110 (CONSUME),amount 为负
487
+ 退款:flowType=130 (CONSUME_REFUND),amount 为正
488
+
489
+ 净消费 = ABS(SUM(CONSUME 的 amount)) - SUM(CONSUME_REFUND 的 amount)
490
+ 或直接:ABS(SUM(flowType IN (110, 130) 的 amount))
491
+ ```
492
+
493
+ ---
494
+
495
+ ## 八、开发检查清单
496
+
497
+ 新建定制汇总报表时,逐项确认:
498
+
499
+ ### 建表
500
+ - [ ] 汇总表包含分组维度字段(statisticDate、canteenId、stallId 等)
501
+ - [ ] 汇总表包含金额汇总字段(consumeAmount、refundAmount、netAmount 等)
502
+ - [ ] 审计字段(crby/crtime/upby/uptime/del_flag)
503
+ - [ ] 无 tenant_id(双库物理隔离)
504
+
505
+ ### 实现
506
+ - [ ] 实现 `ReportOrderConsumeService`(或 `ReportAccountConsumeService`)接口
507
+ - [ ] 设置合理的 `getOrder()` 值(10-29 普通汇总,30+ 菜品汇总)
508
+ - [ ] 实现 `batchConsume()` — 分组 → 查存量 → 累加/新建
509
+ - [ ] 实现 `fix()` — 先删后插,从基础表重新聚合
510
+
511
+ ### 退款处理
512
+ - [ ] 确认是否需要处理退款数据
513
+ - [ ] 选择退款计算方式(直接 SUM 负数 / 分别统计 / 排除全退)
514
+ - [ ] 菜品级别退款需关注 `detailState` 和 `goodsRefundNum`
515
+
516
+ ### 查询接口
517
+ - [ ] 分页 + 合计行模式(PageVO + TotalVO)
518
+ - [ ] 三并行 CompletableFuture(count + list + total)
519
+ - [ ] 导出功能(如需要)
520
+
521
+ ---
522
+
523
+ ## 九、关键代码位置
524
+
525
+ | 类型 | 路径 |
526
+ |------|------|
527
+ | 下单 MQ 监听器 | `sys-canteen/.../report/statistics/config/mq/ReportOrderMQListener.java` |
528
+ | 退款 MQ 监听器 | `sys-canteen/.../report/statistics/config/mq/ReportOrderRefundMQListener.java` |
529
+ | 汇总消费调度 | `sys-canteen/.../report/statistics/config/mq/service/ReportConsumerService.java` |
530
+ | ConsumeService 接口 | `sys-canteen/.../report/statistics/config/mq/ReportOrderConsumeService.java` |
531
+ | 食堂汇总参考实现 | `sys-canteen/.../report/statistics/order/summary/service/ReportSumCanteenService.java` |
532
+ | Fix Controller | `sys-canteen/.../report/statistics/order/fix/controller/ReportFixController.java` |
533
+ | Fix Service | `sys-canteen/.../report/statistics/order/fix/service/ReportFixService.java` |
534
+ | ReportOrderInfo 实体 | `sys-canteen/.../report/statistics/order/basic/model/ReportOrderInfo.java` |
535
+ | ReportOrderDetail 实体 | `sys-canteen/.../report/statistics/order/basic/model/ReportOrderDetail.java` |
536
+ | ReportAccountFlow 实体 | `sys-canteen/.../report/statistics/account/model/ReportAccountFlow.java` |
537
+ | ReportAccountFlowDetail 实体 | `sys-canteen/.../report/statistics/account/model/ReportAccountFlowDetail.java` |
538
+ | AccTradeTypeEnum | `sys-canteen/.../account/v3/constants/AccTradeTypeEnum.java` |
539
+ | AccWalletIdEnum | `sys-canteen/.../account/v3/constants/AccWalletIdEnum.java` |
540
+
541
+ ---
542
+
543
+ ## 注意
544
+
545
+ - 如果是 CRUD 增删改查开发(非报表),请使用 `leniu-crud-development`
546
+ - 如果涉及 MyBatis XML 编写规范,请使用 `leniu-java-mybatis`
547
+ - 如果涉及报表查询入参 Param 类设计,请使用 `leniu-java-report-query-param`
548
+ - 如果涉及合计行实现,请使用 `leniu-java-total-line`
549
+ - 如果涉及餐次过滤,请使用 `leniu-mealtime`
@@ -14,7 +14,7 @@ description: |
14
14
  - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
15
15
  - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
16
16
 
17
- 触发词:leniu-安全、leniu-认证、leniu-@RequiresAuthentication、leniu-@RequiresGuest、leniu-SQL注入、leniu-XSS、leniu-数据脱敏、leniu-限流、net.xnzn、leniu-yunshitang
17
+ 触发词:安全认证、SQL注入防护、XSS防护、数据脱敏、SM4加密、接口安全、限流
18
18
  ---
19
19
 
20
20
  # leniu-security-guard
@@ -16,7 +16,7 @@ description: |
16
16
  - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
17
17
  - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
18
18
 
19
- 触发词:leniu-工具类、leniu-BeanUtil、leniu-StrUtil、leniu-CollUtil、leniu-ObjectUtil、leniu-RedisUtil、leniu-JacksonUtil、leniu-LeBeanUtil、net.xnzn、leniu-yunshitang、leniu-yunshitang-core、云食堂工具类
19
+ 触发词:工具类、BeanUtil、StrUtil、CollUtil、ObjectUtil、RedisUtil、JacksonUtil、LeBeanUtil
20
20
 
21
21
  ---
22
22
 
@@ -12,7 +12,7 @@ description: |
12
12
 
13
13
  适用项目:leniu-tengyun-core(云食堂项目)
14
14
 
15
- 触发词:leniu-API、leniu-接口、leniu-Controller、leniu-RESTful、LeResult、云食堂接口、云食堂API
15
+ 触发词:API接口、Controller、RESTful、LeResult、LeResponse、LeRequest、接口开发、路由前缀
16
16
  ---
17
17
 
18
18
  # leiu-yunshitang-core API 接口开发规范
@@ -10,7 +10,11 @@ description: |
10
10
  - 多租户数据隔离设计
11
11
  - Entity 审计字段与逻辑删除设计
12
12
 
13
- 触发词:leniu架构、云食堂架构、双库架构、商户库、系统库、net.xnzn、pigx框架
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
+ 触发词:架构设计、双库架构、商户库、系统库、pigx框架、四层架构、模块划分、Business层
14
18
  ---
15
19
 
16
20
  # leniu-yunshitang-core 架构设计指南
@@ -9,9 +9,11 @@ description: |
9
9
  - 配置 Swagger 文档注解(@Api, @ApiOperation, @ApiModelProperty)
10
10
  - 配置分组校验(InsertGroup, UpdateGroup)
11
11
 
12
- 适用项目:leniu-tengyun-core(云食堂项目)
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
13
15
 
14
- 触发词:leniu-注解、leniu-@RequiresAuthentication、leniu-@RequiresGuest、leniu-@Validated、leniu-@NotNull、leniu-@Api、leniu-@ApiOperation、leniu-@ApiModelProperty、leniu-分组校验、leniu-InsertGroup、leniu-UpdateGroup、net.xnzn、leniu-yunshitang、云食堂注解
16
+ 触发词:@RequiresAuthentication、@RequiresGuest、@Validated、@NotNull、@Api、@ApiOperation、@ApiModelProperty、分组校验、InsertGroup、UpdateGroup
15
17
  ---
16
18
 
17
19
  # leniu-yunshitang-core 后端注解指南
@@ -14,7 +14,7 @@ description: |
14
14
  - leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
15
15
  - leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
16
16
 
17
- 触发词:leniu头脑风暴、云食堂方案、leniu怎么设计、云食堂讨论、leniu创意、leniu方案探索、leniu功能规划、leniu-yunshitang
17
+ 触发词:头脑风暴、方案设计、怎么设计、创意探索、功能规划、可行性分析
18
18
  ---
19
19
 
20
20
  # leniu-yunshitang-core 头脑风暴框架
@@ -10,8 +10,7 @@ description: |
10
10
  - 避免过度工程
11
11
  - 代码风格检查
12
12
 
13
- 触发词:规范、禁止、命名、Git提交、代码风格、不能用、不允许、包名、架构、leniupigx
14
- 注意:leniu CRUD 开发规范请激活 leniu-crud-development,API 开发规范请激活 leniu-api-development。
13
+ 触发词:代码禁令、代码规范、命名规范、代码风格、Git提交规范、包结构、禁止写法、审计字段规范、delFlagcrby/crtime
15
14
  ---
16
15
 
17
16
  # leniu-tengyun-core 代码规范速查
@@ -348,6 +347,53 @@ Target target = BeanUtil.copyProperties(source, Target.class);
348
347
  List<Target> targets = BeanUtil.copyToList(sources, Target.class);
349
348
  ```
350
349
 
350
+ ## 包结构规范
351
+
352
+ ```
353
+ net.xnzn.core
354
+ ├── [module]/
355
+ │ ├── controller/ # 控制器层(按端分:web/mobile/android)
356
+ │ ├── business/impl/ # 业务编排层
357
+ │ ├── service/impl/ # 服务层
358
+ │ ├── mapper/ # 数据访问层(含 XML 同目录)
359
+ │ ├── model/ # 数据模型(Entity)
360
+ │ ├── vo/ # 视图对象(返回前端)
361
+ │ ├── dto/ # 数据传输对象(服务间/MQ)
362
+ │ ├── param/ # 请求参数对象
363
+ │ ├── constants/ # 常量和枚举
364
+ │ ├── config/ # 配置类
365
+ │ ├── mq/ # 消息队列监听器
366
+ │ ├── task/ # 定时任务
367
+ │ ├── handle/ # 业务处理器(策略模式)
368
+ │ └── util/ # 工具类
369
+ ```
370
+
371
+ ## 依赖注入规范
372
+
373
+ ```java
374
+ // 推荐:字段注入
375
+ @Autowired
376
+ private OrderService orderService;
377
+
378
+ // 解决循环依赖
379
+ @Resource
380
+ @Lazy
381
+ private ProductService productService;
382
+ ```
383
+
384
+ ## 类注释规范
385
+
386
+ ```java
387
+ /**
388
+ * 类功能描述
389
+ *
390
+ * @author xujiajun
391
+ * @date 2026-02-20
392
+ */
393
+ public class OrderService {
394
+ }
395
+ ```
396
+
351
397
  ## 通用代码规范
352
398
 
353
399
  无论使用哪种项目架构,以下规范都是通用的: