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.
- package/.claude/hooks/skill-forced-eval.js +26 -19
- package/.claude/skills/leniu-api-development/SKILL.md +1 -1
- package/.claude/skills/leniu-architecture-design/SKILL.md +1 -1
- package/.claude/skills/leniu-backend-annotations/SKILL.md +1 -1
- package/.claude/skills/leniu-brainstorm/SKILL.md +1 -1
- package/.claude/skills/leniu-code-patterns/SKILL.md +48 -2
- package/.claude/skills/leniu-crud-development/SKILL.md +1 -1
- package/.claude/skills/leniu-data-permission/SKILL.md +15 -0
- package/.claude/skills/leniu-database-ops/SKILL.md +1 -1
- package/.claude/skills/leniu-error-handler/SKILL.md +1 -1
- package/.claude/skills/leniu-java-amount-handling/SKILL.md +1 -1
- package/.claude/skills/leniu-java-concurrent/SKILL.md +1 -1
- package/.claude/skills/leniu-java-entity/SKILL.md +1 -1
- package/.claude/skills/leniu-java-export/SKILL.md +1 -1
- package/.claude/skills/leniu-java-logging/SKILL.md +1 -1
- package/.claude/skills/leniu-java-mq/SKILL.md +1 -1
- package/.claude/skills/leniu-java-mybatis/SKILL.md +1 -1
- package/.claude/skills/leniu-java-report-query-param/SKILL.md +1 -1
- package/.claude/skills/leniu-java-task/SKILL.md +1 -1
- package/.claude/skills/leniu-java-total-line/SKILL.md +1 -1
- package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +1 -1
- package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +1 -1
- package/.claude/skills/leniu-mealtime/SKILL.md +1 -1
- package/.claude/skills/leniu-redis-cache/SKILL.md +15 -0
- package/.claude/skills/leniu-report-customization/SKILL.md +549 -0
- package/.claude/skills/leniu-security-guard/SKILL.md +1 -1
- package/.claude/skills/leniu-utils-toolkit/SKILL.md +1 -1
- package/.codex/skills/leniu-api-development/SKILL.md +1 -1
- package/.codex/skills/leniu-architecture-design/SKILL.md +5 -1
- package/.codex/skills/leniu-backend-annotations/SKILL.md +4 -2
- package/.codex/skills/leniu-brainstorm/SKILL.md +1 -1
- package/.codex/skills/leniu-code-patterns/SKILL.md +48 -2
- package/.codex/skills/leniu-crud-development/SKILL.md +1 -1
- package/.codex/skills/leniu-data-permission/SKILL.md +15 -0
- package/.codex/skills/leniu-database-ops/SKILL.md +1 -1
- package/.codex/skills/leniu-error-handler/SKILL.md +1 -1
- package/.codex/skills/leniu-java-amount-handling/SKILL.md +1 -1
- package/.codex/skills/leniu-java-concurrent/SKILL.md +1 -1
- package/.codex/skills/leniu-java-entity/SKILL.md +1 -1
- package/.codex/skills/leniu-java-export/SKILL.md +24 -23
- package/.codex/skills/leniu-java-logging/SKILL.md +1 -1
- package/.codex/skills/leniu-java-mq/SKILL.md +1 -1
- package/.codex/skills/leniu-java-mybatis/SKILL.md +1 -1
- package/.codex/skills/leniu-java-report-query-param/SKILL.md +1 -1
- package/.codex/skills/leniu-java-task/SKILL.md +1 -1
- package/.codex/skills/leniu-java-total-line/SKILL.md +21 -20
- package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +1 -1
- package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +1 -1
- package/.codex/skills/leniu-mealtime/SKILL.md +1 -1
- package/.codex/skills/leniu-redis-cache/SKILL.md +15 -0
- package/.codex/skills/leniu-report-customization/SKILL.md +549 -0
- package/.codex/skills/leniu-security-guard/SKILL.md +1 -1
- package/.codex/skills/leniu-utils-toolkit/SKILL.md +4 -2
- package/AGENTS.md +26 -26
- 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
|
-
|
|
17
|
+
触发词:安全认证、SQL注入防护、XSS防护、数据脱敏、SM4加密、接口安全、限流
|
|
18
18
|
---
|
|
19
19
|
|
|
20
20
|
# leniu-security-guard
|
|
@@ -12,9 +12,11 @@ description: |
|
|
|
12
12
|
- JSON 序列化
|
|
13
13
|
- 模糊查询处理
|
|
14
14
|
|
|
15
|
-
适用项目:
|
|
15
|
+
适用项目:
|
|
16
|
+
- leniu-tengyun-core:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun-core
|
|
17
|
+
- leniu-yunshitang:/Users/xujiajun/Developer/gongsi_proj/leniu-api/leniu-tengyun/leniu-yunshitang
|
|
16
18
|
|
|
17
|
-
|
|
19
|
+
触发词:工具类、BeanUtil、StrUtil、CollUtil、ObjectUtil、RedisUtil、JacksonUtil、LeBeanUtil
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
package/AGENTS.md
CHANGED
|
@@ -78,32 +78,32 @@
|
|
|
78
78
|
|
|
79
79
|
| 技能名称 | 触发条件 |
|
|
80
80
|
|---------|---------|
|
|
81
|
-
| `leniu-crud-development` |
|
|
82
|
-
| `leniu-api-development` |
|
|
83
|
-
| `leniu-brainstorm` |
|
|
84
|
-
| `leniu-architecture-design` |
|
|
85
|
-
| `leniu-database-ops` |
|
|
86
|
-
| `leniu-utils-toolkit` |
|
|
87
|
-
| `leniu-error-handler` |
|
|
88
|
-
| `leniu-backend-annotations` |
|
|
89
|
-
| `leniu-security-guard` |
|
|
90
|
-
| `leniu-data-permission` |
|
|
91
|
-
| `leniu-redis-cache` |
|
|
92
|
-
| `leniu-code-patterns` |
|
|
93
|
-
| `leniu-java-entity` |
|
|
94
|
-
| `leniu-java-logging` |
|
|
95
|
-
| `leniu-java-mybatis` |
|
|
96
|
-
| `leniu-java-
|
|
97
|
-
| `leniu-java-
|
|
98
|
-
| `leniu-java-
|
|
99
|
-
| `leniu-java-
|
|
100
|
-
| `leniu-java-
|
|
101
|
-
| `leniu-java-
|
|
102
|
-
| `leniu-java-
|
|
103
|
-
| `leniu-
|
|
104
|
-
| `leniu-
|
|
105
|
-
| `leniu-marketing-
|
|
106
|
-
| `leniu-
|
|
81
|
+
| `leniu-crud-development` | CRUD、增删改查、新建模块、Business层、Service、Mapper、Controller、分页查询、LeRequest、PageDTO |
|
|
82
|
+
| `leniu-api-development` | API接口、Controller、RESTful、LeResult、LeResponse、LeRequest、接口开发、路由前缀 |
|
|
83
|
+
| `leniu-brainstorm` | 头脑风暴、方案设计、怎么设计、创意探索、功能规划、可行性分析 |
|
|
84
|
+
| `leniu-architecture-design` | 架构设计、双库架构、商户库、系统库、pigx框架、四层架构、模块划分、Business层 |
|
|
85
|
+
| `leniu-database-ops` | 数据库、SQL、建表、双库、商户库、系统库、审计字段、crby、crtime、del_flag |
|
|
86
|
+
| `leniu-utils-toolkit` | 工具类、BeanUtil、StrUtil、CollUtil、ObjectUtil、RedisUtil、JacksonUtil、LeBeanUtil |
|
|
87
|
+
| `leniu-error-handler` | 异常处理、LeException、全局异常、参数校验、错误码、I18n、国际化 |
|
|
88
|
+
| `leniu-backend-annotations` | @RequiresAuthentication、@RequiresGuest、@Validated、@NotNull、@Api、分组校验、InsertGroup |
|
|
89
|
+
| `leniu-security-guard` | 安全认证、SQL注入防护、XSS防护、数据脱敏、SM4加密、接口安全、限流 |
|
|
90
|
+
| `leniu-data-permission` | 多租户、数据权限、@UseSystem、Executors.doInTenant、TenantContextHolder、MERCHANT-ID、双库隔离 |
|
|
91
|
+
| `leniu-redis-cache` | Redis、缓存、RedisUtil、分布式锁、RLock、getLock、setNx、ZSet、限流、缓存击穿 |
|
|
92
|
+
| `leniu-code-patterns` | 代码禁令、代码规范、命名规范、代码风格、Git提交规范、包结构、禁止写法、审计字段规范 |
|
|
93
|
+
| `leniu-java-entity` | Entity实体类、VO视图对象、DTO数据传输、Param参数类、@TableName、@TableField、字段映射 |
|
|
94
|
+
| `leniu-java-logging` | 日志、@Slf4j、log.info、log.error、log.debug、日志级别、logback |
|
|
95
|
+
| `leniu-java-mybatis` | MyBatis、MyBatisPlus、Mapper、LambdaQueryWrapper、XML映射、动态SQL、BaseMapper |
|
|
96
|
+
| `leniu-java-amount-handling` | 金额处理、分转元、元转分、Long金额、money、fen、BigDecimal金额 |
|
|
97
|
+
| `leniu-java-concurrent` | 并发、CompletableFuture、线程池、ThreadPool、并发安全、异步处理 |
|
|
98
|
+
| `leniu-java-export` | 导出、Excel导出、异步导出、分页导出、@ExcelProperty、exportApi |
|
|
99
|
+
| `leniu-java-mq` | 消息队列、MQ、MqUtil、@MqConsumer、延迟消息、消息重试、事务消息 |
|
|
100
|
+
| `leniu-java-task` | 定时任务、XXL-Job、@XxlJob、TenantLoader、任务调度、分布式定时 |
|
|
101
|
+
| `leniu-java-report-query-param` | 报表查询入参、Param类、分页参数、时间范围查询、ReportBaseParam、exportCols |
|
|
102
|
+
| `leniu-java-total-line` | 合计行、totalLine、报表合计、SUM合计、ReportBaseTotalVO、合计查询 |
|
|
103
|
+
| `leniu-mealtime` | 餐次、mealtime、mealtimeType、早餐、午餐、晚餐、下午茶、夜宵、AllocMealtimeTypeEnum |
|
|
104
|
+
| `leniu-marketing-price-rule-customizer` | 营销计费、计价规则、RulePriceHandler、RulePriceEnum、折扣规则、满减规则、限额规则 |
|
|
105
|
+
| `leniu-marketing-recharge-rule-customizer` | 营销充值、充值规则、RuleRechargeHandler、RuleRechargeEnum、满赠规则、充值赠送 |
|
|
106
|
+
| `leniu-report-customization` | 定制报表、汇总报表、report_order_info、report_order_detail、report_account_flow、退款汇总、消费金额统计 |
|
|
107
107
|
|
|
108
108
|
### 前端开发技能(需 plus-ui 目录存在)
|
|
109
109
|
|