ai-engineering-init 1.16.3 → 1.17.0

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 (44) hide show
  1. package/.claude/commands/init-config.md +154 -0
  2. package/.claude/skills/{leniu-java-amount-handling/SKILL.md → leniu-report-scenario/references/amount-handling.md} +0 -13
  3. package/.claude/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  4. package/{.cursor/skills/leniu-mealtime/SKILL.md → .claude/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  5. package/{.codex/skills/leniu-java-report-query-param/SKILL.md → .claude/skills/leniu-report-scenario/references/query-param.md} +0 -17
  6. package/.claude/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  7. package/{.codex/skills/leniu-java-total-line/SKILL.md → .claude/skills/leniu-report-scenario/references/total-line.md} +0 -17
  8. package/.claude/templates/env-config.md +27 -0
  9. package/.codex/skills/leniu-report-scenario/references/amount-handling.md +448 -0
  10. package/.codex/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  11. package/{.claude/skills/leniu-mealtime/SKILL.md → .codex/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  12. package/{.cursor/skills/leniu-java-report-query-param/SKILL.md → .codex/skills/leniu-report-scenario/references/query-param.md} +0 -17
  13. package/.codex/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  14. package/{.cursor/skills/leniu-java-total-line/SKILL.md → .codex/skills/leniu-report-scenario/references/total-line.md} +0 -17
  15. package/.cursor/skills/leniu-report-scenario/references/amount-handling.md +448 -0
  16. package/.cursor/skills/{leniu-java-export/SKILL.md → leniu-report-scenario/references/export.md} +0 -17
  17. package/{.codex/skills/leniu-mealtime/SKILL.md → .cursor/skills/leniu-report-scenario/references/mealtime.md} +0 -18
  18. package/{.claude/skills/leniu-java-report-query-param/SKILL.md → .cursor/skills/leniu-report-scenario/references/query-param.md} +0 -17
  19. package/.cursor/skills/leniu-report-scenario/references/standard-customization.md +112 -0
  20. package/{.claude/skills/leniu-java-total-line/SKILL.md → .cursor/skills/leniu-report-scenario/references/total-line.md} +0 -17
  21. package/.cursor/templates/env-config.md +27 -0
  22. package/bin/index.js +235 -1
  23. package/package.json +1 -1
  24. package/.claude/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  25. package/.claude/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  26. package/.claude/skills/leniu-report-customization/SKILL.md +0 -415
  27. package/.claude/skills/leniu-report-standard-customization/SKILL.md +0 -391
  28. package/.codex/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  29. package/.codex/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  30. package/.codex/skills/leniu-report-customization/SKILL.md +0 -415
  31. package/.codex/skills/leniu-report-standard-customization/SKILL.md +0 -391
  32. package/.cursor/skills/leniu-marketing-price-rule-customizer/SKILL.md +0 -301
  33. package/.cursor/skills/leniu-marketing-recharge-rule-customizer/SKILL.md +0 -285
  34. package/.cursor/skills/leniu-report-customization/SKILL.md +0 -415
  35. package/.cursor/skills/leniu-report-standard-customization/SKILL.md +0 -391
  36. /package/.claude/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  37. /package/.claude/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  38. /package/.claude/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
  39. /package/.codex/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  40. /package/.codex/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  41. /package/.codex/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
  42. /package/.cursor/skills/{leniu-report-standard-customization → leniu-report-scenario}/references/analysis-module.md +0 -0
  43. /package/.cursor/skills/{leniu-report-customization/references/table-fields.md → leniu-report-scenario/references/customization-table-fields.md} +0 -0
  44. /package/.cursor/skills/{leniu-report-standard-customization/references/table-fields.md → leniu-report-scenario/references/standard-table-fields.md} +0 -0
@@ -1,391 +0,0 @@
1
- ---
2
- name: leniu-report-standard-customization
3
- description: |
4
- leniu-tengyun-core 标准版(core-report 模块)定制报表开发指南。基于独立 report_order_info / report_refund / report_account_flow 等报表基础表实现汇总报表。
5
-
6
- 触发场景:
7
- - 在标准版(core-report 模块)中开发定制汇总报表
8
- - 基于标准版退款表(report_refund)处理退款统计
9
- - 开发账户流水报表(日结/操作员/钱包消费/消费汇总)
10
- - 开发经营分析报表(营业额/用户/菜品/评价/充值/设备分析)
11
- - 开发商户维度的消费/充值汇总报表
12
-
13
- 触发词:标准版报表、core-report、report_refund、report_refund_detail、经营分析、营业额分析、用户活跃度、菜品排行、操作员统计、账户日结、钱包消费汇总、商户消费汇总、ReportOrderConsumeService、ReportAccountConsumeService
14
- ---
15
-
16
- # leniu 标准版定制报表开发指南
17
-
18
- > 详细字段说明见 `references/table-fields.md`,经营分析详情见 `references/analysis-module.md`
19
-
20
- ## 版本识别(必读)
21
-
22
- **本 skill 仅适用于标准版(core-report 独立模块)**。
23
-
24
- | 判断方式 | 标准版(本指南) | v5.29 版本 |
25
- |---------|--------------|-----------|
26
- | 目录结构 | `core-report/` 独立模块 | `sys-canteen/` 内嵌 |
27
- | 退款存储 | 独立 `report_refund` 表(**正数**) | 合并入 `report_order_info`(`consumeType=2`,**负数**) |
28
- | 第二阶段 | `fix()` 按日重算 | `batchConsume()` 增量累加 |
29
- | consumeType | **无此字段** | 1=消费,2=退款 |
30
-
31
- > v5.29 报表请使用 `leniu-report-customization`。钱包/交易类型枚举同 v5.29,参见该 skill。
32
-
33
- ---
34
-
35
- ## 一、报表系统架构
36
-
37
- ### 1.1 模块结构
38
-
39
- ```
40
- core-report/.../statistics/
41
- ├── config/mq/ # MQ 监听器 + 消费调度 + 线程池
42
- ├── order/ # 订单报表(basic/summary/fix/analysis)
43
- ├── account/ # 账户流水报表
44
- ├── merchant/ # 商户维度报表
45
- ├── common/ # 错误日志/定时任务
46
- └── param/vo/constants/ # 公共类
47
- ```
48
-
49
- ### 1.2 三大 MQ 监听器
50
-
51
- | 监听器 | Topic/Tag |
52
- |-------|-----------|
53
- | `ReportOrderMQListener` | `order / order-v3-placed` |
54
- | `ReportOrderRefundMQListener` | `order / order-v3-refunded` |
55
- | `ReportAccountMQListener` | `acc / acc-trade-report-queue` |
56
-
57
- ### 1.3 两阶段消费模型
58
-
59
- ```
60
- 第一阶段(ORDER < 10,同步写基础表)
61
- ├── ORDER=1 ReportOrderInfoService → report_order_info
62
- ├── ORDER=2 ReportOrderDiscountService → report_order_discount
63
- ├── ORDER=3 ReportOrderDetailService → report_order_detail
64
- ├── ORDER=4 ReportOrderPayService → report_order_pay
65
- ├── ORDER=5 ReportRefundService → report_refund
66
- ├── ORDER=6 ReportRefundDetailService → report_refund_detail
67
- └── ORDER=11 ReportOrderInfoSnapshotService → report_order_info_snapshot
68
-
69
- 第二阶段(ORDER >= 10,fix 按日重算汇总表,由 Redis 计数触发)
70
- ├── ORDER=13 ReportSumMealtimeService → 分餐次汇总
71
- ├── ORDER=16 ReportSumPayService → 支付渠道汇总
72
- ├── ORDER=17 ReportSumPayMerService → 商户支付汇总
73
- ├── ORDER=18 ReportSumDishesService → 菜品销售汇总
74
- ├── ORDER=50 ReportAnalysisCustService → 用户分析
75
- └── ORDER=51 ReportAnalysisDishesSaleService→ 菜品销售分析
76
- ```
77
-
78
- ### 1.4 第二阶段核心逻辑(fix 重算模式)
79
-
80
- ```java
81
- // ReportConsumerService.consumeOrderReport()
82
- void consumeOrderReport() {
83
- for (TenantInfo tenant : allTenants) {
84
- Executors.doInTenant(tenant.getId(), () -> {
85
- RLock lock = RedisUtil.getLock(REPORT_ORDER_LOCK);
86
- lock.lock(120, TimeUnit.MINUTES);
87
- try {
88
- List<ReportNotConsumeDTO> list = reportOrderInfoService.queryNotConsumeData();
89
- // 菜品:按 orderDate 分组调 fix
90
- // 其他:按 statisticDate 分组,依次调所有 ORDER>=10 的 fix()
91
- reportOrderInfoService.updateOrderMsg(list); // 标记已消费
92
- } finally { lock.unlock(); }
93
- });
94
- }
95
- }
96
- ```
97
-
98
- **触发机制**:Redis 计数器每条消息递减,达阈值(默认100)触发 + XxlJob 定时兜底。
99
-
100
- ---
101
-
102
- ## 二、核心基础表概要
103
-
104
- ### 2.1 report_order_info(仅存正向订单,无 consumeType)
105
-
106
- 关键字段:`orderId`(主键), `canteenId/stallId`, `mealtimeType`, `orderType`, `payableAmount/realAmount/refundAmount`(分), `accPayAmount/outPayAmount`, `payTime/orderDate`, `orderRefundState`(1未退/2全退/3部分退), `status`(0未消费/1已消费), `nuClearMode`, `psnType`, `ageType/holidayType`
107
-
108
- ### 2.2 report_order_detail(菜品明细)
109
-
110
- 关键字段:`detailId`, `orderId`, `goodsDishesId/goodsDishesName`, `price/totalAmount/realAmount`(分), `quantity`, `salesMode`(1按份/2称重), `detailState`(1正常/2全退/3部分退), `goodsRefundNum`, `refundAmount`, `detailType`
111
-
112
- ### 2.3 report_refund / report_refund_detail(标准版特有,退款为正数)
113
-
114
- **report_refund**:`orderRefundId`(主键), `orderId`(原订单), `realRefundAmount`(**正数**), `applyType`(1退单/2纠错), `checkTime`
115
-
116
- **report_refund_detail**:`orderRefundId`, `detailId`, `realQuantity`, `realRefundAmount`(**正数**)
117
-
118
- ### 2.4 其他基础表
119
-
120
- - **report_order_pay**:`orderId`, `payType/payChannel`, `payAmount/refundAmount`
121
- - **report_order_discount**:`orderId`, `changeAmount`, `changeType`(1上浮/2优惠), `changeDetailType`
122
- - **report_order_info_snapshot**:订单交易快照
123
-
124
- ---
125
-
126
- ## 三、退款数据处理(核心重点)
127
-
128
- ### 3.1 存储模型
129
-
130
- ```
131
- 正向订单 → report_order_info(realAmount 为正)
132
- 退款记录 → report_refund(realRefundAmount 为正)+ report_refund_detail
133
- 同时更新 → report_order_info.orderRefundState + refundAmount
134
- ```
135
-
136
- ### 3.2 净消费金额计算(3种方式)
137
-
138
- **方式一:主表 refundAmount 减退(推荐)**
139
- ```sql
140
- SELECT SUM(real_amount - IFNULL(refund_amount, 0)) AS netAmount
141
- FROM report_order_info WHERE pay_time BETWEEN #{start} AND #{end}
142
- ```
143
-
144
- **方式二:排除全退**
145
- ```sql
146
- WHERE order_refund_state IN (1, 3)
147
- ```
148
-
149
- **方式三:关联 report_refund**
150
- ```sql
151
- SELECT o.canteen_id, SUM(o.real_amount) AS consume, IFNULL(SUM(r.real_refund_amount), 0) AS refund
152
- FROM report_order_info o LEFT JOIN report_refund r ON o.order_id = r.order_id
153
- GROUP BY o.canteen_id
154
- ```
155
-
156
- ### 3.3 菜品级别退款
157
-
158
- ```sql
159
- SELECT goods_dishes_name,
160
- SUM(quantity - IFNULL(goods_refund_num, 0)) AS netQuantity,
161
- SUM(total_amount - IFNULL(refund_amount, 0)) AS netAmount
162
- FROM report_order_detail WHERE detail_state IN (1, 3) GROUP BY goods_dishes_name
163
- ```
164
-
165
- ---
166
-
167
- ## 四、账户流水报表
168
-
169
- ### 4.1 report_account_flow(流水主表)
170
-
171
- 核心字段:`flowId`, `custId/orgId`, `payTime`, `flowType`(AccTradeTypeEnum), `flowRealAmount/flowAmount`, `manageCost`, `accTotalBal/accAllBal`, `status`
172
-
173
- ### 4.2 report_account_summary(用户账户日结表)
174
-
175
- 联合主键:`statisticDate` + `custId`。期末余额 = 期初 + 充值 - 撤销充值 + 补贴 - 撤销补贴 + 红包 + 赠送 - 消费 - 补扣 + 退款 - 提现 - 清空 - 管理费
176
-
177
- ### 4.3 AccountConsumeService 实现
178
-
179
- | ORDER | 类 | 汇总表 |
180
- |-------|---|-------|
181
- | 1/2 | Flow/FlowDetail | 基础表 |
182
- | 13 | AccountSummary | 日结 |
183
- | 14 | AccountOperator | 操作员 |
184
- | 15-17 | ConsumeSummary/Org/Type | 消费维度 |
185
- | 18 | WalletConsume | 钱包 |
186
- | 19 | SumRechargeMer | 商户充值 |
187
-
188
- ---
189
-
190
- ## 五、汇总表开发标准模式
191
-
192
- ### 5.1 接口与实现
193
-
194
- ```java
195
- @Service @Slf4j
196
- public class ReportSumXxxService implements ReportOrderConsumeService {
197
- @Override public int getOrder() { return 15; } // 10-29普通,30+菜品,50+分析
198
-
199
- @Override public void consume(OrderChangePO payload, ReportOrderInfoDTO baseInfo) {
200
- // 标准版:留空,由 fix() 统一处理
201
- }
202
-
203
- @Override public void fix(ReportBaseParam param) {
204
- LocalDateTime start = param.getStartPayTime(), end = param.getEndPayTime();
205
- mapper.delete(Wrappers.<ReportSumXxx>lambdaQuery()
206
- .between(ReportSumXxx::getStatisticDate, start.toLocalDate(), end.toLocalDate()));
207
- mapper.initFix(start, end);
208
- }
209
- }
210
- ```
211
-
212
- ### 5.2 fix SQL 模板
213
-
214
- ```xml
215
- <insert id="initFix">
216
- INSERT INTO report_sum_xxx (id, statistic_date, canteen_id, canteen_name,
217
- order_count, consume_amount, refund_amount, net_amount)
218
- SELECT #{id}, DATE(a.pay_time), a.canteen_id, a.canteen_name,
219
- COUNT(*), SUM(a.real_amount),
220
- SUM(IFNULL(a.refund_amount, 0)),
221
- SUM(a.real_amount - IFNULL(a.refund_amount, 0))
222
- FROM report_order_info a
223
- WHERE a.pay_time BETWEEN #{startTime} AND #{endTime}
224
- GROUP BY DATE(a.pay_time), a.canteen_id, a.canteen_name
225
- </insert>
226
- ```
227
-
228
- ### 5.3 查询接口(并行 + 权限)
229
-
230
- ```java
231
- public ReportBaseTotalVO<XxxVO> pageSummary(XxxParam param) {
232
- MgrUserAuthPO authPO = MgrUserAuthApi.getUserAuthPO();
233
- CompletableFuture<List<XxxVO>> listF = supplyAsync(() -> mapper.listSummary(param, authPO));
234
- CompletableFuture<XxxVO> totalF = supplyAsync(() -> mapper.getSummaryTotal(param, authPO));
235
- CompletableFuture.allOf(listF, totalF).join();
236
- return new ReportBaseTotalVO<>(PageVO.of(listF.join(), param.getPage()), totalF.join());
237
- }
238
- ```
239
-
240
- 权限 SQL:
241
- ```xml
242
- <if test="'-1'.toString() != authPO.roleType.toString()">
243
- AND EXISTS (SELECT null FROM mgr_role_org it1
244
- WHERE a.org_id = it1.org_id AND it1.role_id = #{authPO.roleId})
245
- </if>
246
- ```
247
-
248
- ---
249
-
250
- ## 六、汇总模型速查
251
-
252
- | 表 | 维度 | 金额 |
253
- |----|------|------|
254
- | report_sum_mealtime | date/canteen/stall/org/age/mealtime/psn/machine/source | custNum/consumeNum/realAmount/refundAmount |
255
- | report_sum_pay | date/mealtime/canteen/stall/org/age/payChannel/payType | payNum/realAmount/refundAmount |
256
- | report_sum_dishes | date/area/canteen/stall/reportOrderType/mealtime/cook/device/dishes/salesMode/detailType | quantity/realAmount |
257
- | report_sum_pay_mer | tenantId/date/payChannel/payType | custNum/payNum/realAmount/refundAmount |
258
-
259
- ---
260
-
261
- ## 七、经营分析模块
262
-
263
- | 分析 | Service | 路由前缀 |
264
- |-----|---------|---------|
265
- | 营业额 | ReportAnalysisTurnoverService | `/summary/analysis/turnover/` |
266
- | 用户 | ReportAnalysisCustService (ORDER=50) | `/summary/analysis/cust/` |
267
- | 菜品 | ReportAnalysisDishesSaleService (ORDER=51) | `/summary/analysis/dishes/` |
268
- | 满意度 | ReportAnalysisEvaluateService | `/summary/analysis/evaluate/` |
269
- | 充值 | ReportAnalysisTurnoverService | `/summary/analysis/recharge/` |
270
- | 设备 | ReportAnalysisTurnoverService | `/summary/analysis/device/` |
271
-
272
- ---
273
-
274
- ## 八、公共模块
275
-
276
- - **报表错误日志**(report_error_log):`reportErrorType`(1账户/2订单), `reportErrorState`(1已创建/2已处理)。定时任务 `@XxlJob("reportExceptionHandle")` 自动修复。
277
- - **金额范围设置**:`POST /report/alloc/amount-scope/save`
278
- - **数据修复**:`POST /summary/fix/order|account`(限31天,Redisson 锁 120 分钟)
279
-
280
- ### 核心枚举
281
-
282
- | 枚举 | 值 |
283
- |------|---|
284
- | ReportClassifyEnum | 1组织/2类别/3食堂/4设备/5收入/6渠道/7餐次 |
285
- | ReportPayTypeEnum | 1微信/2支付宝/3系统账户/9现金/20其他 |
286
-
287
- ---
288
-
289
- ## 九、MySQL only_full_group_by 规范(必须遵守)
290
-
291
- > MySQL 默认开启 `sql_mode=ONLY_FULL_GROUP_BY`,SELECT 中所有非聚合列必须出现在 GROUP BY 中,且 GROUP BY 表达式必须与 SELECT 表达式**完全一致**。
292
-
293
- ### 核心规则
294
-
295
- **SELECT 的表达式 == GROUP BY 的表达式**(字符级别完全一致)
296
-
297
- ### ❌ 错误示例(GROUP BY 与 SELECT 不一致)
298
-
299
- ```xml
300
- <!-- 报错:Expression #1 of SELECT list is not in GROUP BY clause -->
301
- SELECT
302
- DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
303
- SUM(roi.real_amount) AS totalAmount
304
- FROM report_order_info roi
305
- GROUP BY DATE(roi.consume_time) <!-- ❌ DATE() ≠ DATE_FORMAT(..., '%Y-%m-%d') -->
306
- ORDER BY DATE(roi.consume_time)
307
- ```
308
-
309
- ### ✅ 正确示例(GROUP BY 与 SELECT 完全一致)
310
-
311
- ```xml
312
- SELECT
313
- DATE_FORMAT(roi.consume_time, '%Y-%m-%d') AS statisticDate,
314
- SUM(roi.real_amount) AS totalAmount
315
- FROM report_order_info roi
316
- GROUP BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ 与 SELECT 完全一致 -->
317
- ORDER BY DATE_FORMAT(roi.consume_time, '%Y-%m-%d') <!-- ✅ ORDER BY 也保持一致 -->
318
- ```
319
-
320
- ### ❌ 错误示例(SELECT 含非聚合列未加入 GROUP BY)
321
-
322
- ```xml
323
- SELECT
324
- roi.canteen_id,
325
- roi.canteen_name, <!-- ❌ 非聚合列未在 GROUP BY 中 -->
326
- SUM(roi.real_amount) AS totalAmount
327
- FROM report_order_info roi
328
- GROUP BY roi.canteen_id
329
- ```
330
-
331
- ### ✅ 正确示例(所有非聚合列都在 GROUP BY 中)
332
-
333
- ```xml
334
- SELECT
335
- roi.canteen_id,
336
- roi.canteen_name,
337
- SUM(roi.real_amount) AS totalAmount
338
- FROM report_order_info roi
339
- GROUP BY roi.canteen_id, roi.canteen_name <!-- ✅ 所有非聚合列都在 GROUP BY -->
340
- ```
341
-
342
- ### 检查清单
343
-
344
- - [ ] SELECT 中按日期分组时使用 `DATE_FORMAT(col, '%Y-%m-%d')`,**不要用 `DATE()`**
345
- - [ ] GROUP BY 表达式与 SELECT 中对应列**逐字相同**(复制粘贴而非重写)
346
- - [ ] ORDER BY 中同样使用与 GROUP BY 一致的表达式
347
- - [ ] SELECT 中所有非聚合列(无 SUM/COUNT/AVG/MAX/MIN 包裹)都出现在 GROUP BY 中
348
-
349
- ---
350
-
351
- ## 十、开发检查清单
352
-
353
- ### 建表
354
- - [ ] 分组维度 + 金额汇总 + 审计字段(crby/crtime/upby/uptime/del_flag),无 tenant_id
355
-
356
- ### 实现
357
- - [ ] 实现 `ReportOrderConsumeService`,设 `getOrder()`
358
- - [ ] `fix()` 先删后插(**标准版核心模式**),`consume()` 留空
359
-
360
- ### 退款(标准版特有)
361
- - [ ] 退款在独立 `report_refund` 表(**正数金额**)
362
- - [ ] 净消费 = `real_amount - IFNULL(refund_amount, 0)`
363
- - [ ] **不要使用 consumeType 字段**
364
-
365
- ### 查询
366
- - [ ] ReportBaseTotalVO + CompletableFuture 并行 + MgrUserAuthPO 权限
367
- - [ ] GROUP BY / ORDER BY 表达式与 SELECT 完全一致(only_full_group_by)
368
-
369
- ---
370
-
371
- ## 十一、关键代码位置
372
-
373
- > 路径前缀均为 `core-report/.../statistics/`
374
-
375
- | 类型 | 路径 |
376
- |------|------|
377
- | MQ 监听器 | `config/mq/ReportOrderMQListener.java` / `ReportAccountMQListener.java` |
378
- | 消费调度 | `config/mq/service/ReportConsumerService.java` |
379
- | 订单基础表 | `order/basic/model/ReportOrderInfo.java` / `ReportRefund.java` |
380
- | 汇总 Service | `order/summary/service/ReportSumMealtimeService.java` / `ReportSumPayService.java` |
381
- | 分析 Service | `order/analysis/service/ReportAnalysisTurnoverService.java` |
382
- | 账户 Service | `account/service/ReportAccountSummaryService.java` |
383
- | Fix | `order/fix/controller/ReportFixController.java` |
384
-
385
- ---
386
-
387
- ## 注意
388
-
389
- - 标准版退款为独立表(正数金额),**不要使用 consumeType 字段**
390
- - 标准版第二阶段用 `fix()` 按日重算,**不要使用 batchConsume() 增量模式**
391
- - CRUD 用 `leniu-crud-development`,MyBatis 用 `leniu-java-mybatis`,入参用 `leniu-java-report-query-param`,合计行用 `leniu-java-total-line`,餐次用 `leniu-mealtime`
@@ -1,301 +0,0 @@
1
- ---
2
- name: leniu-marketing-price-rule-customizer
3
- description: |
4
- leniu-tengyun-core 项目营销计费(price)规则定制指南。当需要定制营销计费规则时使用,支持新增规则类型、重写规则逻辑、扩展 DTO 字段。
5
-
6
- 触发场景:
7
- - 新增营销计费规则类型(折扣、满减、限额、补贴等)
8
- - 重写现有计费规则逻辑(@Primary 模式)
9
- - 扩展规则 DTO 字段(向后兼容)
10
- - 定制规则计算行为(handle 方法实现)
11
- - 注册规则枚举(RulePriceEnum)
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
- 触发词:营销计费、计价规则、RulePriceHandler、RulePriceEnum、折扣规则、满减规则、限额规则、补贴规则
18
- ---
19
-
20
- # leniu-tengyun-core 营销计费规则定制
21
-
22
- ## 概述
23
-
24
- leniu-tengyun-core 项目的营销计费(price)规则功能采用扩展点设计,支持灵活的规则定制。
25
-
26
- 营销计费规则是营销系统的核心组件,负责计算订单的优惠金额、限制消费行为、提供补贴等功能。
27
-
28
- ## 何时使用此 Skill
29
-
30
- - 需要新增一个计价规则类型
31
- - 需要修改现有规则的计算逻辑
32
- - 需要为规则添加新的配置字段
33
- - 需要针对特定项目定制规则行为
34
- - 参考现有规则实现新的定制需求
35
-
36
- ## 规则定制工作流
37
-
38
- ### 步骤1:确定定制模式
39
-
40
- 根据需求选择合适的定制模式:
41
-
42
- **模式A:新增规则**
43
- - 适用场景:创建全新的规则类型
44
- - 需要创建:DTO、扩展接口、默认实现
45
-
46
- **模式B:重写规则(@Primary)**
47
- - 适用场景:完全替换现有规则行为
48
- - 需要创建:定制实现类(使用@Primary注解)
49
- - 可选:扩展DTO字段
50
-
51
- **模式C:重写规则(Ordered)**
52
- - 适用场景:多个实现共存
53
- - 需要创建:定制实现类(实现Ordered接口)
54
-
55
- ### 步骤2:理解规则结构
56
-
57
- 了解:
58
- - 规则接口层次(RulePriceHandler → Extension → Implementation)
59
- - 规则DTO结构
60
- - 规则计算入参(RulePriceResultOrderDTO、MarketRuleVO、orderResults)
61
- - 规则计算流程
62
- - 常用工具类(MarketUtil、MarketRuleRangeService)
63
-
64
- ### 步骤3:参考实际案例
65
-
66
- 查看实际案例,了解如何实现具体的定制需求:
67
- - **案例1**:支持自定义场景类型选择(@Primary + DTO扩展)
68
- - **案例2**:移除订单类型过滤(直接修改)
69
-
70
- ### 步骤4:实现规则定制
71
-
72
- #### 新增规则的实现步骤
73
-
74
- 1. **创建规则DTO**
75
- - 位置:`net.xnzn.core.marketing.v2.rule.price.handler.[ruletype].dto`
76
- - 包含规则配置字段
77
- - 实现 `toString()` 方法返回可读描述
78
-
79
- 2. **创建扩展接口**
80
- - 位置:`net.xnzn.core.marketing.v2.rule.price.handler.[ruletype].extension`
81
- - 继承 `RulePriceHandler`
82
- - 实现 `getRuleType()` 和 `checkRuleInfo()` 方法
83
-
84
- 3. **创建默认实现**
85
- - 位置:`net.xnzn.core.marketing.v2.rule.price.handler.[ruletype].extension.impl`
86
- - 实现扩展接口
87
- - 添加 `@Service` 注解
88
- - 实现 `handle()` 方法
89
-
90
- 4. **注册规则枚举**
91
- - 在 `RulePriceEnum` 中添加新的规则类型
92
-
93
- #### 重写规则的实现步骤(@Primary模式)
94
-
95
- 1. **(可选)扩展DTO字段**
96
- - 在定制项目中覆盖核心DTO类
97
- - 添加新字段并保持向后兼容
98
-
99
- 2. **创建定制实现**
100
- - 位置:定制项目包(如 `net.xnzn.yunshitang.marketing.handler`)
101
- - 继承默认实现类(可选,用于复用逻辑)
102
- - 实现扩展接口
103
- - 添加 `@Service` 和 `@Primary` 注解
104
- - 重写 `handle()` 方法
105
-
106
- 3. **实现定制逻辑**
107
- - 解析规则配置(包含新字段)
108
- - 实现自定义计算逻辑
109
- - 更新订单优惠金额或抛出限制异常
110
-
111
- ### 步骤5:测试规则
112
-
113
- 1. 测试向后兼容性(新字段为null的场景)
114
- 2. 测试新功能(新字段有值的场景)
115
- 3. 测试边界条件
116
- 4. 测试与其他规则的组合使用
117
-
118
- ## 代码模板
119
-
120
- ### 新增规则模板
121
-
122
- ```java
123
- // 1. DTO
124
- @Data
125
- @ApiModel("计价规则详情-[规则名称]")
126
- public class [RuleType]DTO {
127
- @ApiModelProperty("[字段说明]")
128
- private [Type] fieldName;
129
-
130
- @Override
131
- public String toString() {
132
- return "字段:" + fieldName;
133
- }
134
- }
135
-
136
- // 2. 扩展接口
137
- public interface [RuleType]HandlerExtension extends RulePriceHandler {
138
- @Override
139
- default Integer getRuleType() {
140
- return RulePriceEnum.[RULE_TYPE_ENUM].getKey();
141
- }
142
-
143
- @Override
144
- default String checkRuleInfo(String ruleInfo) {
145
- [RuleType]DTO rule = JSON.parseObject(ruleInfo, [RuleType]DTO.class);
146
- return rule.toString();
147
- }
148
- }
149
-
150
- // 3. 默认实现
151
- @Slf4j
152
- @Service
153
- public class Default[RuleType]HandlerImpl implements [RuleType]HandlerExtension {
154
-
155
- @Autowired
156
- private MarketRuleRangeService rangeService;
157
-
158
- @Override
159
- public void handle(RulePriceResultOrderDTO order, MarketRuleVO rule,
160
- List<RulePriceResultOrderDTO> orderResults) {
161
- // 1. 解析规则配置
162
- [RuleType]DTO ruleInfo = JSON.parseObject(rule.getRuleInfo(), [RuleType]DTO.class);
163
-
164
- // 2. 查询规则适用范围
165
- List<MarketRuleRange> rangeList = rangeService.listRuleRangeLatest(rule.getRuleId());
166
-
167
- // 3. 实现规则计算逻辑
168
- // ...
169
-
170
- // 4. 更新订单优惠金额或抛出异常
171
- order.setDiscountsAmount(order.getDiscountsAmount().add(discountAmount));
172
- }
173
- }
174
- ```
175
-
176
- ### 重写规则模板(@Primary)
177
-
178
- ```java
179
- @Slf4j
180
- @Service
181
- @Primary // 标记为主要实现
182
- public class Custom[RuleType]HandlerImpl extends Default[RuleType]HandlerImpl
183
- implements [RuleType]HandlerExtension {
184
-
185
- @Autowired
186
- private MarketRuleRangeService rangeService;
187
-
188
- @Override
189
- public void handle(RulePriceResultOrderDTO order, MarketRuleVO rule,
190
- List<RulePriceResultOrderDTO> orderResults) {
191
- // 解析规则配置(可能包含扩展字段)
192
- [RuleType]DTO ruleInfo = JSON.parseObject(rule.getRuleInfo(), [RuleType]DTO.class);
193
-
194
- // 实现定制逻辑
195
- // 可以调用父类方法:super.handle(order, rule, orderResults);
196
- // 或完全自定义实现
197
- }
198
- }
199
- ```
200
-
201
- ## 常见规则类型
202
-
203
- ### 优惠类规则
204
- - 折扣规则:`PriceDiscountHandlerExtension`
205
- - 菜品折扣:`PriceDishDiscountHandlerExtension`
206
- - 商品折扣:`PriceGoodsDiscountHandlerExtension`
207
- - 满减规则:`PriceReductionFixedHandlerExtension`
208
-
209
- ### 限制类规则
210
- - 限额-单次金额:`PriceLimitMaxAmountHandlerExtension`
211
- - 限额-累计次数:`PriceLimitMaxCountHandlerExtension`
212
- - 限额-累计金额:`PriceLimitSumAmountHandlerExtension`
213
-
214
- ### 补贴类规则
215
- - 固定补贴:`PriceSubsidyFixedHandlerExtension`
216
- - 累计赠送:`PriceSumGiveHandlerExtension`
217
-
218
- ## 最佳实践
219
-
220
- ### 包名规范
221
- - 覆盖核心类:使用核心工程的包名(`net.xnzn.core.marketing.v2.rule.price.handler.[ruletype]`)
222
- - 定制实现:使用项目特定包名(如 `net.xnzn.yunshitang.marketing.handler`)
223
-
224
- ### 类名规范
225
- - 扩展接口:`[RuleType]HandlerExtension`
226
- - 默认实现:`Default[RuleType]HandlerImpl`
227
- - 定制实现:`Custom[RuleType]HandlerImpl` 或描述性名称
228
-
229
- ### 注解使用
230
- - 所有实现类必须添加 `@Service` 注解
231
- - 重写规则使用 `@Primary` 注解(推荐)
232
- - 日志记录使用 `@Slf4j` 注解
233
-
234
- ### 向后兼容
235
- - 扩展DTO字段时,新字段应支持null值
236
- - 新字段为null时应保持原有行为
237
- - 在toString方法中包含所有字段
238
-
239
- ## 快速开始示例
240
-
241
- 假设需要为限额累计金额规则(`PriceLimitSumAmountHandlerExtension`)添加场景类型选择功能:
242
-
243
- 1. **扩展DTO**:在 `PriceLimitSumAmountDTO` 中添加 `sceneTypes` 字段
244
- 2. **创建定制实现**:创建 `CrossScenePriceLimitSumAmountHandlerImpl`,位于 `net.xnzn.yunshitang.marketing.handler`,使用 `@Primary` 注解
245
- 3. **实现逻辑**:在 `handle()` 方法中使用 `sceneTypes` 字段过滤订单
246
- 4. **向后兼容**:`sceneTypes` 为null时查询所有场景
247
-
248
- ```java
249
- // 1. 扩展DTO(在核心工程包名下覆盖)
250
- // 位置:net.xnzn.core.marketing.v2.rule.price.handler.limitsum.dto.PriceLimitSumAmountDTO
251
- @Data
252
- @ApiModel("限额-累计金额规则详情")
253
- public class PriceLimitSumAmountDTO {
254
- @ApiModelProperty("累计金额限额(分)")
255
- private BigDecimal limitAmount;
256
-
257
- // 扩展字段:场景类型列表,null时表示所有场景
258
- @ApiModelProperty("适用场景类型(null=所有)")
259
- private List<Integer> sceneTypes;
260
-
261
- @Override
262
- public String toString() {
263
- return "limitAmount=" + limitAmount + ", sceneTypes=" + sceneTypes;
264
- }
265
- }
266
-
267
- // 2. 定制实现(在yunshitang项目包下)
268
- // 位置:net.xnzn.yunshitang.marketing.handler
269
- @Slf4j
270
- @Service
271
- @Primary
272
- public class CrossScenePriceLimitSumAmountHandlerImpl
273
- implements PriceLimitSumAmountHandlerExtension {
274
-
275
- @Autowired
276
- private MarketRuleRangeService rangeService;
277
-
278
- @Override
279
- public void handle(RulePriceResultOrderDTO order, MarketRuleVO rule,
280
- List<RulePriceResultOrderDTO> orderResults) {
281
- PriceLimitSumAmountDTO ruleInfo = JSON.parseObject(
282
- rule.getRuleInfo(), PriceLimitSumAmountDTO.class);
283
-
284
- // 场景过滤(向后兼容:null时不过滤)
285
- if (CollUtil.isNotEmpty(ruleInfo.getSceneTypes())
286
- && !ruleInfo.getSceneTypes().contains(order.getSceneType())) {
287
- return; // 不在适用场景内,跳过
288
- }
289
-
290
- // 原有累计金额限额逻辑...
291
- BigDecimal sumAmount = calcSumAmount(order, orderResults);
292
- if (sumAmount.compareTo(ruleInfo.getLimitAmount()) >= 0) {
293
- throw new LeException("已超出累计消费限额");
294
- }
295
- }
296
- }
297
- ```
298
-
299
- ## 参考文档
300
-
301
- 详见:[leniu-tengyun-core 源码](/Users/xujiajun/Developer/gongsi_proj/core/leniu-tengyun-core)