mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-06-15 04:39:50 +08:00
更新文档
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
# PRD:优惠券功能
|
||||
|
||||
**版本**: v1.1
|
||||
**日期**: 2026-03-03
|
||||
**版本**: v1.2
|
||||
**日期**: 2026-03-04
|
||||
**项目**: 万车充运营管理平台
|
||||
**状态**: 草稿
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
|------|------|---------|
|
||||
| v1.0 | 2026-03-03 | 初稿:积分兑换洗车券功能 |
|
||||
| v1.1 | 2026-03-03 | 新增:券创建权限体系(平台人员 vs 运营商人员);新增充电折扣券规划;更新数据库设计与业务规则 |
|
||||
| v1.2 | 2026-03-04 | 补全遗漏点:修复 scope 规则自相矛盾;统一 member_id/积分字段类型与现网一致;新增幂等键字段、核销日志表、scope 反向索引;补齐接口分页与安全参数;完善业务规则(字段冻结、过期边界、核销 scope 校验、时区规定);为 v1.2 折扣券预埋扩展字段 |
|
||||
| v1.3 | 2026-03-04 | 新增 total_limit(单用户累计兑换上限)字段,补全领券限制覆盖场景说明 |
|
||||
|
||||
---
|
||||
|
||||
@@ -67,11 +69,12 @@
|
||||
|
||||
### 3.3 业务规则
|
||||
|
||||
1. 运营商管理员创建券时,`scope_type` 最高只能选 `2`(本运营商),**不能选** `1`(全平台)
|
||||
2. 运营商管理员创建的指定站点券,`scope_merchant_id` 强制写入本运营商 ID,不可修改
|
||||
3. 平台管理员可查看所有运营商的券;运营商管理员只能查看和管理本运营商创建的券
|
||||
4. 用户兑换或领取券时,系统根据券的 `scope_type` 及关联数据校验该用户是否符合领取资格(如充电站是否在券可用范围内)
|
||||
5. 充电折扣券使用时额外校验:本次充电所在站点是否在券的可用范围内
|
||||
1. 运营商管理员创建券时,`scope_type` 可选范围为 `{2, 3}`,**不能选** `1`(全平台)
|
||||
2. 运营商管理员选择 `scope_type=3`(指定站点)时,后端强制校验所选站点必须属于本运营商,不可选其他运营商站点;`coupon_template_scope` 中记录的 `scope_id` 均需经过此校验
|
||||
3. 平台管理员可查看所有运营商的券;运营商管理员只能查看和管理本运营商(`creator_merchant_id` 匹配)创建的券
|
||||
4. 用户兑换券时,系统根据券的 `scope_type` 及关联数据校验用户资格:**用户资格判定优先级**:当前所在充电站 > 最近一次充电站 > 不做限制(兜底)
|
||||
5. 核销时必须传入 `storeId/stationId`,后端校验核销门店是否在券的 `scope` 范围内;不在范围内的核销请求拒绝
|
||||
6. 充电折扣券使用时额外校验:本次充电所在站点是否在券的可用范围内(v1.2 实现)
|
||||
|
||||
---
|
||||
|
||||
@@ -137,10 +140,16 @@
|
||||
### 5.3 洗车券核销流程
|
||||
|
||||
```
|
||||
用户出示洗车券二维码
|
||||
→ 洗车商扫码 → 调用核销接口
|
||||
→ 系统校验:券是否有效 + 是否已使用 + 是否在有效期内
|
||||
→ 更新券状态为「已使用」,记录核销时间、核销门店
|
||||
用户出示洗车券二维码(短时签名 token,有效期 5 分钟)
|
||||
→ 洗车商扫码 → 调用核销接口(携带 storeId + requestId + sign)
|
||||
→ 系统校验:
|
||||
① token 有效性(后端换取真实 couponNo,防止直接暴露)
|
||||
② 券状态是否为「未使用」
|
||||
③ 是否在有效期内
|
||||
④ 核销门店是否在券的 scope 范围内
|
||||
→ 原子更新:UPDATE member_coupon SET status=1 WHERE coupon_no=? AND status=0
|
||||
(原子更新保证并发核销幂等,更新行数=0 则返回"已核销")
|
||||
→ 写入核销日志 coupon_verify_record(成功/失败均记录)
|
||||
→ 核销成功响应
|
||||
```
|
||||
|
||||
@@ -158,18 +167,24 @@
|
||||
| `creator_type` | tinyint | 创建人类型:1=平台管理员 2=运营商管理员 |
|
||||
| `creator_merchant_id` | bigint | 创建人所属运营商 ID(平台管理员为 NULL) |
|
||||
| `scope_type` | tinyint | 可用范围:1=全平台 2=指定运营商 3=指定站点 |
|
||||
| `points_cost` | int | 兑换所需积分(洗车券用) |
|
||||
| `points_cost` | decimal(10,2) | 兑换所需积分(洗车券用,与现网积分精度一致) |
|
||||
| `discount_rate` | decimal(4,2) | 折扣比例(充电折扣券用,如 0.85 表示 85 折) |
|
||||
| `min_charge_amount` | decimal(10,2) | 充电最低消费金额门槛(折扣券,v1.2)|
|
||||
| `max_discount_amount` | decimal(10,2) | 单次最大抵扣金额上限(折扣券,v1.2)|
|
||||
| `exchange_start_time` | datetime | 兑换活动开始时间(NULL=不限) |
|
||||
| `exchange_end_time` | datetime | 兑换活动结束时间(NULL=不限) |
|
||||
| `stock_total` | int | 总库存(-1 表示不限制) |
|
||||
| `stock_remain` | int | 剩余库存 |
|
||||
| `stock_remain` | int | 剩余库存(-1 表示不限制) |
|
||||
| `validity_type` | tinyint | 有效期类型:1=固定日期 2=领取后N天 |
|
||||
| `valid_start_time` | datetime | 固定有效期开始(validity_type=1) |
|
||||
| `valid_end_time` | datetime | 固定有效期结束(validity_type=1) |
|
||||
| `valid_days` | int | 领取后有效天数(validity_type=2) |
|
||||
| `daily_limit` | int | 单用户每日兑换上限(0=不限) |
|
||||
| `monthly_limit` | int | 单用户每月兑换上限(0=不限) |
|
||||
| `daily_limit` | int | 单用户每日兑换上限(0=不限,以 Asia/Shanghai 自然日计算) |
|
||||
| `monthly_limit` | int | 单用户每月兑换上限(0=不限,以 Asia/Shanghai 自然月计算) |
|
||||
| `total_limit` | int | 单用户累计兑换上限(0=不限,如设为 1 则每人终身只能兑换 1 张) |
|
||||
| `status` | tinyint | 状态:0=下架 1=上架 |
|
||||
| `description` | varchar(500) | 券使用说明 |
|
||||
| `update_by` | varchar(64) | 最后修改人账号 |
|
||||
| `create_by` | varchar(64) | 创建人账号 |
|
||||
| `create_time` | datetime | 创建时间 |
|
||||
| `update_time` | datetime | 更新时间 |
|
||||
@@ -186,16 +201,19 @@
|
||||
| `scope_type` | tinyint | 范围类型:2=运营商 3=站点 |
|
||||
| `scope_id` | bigint | 运营商 ID 或站点 ID |
|
||||
|
||||
> 新增索引 `idx_scope_lookup(scope_type, scope_id, template_id)` 支持"按门店/运营商反查可用券模板"的高频查询
|
||||
|
||||
### 6.3 用户券表 `member_coupon`
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `coupon_no` | varchar(32) | 券编号(唯一,用于核销) |
|
||||
| `coupon_no` | varchar(32) | 券编号(唯一,内部使用) |
|
||||
| `exchange_request_id` | varchar(64) | 兑换幂等键(客户端 requestId,唯一索引) |
|
||||
| `template_id` | bigint | 关联券模板 ID |
|
||||
| `member_id` | bigint | 关联会员 ID |
|
||||
| `member_id` | varchar(64) | 关联会员 ID(与现有 member_points_info.memberId 类型一致,varchar) |
|
||||
| `coupon_type` | tinyint | 券类型快照:1=洗车券 2=充电折扣券 |
|
||||
| `points_cost` | int | 兑换时消耗的积分(快照,折扣券为 0) |
|
||||
| `points_cost` | decimal(10,2) | 兑换时消耗的积分(快照,与现网积分精度一致) |
|
||||
| `discount_rate` | decimal(4,2) | 折扣比例快照(洗车券为 NULL) |
|
||||
| `status` | tinyint | 状态:0=未使用 1=已使用 2=已过期 |
|
||||
| `source` | tinyint | 来源:1=积分兑换 |
|
||||
@@ -207,12 +225,27 @@
|
||||
| `create_time` | datetime | 创建时间 |
|
||||
| `del_flag` | char(1) | 删除标志 |
|
||||
|
||||
### 6.4 依赖现有表
|
||||
### 6.4 核销日志表 `coupon_verify_record`
|
||||
|
||||
- `member_points_info`:积分余额更新
|
||||
> 记录每次核销请求(成功/失败均记录),用于风控、追溯与幂等保障
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | bigint PK | 主键 |
|
||||
| `coupon_no` | varchar(32) | 券编号 |
|
||||
| `request_id` | varchar(64) | 核销请求幂等键 |
|
||||
| `store_id` | bigint | 核销门店 ID |
|
||||
| `operator_id` | varchar(64) | 核销操作人 |
|
||||
| `result` | tinyint | 核销结果:1=成功 2=失败 |
|
||||
| `fail_reason` | varchar(200) | 失败原因(result=2 时记录) |
|
||||
| `create_time` | datetime | 请求时间 |
|
||||
|
||||
### 6.5 依赖现有表
|
||||
|
||||
- `member_points_info`:积分余额更新(`totalPoints` 为 `BigDecimal`,`memberId` 为 `String`)
|
||||
- `member_points_record`:新增消耗类型(`type=3` 积分兑换)
|
||||
- `pile_merchant_info`:运营商信息(校验 creator_merchant_id)
|
||||
- `pile_station_info`:站点信息(校验 scope 范围)
|
||||
- `pile_station_info`:站点信息(校验 scope 范围、反查站点所属运营商)
|
||||
|
||||
---
|
||||
|
||||
@@ -222,37 +255,62 @@
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/coupon/template/list` | GET | 获取用户可兑换券模板列表(按用户所属范围过滤) |
|
||||
| `/coupon/exchange` | POST | 积分兑换(入参:templateId) |
|
||||
| `/coupon/my/list` | GET | 我的券包列表(入参:status) |
|
||||
| `/coupon/my/detail/{couponNo}` | GET | 券详情(含核销二维码) |
|
||||
| `/coupon/template/list` | GET | 获取用户可兑换券模板列表;入参:`stationId`(当前所在站点,用于 scope 过滤)、`pageNum`、`pageSize` |
|
||||
| `/coupon/exchange` | POST | 积分兑换;入参:`templateId`、`requestId`(客户端幂等键) |
|
||||
| `/coupon/my/list` | GET | 我的券包列表;入参:`status`(0/1/2)、`pageNum`、`pageSize` |
|
||||
| `/coupon/my/detail/{couponNo}` | GET | 券详情(含短时签名二维码 token,有效期 5 分钟) |
|
||||
|
||||
### 管理后台
|
||||
|
||||
| 接口 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/admin/coupon/template/list` | GET | 券模板列表(运营商管理员仅返回本运营商数据) |
|
||||
| `/admin/coupon/template/list` | GET | 券模板列表;入参:`pageNum`、`pageSize`;运营商管理员仅返回本运营商数据 |
|
||||
| `/admin/coupon/template/add` | POST | 新增券模板(含 scope 权限校验) |
|
||||
| `/admin/coupon/template/edit` | PUT | 编辑券模板 |
|
||||
| `/admin/coupon/template/edit` | PUT | 编辑券模板(已有兑换记录时冻结核心字段,后端校验) |
|
||||
| `/admin/coupon/template/changeStatus` | PUT | 上下架 |
|
||||
| `/admin/coupon/record/list` | GET | 兑换记录查询(运营商管理员仅查本运营商数据) |
|
||||
| `/admin/coupon/verify` | POST | 核销券(入参:couponNo) |
|
||||
| `/admin/coupon/stats` | GET | 数据统计(运营商管理员仅查本运营商数据) |
|
||||
| `/admin/coupon/record/list` | GET | 兑换记录查询;入参:`pageNum`、`pageSize`;运营商管理员仅查本运营商数据 |
|
||||
| `/admin/coupon/verify` | POST | 核销券;入参:`couponNo`(或签名 token)、`storeId`、`requestId`(幂等键)、`operatorId` |
|
||||
| `/admin/coupon/stats` | GET | 数据统计;运营商管理员仅查本运营商数据 |
|
||||
| `/admin/coupon/invalidate` | POST | 人工作废券(需审计日志);入参:`couponNo`、`reason` |
|
||||
|
||||
---
|
||||
|
||||
## 八、关键业务规则
|
||||
|
||||
1. **原子性保障**:扣减积分与生成券记录必须在同一事务中完成,防止积分扣了但券没生成
|
||||
2. **库存防超卖**:使用数据库乐观锁(`UPDATE ... WHERE stock_remain >= 1`)或 Redis 原子操作控制库存
|
||||
3. **幂等兑换**:前端提交带客户端请求 ID,防止重复点击导致多次扣分
|
||||
4. **券码安全**:`coupon_no` 使用 UUID 或带校验位的随机串,不可预测
|
||||
5. **过期处理**:Quartz 定时任务每日凌晨扫描并更新过期券状态
|
||||
6. **积分变更日志**:兑换成功后写入 `member_points_record`,确保积分流水可追溯
|
||||
2. **库存防超卖**:主方案为数据库原子更新(`UPDATE coupon_template SET stock_remain = stock_remain - 1 WHERE id=? AND stock_remain >= 1`);`stock_remain=-1` 表示不限库存,跳过此校验。不采用乐观锁版本号,避免重试逻辑复杂化
|
||||
3. **幂等兑换**:前端每次兑换请求携带 `requestId`(客户端生成 UUID),后端在 `member_coupon` 表的 `exchange_request_id` 字段建唯一索引;重复请求命中唯一索引冲突时直接返回已有结果,不报错
|
||||
4. **券码安全**:`coupon_no` 使用 UUID,不直接暴露给核销方;二维码内容为短时签名 token(有效期 5 分钟),后端解析 token 后取真实 `coupon_no` 执行核销
|
||||
5. **过期处理**:查询侧(我的券包、兑换列表)按 `expire_time < now()` 动态判定过期状态;Quartz 定时任务每日凌晨仅做批量归档修正(将已过期但状态仍为 0 的券更新为 2),避免状态滞后影响用户体验
|
||||
6. **积分变更日志**:兑换成功后写入 `member_points_record`,`type=3`(积分兑换消耗),确保积分流水可追溯
|
||||
7. **Scope 权限隔离**:
|
||||
- 运营商管理员提交创建/编辑接口时,后端强制校验 `scope_type <= 2`,且 `creator_merchant_id` 必须与登录账号所属运营商一致,防止越权
|
||||
- 运营商管理员提交创建/编辑接口时,后端强制校验 `scope_type ∈ {2,3}`(不能为 1),且 `creator_merchant_id` 必须与登录账号所属运营商一致,防止越权
|
||||
- `scope_type=3` 时,后端逐一校验 `coupon_template_scope` 中的 `scope_id` 均属于本运营商站点
|
||||
- 运营商管理员只能查看和操作本运营商创建的券模板,列表接口自动按 `creator_merchant_id` 过滤
|
||||
8. **折扣券使用校验**(v1.2 实现):充电结算时校验充电站是否在折扣券的 `scope` 范围内,不在范围内的券不可抵扣
|
||||
8. **模板字段冻结规则**:券模板一旦有用户完成兑换(`member_coupon` 存在对应记录),以下字段**不可修改**:`type`、`points_cost`、`validity_type`、`valid_days`(防止已兑换用户权益受损);可修改字段:`name`、`description`、`stock_total`、`daily_limit`、`monthly_limit`、`status`
|
||||
9. **兑换限额时区**:每日/每月兑换上限以 `Asia/Shanghai` 自然日/自然月计算
|
||||
10. **领券限制优先级与校验顺序**(每次兑换按序执行):
|
||||
1. 券模板状态:`status=1`(上架)
|
||||
2. 兑换时间段:`exchange_start_time <= now() <= exchange_end_time`(NULL 表示不限)
|
||||
3. 全局库存:`stock_remain >= 1`(-1 表示不限,跳过此步)
|
||||
4. 单用户累计上限:`total_limit > 0` 时,查询 `member_coupon` 该用户对该模板的历史兑换总数,达上限则拒绝
|
||||
5. 单用户每月上限:`monthly_limit > 0` 时,查询本月兑换数
|
||||
6. 单用户每日上限:`daily_limit > 0` 时,查询今日兑换数
|
||||
7. 积分余额:用户当前积分 >= `points_cost`
|
||||
|
||||
**全部通过后**执行原子操作:`UPDATE stock_remain - 1`(有库存限制时)+ 插入 `member_coupon`
|
||||
|
||||
**典型场景覆盖:**
|
||||
|
||||
| 场景 | 配置示例 |
|
||||
|------|---------|
|
||||
| 不限量、不限人 | `stock_total=-1`,三个 limit 均为 0 |
|
||||
| 总量 50 张,不限人 | `stock_total=50`,三个 limit 均为 0 |
|
||||
| 总量 50 张,每人限 1 张 | `stock_total=50`,`total_limit=1` |
|
||||
| 活动期间(3月)每人每天限 1 张 | `exchange_start/end_time` 设活动期,`daily_limit=1` |
|
||||
| 每人每月最多 3 张、每天最多 1 张 | `monthly_limit=3`,`daily_limit=1` |
|
||||
10. **核销 Scope 校验**:核销接口必须传入 `storeId`,后端校验该门店是否在券的 `scope` 范围内;核销结果(成功/失败)写入 `coupon_verify_record` 日志表
|
||||
11. **折扣券使用校验**(v1.2 实现):充电结算时校验充电站是否在折扣券的 `scope` 范围内,不在范围内的券不可抵扣
|
||||
|
||||
---
|
||||
|
||||
@@ -280,18 +338,24 @@ CREATE TABLE `coupon_template` (
|
||||
`creator_type` TINYINT NOT NULL DEFAULT 1 COMMENT '创建人类型:1=平台管理员 2=运营商管理员',
|
||||
`creator_merchant_id` BIGINT DEFAULT NULL COMMENT '创建人所属运营商ID,平台管理员为NULL',
|
||||
`scope_type` TINYINT NOT NULL DEFAULT 1 COMMENT '可用范围:1=全平台 2=指定运营商 3=指定站点',
|
||||
`points_cost` INT DEFAULT NULL COMMENT '兑换所需积分(洗车券使用)',
|
||||
`points_cost` DECIMAL(10,2) DEFAULT NULL COMMENT '兑换所需积分(洗车券使用,与现网积分精度一致)',
|
||||
`discount_rate` DECIMAL(4, 2) DEFAULT NULL COMMENT '折扣比例(充电折扣券使用,如0.85表示85折)',
|
||||
`min_charge_amount` DECIMAL(10,2) DEFAULT NULL COMMENT '充电最低消费金额门槛(折扣券,v1.2预留)',
|
||||
`max_discount_amount` DECIMAL(10,2) DEFAULT NULL COMMENT '单次最大抵扣金额上限(折扣券,v1.2预留)',
|
||||
`exchange_start_time` DATETIME DEFAULT NULL COMMENT '兑换活动开始时间,NULL=不限',
|
||||
`exchange_end_time` DATETIME DEFAULT NULL COMMENT '兑换活动结束时间,NULL=不限',
|
||||
`stock_total` INT NOT NULL DEFAULT -1 COMMENT '总库存,-1表示不限制',
|
||||
`stock_remain` INT NOT NULL DEFAULT -1 COMMENT '剩余库存,-1表示不限制',
|
||||
`validity_type` TINYINT NOT NULL DEFAULT 2 COMMENT '有效期类型:1=固定日期 2=领取后N天',
|
||||
`valid_start_time` DATETIME DEFAULT NULL COMMENT '固定有效期开始时间(validity_type=1时有效)',
|
||||
`valid_end_time` DATETIME DEFAULT NULL COMMENT '固定有效期结束时间(validity_type=1时有效)',
|
||||
`valid_days` INT DEFAULT NULL COMMENT '领取后有效天数(validity_type=2时有效)',
|
||||
`daily_limit` INT NOT NULL DEFAULT 0 COMMENT '单用户每日兑换上限,0=不限',
|
||||
`monthly_limit` INT NOT NULL DEFAULT 0 COMMENT '单用户每月兑换上限,0=不限',
|
||||
`daily_limit` INT NOT NULL DEFAULT 0 COMMENT '单用户每日兑换上限,0=不限,按Asia/Shanghai自然日计算',
|
||||
`monthly_limit` INT NOT NULL DEFAULT 0 COMMENT '单用户每月兑换上限,0=不限,按Asia/Shanghai自然月计算',
|
||||
`total_limit` INT NOT NULL DEFAULT 0 COMMENT '单用户累计兑换上限,0=不限,如1=每人终身限1张',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=下架 1=上架',
|
||||
`description` VARCHAR(500) DEFAULT NULL COMMENT '券使用说明',
|
||||
`update_by` VARCHAR(64) DEFAULT '' COMMENT '最后修改人账号',
|
||||
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建人账号',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||
@@ -314,7 +378,8 @@ CREATE TABLE `coupon_template_scope` (
|
||||
`scope_id` BIGINT NOT NULL COMMENT '运营商ID 或 站点ID',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_template_id` (`template_id`),
|
||||
UNIQUE KEY `uk_template_scope` (`template_id`, `scope_type`, `scope_id`)
|
||||
UNIQUE KEY `uk_template_scope` (`template_id`, `scope_type`, `scope_id`),
|
||||
KEY `idx_scope_lookup` (`scope_type`, `scope_id`, `template_id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COMMENT = '优惠券可用范围明细表';
|
||||
@@ -324,35 +389,59 @@ CREATE TABLE `coupon_template_scope` (
|
||||
-- 用户券表
|
||||
-- ----------------------------
|
||||
CREATE TABLE `member_coupon` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`coupon_no` VARCHAR(32) NOT NULL COMMENT '券编号(唯一,用于核销)',
|
||||
`template_id` BIGINT NOT NULL COMMENT '关联券模板ID',
|
||||
`member_id` BIGINT NOT NULL COMMENT '关联会员ID',
|
||||
`coupon_type` TINYINT NOT NULL COMMENT '券类型快照:1=洗车券 2=充电折扣券',
|
||||
`points_cost` INT NOT NULL DEFAULT 0 COMMENT '兑换时消耗的积分快照,折扣券为0',
|
||||
`discount_rate` DECIMAL(4, 2) DEFAULT NULL COMMENT '折扣比例快照,洗车券为NULL',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未使用 1=已使用 2=已过期',
|
||||
`source` TINYINT NOT NULL DEFAULT 1 COMMENT '来源:1=积分兑换',
|
||||
`exchange_time` DATETIME DEFAULT NULL COMMENT '兑换时间',
|
||||
`expire_time` DATETIME NOT NULL COMMENT '过期时间',
|
||||
`use_time` DATETIME DEFAULT NULL COMMENT '核销时间',
|
||||
`use_store_id` BIGINT DEFAULT NULL COMMENT '核销门店/站点ID',
|
||||
`use_operator` VARCHAR(64) DEFAULT NULL COMMENT '核销操作人',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志:0=存在 2=删除',
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`coupon_no` VARCHAR(32) NOT NULL COMMENT '券编号(唯一,内部使用)',
|
||||
`exchange_request_id` VARCHAR(64) DEFAULT NULL COMMENT '兑换幂等键(客户端requestId),唯一索引防重复兑换',
|
||||
`template_id` BIGINT NOT NULL COMMENT '关联券模板ID',
|
||||
`member_id` VARCHAR(64) NOT NULL COMMENT '关联会员ID(与member_points_info.memberId类型一致)',
|
||||
`coupon_type` TINYINT NOT NULL COMMENT '券类型快照:1=洗车券 2=充电折扣券',
|
||||
`points_cost` DECIMAL(10,2) NOT NULL DEFAULT 0 COMMENT '兑换时消耗的积分快照,与现网积分精度一致',
|
||||
`discount_rate` DECIMAL(4, 2) DEFAULT NULL COMMENT '折扣比例快照,洗车券为NULL',
|
||||
`status` TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0=未使用 1=已使用 2=已过期',
|
||||
`source` TINYINT NOT NULL DEFAULT 1 COMMENT '来源:1=积分兑换',
|
||||
`exchange_time` DATETIME DEFAULT NULL COMMENT '兑换时间',
|
||||
`expire_time` DATETIME NOT NULL COMMENT '过期时间',
|
||||
`use_time` DATETIME DEFAULT NULL COMMENT '核销时间',
|
||||
`use_store_id` BIGINT DEFAULT NULL COMMENT '核销门店/站点ID',
|
||||
`use_operator` VARCHAR(64) DEFAULT NULL COMMENT '核销操作人',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志:0=存在 2=删除',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_coupon_no` (`coupon_no`),
|
||||
UNIQUE KEY `uk_exchange_request` (`exchange_request_id`),
|
||||
KEY `idx_member_id` (`member_id`, `status`),
|
||||
KEY `idx_template_id` (`template_id`),
|
||||
KEY `idx_expire_time` (`expire_time`, `status`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COMMENT = '会员优惠券表';
|
||||
|
||||
|
||||
-- ----------------------------
|
||||
-- 核销日志表
|
||||
-- ----------------------------
|
||||
CREATE TABLE `coupon_verify_record` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
`coupon_no` VARCHAR(32) NOT NULL COMMENT '券编号',
|
||||
`request_id` VARCHAR(64) DEFAULT NULL COMMENT '核销请求幂等键',
|
||||
`store_id` BIGINT DEFAULT NULL COMMENT '核销门店ID',
|
||||
`operator_id` VARCHAR(64) DEFAULT NULL COMMENT '核销操作人',
|
||||
`result` TINYINT NOT NULL COMMENT '核销结果:1=成功 2=失败',
|
||||
`fail_reason` VARCHAR(200) DEFAULT NULL COMMENT '失败原因',
|
||||
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '请求时间',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_coupon_no` (`coupon_no`),
|
||||
KEY `idx_request_id` (`request_id`)
|
||||
) ENGINE = InnoDB
|
||||
DEFAULT CHARSET = utf8mb4
|
||||
COMMENT = '优惠券核销日志表';
|
||||
```
|
||||
|
||||
> **索引说明**
|
||||
> - `coupon_template.idx_creator_merchant`:运营商管理员查看本运营商券模板列表的高频查询
|
||||
> - `coupon_template_scope.uk_template_scope`:防止同一模板下重复添加相同范围记录
|
||||
> - `coupon_template_scope.idx_scope_lookup`:按门店/运营商反查可用券模板(兑换列表过滤高频查询)
|
||||
> - `member_coupon.uk_exchange_request`:兑换幂等保障,重复提交命中唯一索引冲突直接返回
|
||||
> - `member_coupon.idx_expire_time`:供 Quartz 定时任务扫描过期券使用
|
||||
> - `member_coupon.uk_coupon_no`:唯一索引防止重复券码、保证核销安全
|
||||
|
||||
|
||||
Reference in New Issue
Block a user