Files
jsowell-charger-web/docs/PRD-积分兑换洗车券功能.md
2026-03-04 15:27:49 +08:00

458 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PRD优惠券功能
**版本**: v1.2
**日期**: 2026-03-04
**项目**: 万车充运营管理平台
**状态**: 草稿
---
## 变更记录
| 版本 | 日期 | 变更内容 |
|------|------|---------|
| 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单用户累计兑换上限字段补全领券限制覆盖场景说明 |
---
## 一、背景与目标
### 背景
平台现有积分系统(`member_points_info`)支持充电消费奖励积分,但积分使用场景单一(仅支持抵扣充电费用)。为提升用户留存与活跃度,引入优惠券体系,首期以「积分兑换洗车券」为切入点,同时建立支持平台与运营商双层管理的券权限体系,为后续充电折扣券等功能奠定基础。
### 目标
- 为用户提供积分消耗渠道,提升积分价值感知
- 通过洗车权益与充电绑定,增强用户充电粘性
- 建立可扩展的优惠券基础架构,支持后续更多券类型
- 建立平台与运营商双层券管理权限,支持精细化运营
### 成功指标
- 积分兑换率(兑换用户数 / 持有积分用户数)≥ 15%
- 洗车券核销率 ≥ 60%
- 功能上线后次月充电频次同比提升 ≥ 5%
---
## 二、用户角色
| 角色 | 类型 | 说明 |
|------|------|------|
| **平台管理员** | 创券方 | 平台运营人员,可创建全平台范围或指定运营商范围的优惠券 |
| **运营商管理员** | 创券方 | 运营商后台人员,只能创建本运营商范围内可用的优惠券 |
| **会员用户** | 使用方 | 通过充电获得积分,在 App/小程序中兑换并使用券 |
| **合作洗车商** | 核销方 | 扫码核销洗车券(可通过独立 H5 页面或 API 对接) |
---
## 三、券可用范围权限设计
### 3.1 核心规则
| 创建人角色 | 可设置的最大可用范围 | 说明 |
|-----------|-------------------|------|
| **平台管理员** | 全平台 | 可选:全平台 / 指定运营商 / 指定站点 |
| **运营商管理员** | 本运营商 | 可选:本运营商全部站点 / 指定站点(仅限本运营商下) |
### 3.2 可用范围枚举
| 范围值 | 说明 | 可创建角色 |
|--------|------|----------|
| `1` - 全平台 | 所有运营商、所有站点均可使用 | 仅平台管理员 |
| `2` - 指定运营商 | 仅指定运营商下的站点可使用 | 平台管理员(可多选);运营商管理员(固定为本运营商) |
| `3` - 指定站点 | 仅指定站点可使用 | 平台管理员;运营商管理员(仅限本运营商下的站点) |
### 3.3 业务规则
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 实现)
---
## 四、功能范围(首期 v1.0
### 4.1 功能清单
| 模块 | 功能点 | 优先级 |
|------|--------|--------|
| 券模板管理 | 平台管理员创建/编辑/下架洗车券模板 | P0 |
| 券模板管理 | 运营商管理员创建/编辑/下架洗车券模板 | P0 |
| 券模板管理 | 设置券可用范围(全平台 / 指定运营商 / 指定站点) | P0 |
| 券模板管理 | 设置积分兑换比例N 积分 = 1 张券) | P0 |
| 券模板管理 | 设置券有效期(固定日期 / 领取后 N 天) | P0 |
| 积分兑换 | 用户查看可兑换券列表 | P0 |
| 积分兑换 | 用户用积分兑换洗车券 | P0 |
| 积分兑换 | 兑换记录查询 | P0 |
| 我的券包 | 查看持有的洗车券(未使用/已使用/已过期) | P0 |
| 券核销 | 展示券码(二维码 / 核销码) | P0 |
| 券核销 | 合作商扫码核销 | P0 |
| 数据看板 | 兑换量、核销量、积分消耗统计 | P1 |
| 运营配置 | 单用户每日/每月兑换上限 | P1 |
| 运营配置 | 券库存上限控制 | P1 |
### 4.2 首期不包含
- 现金购买券
- 券转赠
- 充电折扣券(规划于 v1.2
- 第三方洗车平台 API 对接(仅本地核销)
---
## 五、业务流程
### 5.1 创建券模板流程
```
管理员进入券模板管理页
→ 选择券类型(首期仅洗车券)
→ 系统根据登录角色自动限制可用范围选项
├─ 平台管理员:可选 全平台 / 指定运营商 / 指定站点
└─ 运营商管理员:可选 本运营商 / 本运营商下指定站点
→ 填写积分兑换价格、库存、有效期、兑换限额等配置
→ 提交 → 系统二次校验 scope 权限边界
→ 保存,默认下架状态,手动上架后对用户可见
```
### 5.2 积分兑换流程
```
用户进入积分商城
→ 系统查询该用户可见的洗车券列表
(过滤逻辑:券为上架状态 + 未过期 + 有库存 + 用户所在运营商/站点在券可用范围内)
→ 用户查看洗车券兑换入口(展示所需积分、库存、有效期)
→ 确认兑换
→ 系统校验:积分是否足够 + 库存是否充足 + 兑换限额检查
→ 原子操作:扣减积分 + 生成用户券记录
→ 写入积分变更日志member_points_record类型=积分兑换消耗)
→ 返回兑换成功,展示券码
```
### 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成功/失败均记录)
→ 核销成功响应
```
---
## 六、数据库设计
### 6.1 券模板表 `coupon_template`
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint PK | 主键 |
| `name` | varchar(50) | 券名称,如"洗车券" |
| `type` | tinyint | 券类型1=洗车券 2=充电折扣券 |
| `creator_type` | tinyint | 创建人类型1=平台管理员 2=运营商管理员 |
| `creator_merchant_id` | bigint | 创建人所属运营商 ID平台管理员为 NULL |
| `scope_type` | tinyint | 可用范围1=全平台 2=指定运营商 3=指定站点 |
| `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 | 剩余库存(-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=不限,以 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 | 更新时间 |
| `del_flag` | char(1) | 删除标志 |
### 6.2 券可用范围明细表 `coupon_template_scope`
> 用于存储 `scope_type=2`(指定运营商)或 `scope_type=3`(指定站点)时的具体关联数据
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint PK | 主键 |
| `template_id` | bigint | 关联券模板 ID |
| `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) | 券编号(唯一,内部使用) |
| `exchange_request_id` | varchar(64) | 兑换幂等键(客户端 requestId唯一索引 |
| `template_id` | bigint | 关联券模板 ID |
| `member_id` | varchar(64) | 关联会员 ID与现有 member_points_info.memberId 类型一致varchar |
| `coupon_type` | tinyint | 券类型快照1=洗车券 2=充电折扣券 |
| `points_cost` | decimal(10,2) | 兑换时消耗的积分(快照,与现网积分精度一致) |
| `discount_rate` | decimal(4,2) | 折扣比例快照(洗车券为 NULL |
| `status` | tinyint | 状态0=未使用 1=已使用 2=已过期 |
| `source` | tinyint | 来源1=积分兑换 |
| `exchange_time` | datetime | 兑换时间 |
| `expire_time` | datetime | 过期时间 |
| `use_time` | datetime | 核销时间 |
| `use_store_id` | bigint | 核销门店/站点 ID |
| `use_operator` | varchar(64) | 核销操作人 |
| `create_time` | datetime | 创建时间 |
| `del_flag` | char(1) | 删除标志 |
### 6.4 核销日志表 `coupon_verify_record`
> 记录每次核销请求(成功/失败均记录),用于风控、追溯与幂等保障
| 字段 | 类型 | 说明 |
|------|------|------|
| `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 范围、反查站点所属运营商)
---
## 七、接口设计
### App/小程序端
| 接口 | 方法 | 说明 |
|------|------|------|
| `/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 | 券模板列表;入参:`pageNum``pageSize`;运营商管理员仅返回本运营商数据 |
| `/admin/coupon/template/add` | POST | 新增券模板(含 scope 权限校验) |
| `/admin/coupon/template/edit` | PUT | 编辑券模板(已有兑换记录时冻结核心字段,后端校验) |
| `/admin/coupon/template/changeStatus` | PUT | 上下架 |
| `/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 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,3}`(不能为 1`creator_merchant_id` 必须与登录账号所属运营商一致,防止越权
- `scope_type=3` 时,后端逐一校验 `coupon_template_scope` 中的 `scope_id` 均属于本运营商站点
- 运营商管理员只能查看和操作本运营商创建的券模板,列表接口自动按 `creator_merchant_id` 过滤
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` 范围内,不在范围内的券不可抵扣
---
## 九、非功能需求
| 项目 | 要求 |
|------|------|
| 性能 | 兑换接口 P99 < 500ms |
| 并发 | 支持同一券模板 100 QPS 并发兑换 |
| 可靠性 | 积分扣减与券生成保证原子性 |
| 安全 | 核销接口需鉴权;创券接口需强制 scope 边界校验,防止运营商越权 |
---
## 十、数据库建表语句
```sql
-- ----------------------------
-- 券模板表
-- ----------------------------
CREATE TABLE `coupon_template` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` VARCHAR(50) NOT NULL COMMENT '券名称,如洗车券',
`type` TINYINT NOT NULL DEFAULT 1 COMMENT '券类型1=洗车券 2=充电折扣券',
`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` 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=不限按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 '更新时间',
`del_flag` CHAR(1) NOT NULL DEFAULT '0' COMMENT '删除标志0=存在 2=删除',
PRIMARY KEY (`id`),
KEY `idx_creator_merchant` (`creator_merchant_id`, `status`, `del_flag`),
KEY `idx_scope_type` (`scope_type`, `status`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COMMENT = '优惠券模板表';
-- ----------------------------
-- 券可用范围明细表scope_type=2 或 3 时使用)
-- ----------------------------
CREATE TABLE `coupon_template_scope` (
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`template_id` BIGINT NOT NULL COMMENT '关联券模板ID',
`scope_type` TINYINT NOT NULL COMMENT '范围类型2=运营商 3=站点',
`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`),
KEY `idx_scope_lookup` (`scope_type`, `scope_id`, `template_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COMMENT = '优惠券可用范围明细表';
-- ----------------------------
-- 用户券表
-- ----------------------------
CREATE TABLE `member_coupon` (
`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`:唯一索引防止重复券码、保证核销安全
---
## 十一、后续迭代规划
| 阶段 | 功能 |
|------|------|
| v1.1 | 第三方洗车平台 API 对接(自动核销) |
| v1.2 | 充电折扣券:运营商/平台创建折扣券,充电结算时自动抵扣,校验站点范围 |
| v1.3 | 运营活动:充电满额赠券、节日送券 |
| v2.0 | 积分商城完整体验(多商品、排行榜) |