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

18 KiB
Raw Blame History

PRD优惠券功能

版本: v1.1 日期: 2026-03-03 项目: 万车充运营管理平台 状态: 草稿


变更记录

版本 日期 变更内容
v1.0 2026-03-03 初稿:积分兑换洗车券功能
v1.1 2026-03-03 新增:券创建权限体系(平台人员 vs 运营商人员);新增充电折扣券规划;更新数据库设计与业务规则

一、背景与目标

背景

平台现有积分系统(member_points_info)支持充电消费奖励积分,但积分使用场景单一(仅支持抵扣充电费用)。为提升用户留存与活跃度,引入优惠券体系,首期以「积分兑换洗车券」为切入点,同时建立支持平台与运营商双层管理的券权限体系,为后续充电折扣券等功能奠定基础。

目标

  • 为用户提供积分消耗渠道,提升积分价值感知
  • 通过洗车权益与充电绑定,增强用户充电粘性
  • 建立可扩展的优惠券基础架构,支持后续更多券类型
  • 建立平台与运营商双层券管理权限,支持精细化运营

成功指标

  • 积分兑换率(兑换用户数 / 持有积分用户数)≥ 15%
  • 洗车券核销率 ≥ 60%
  • 功能上线后次月充电频次同比提升 ≥ 5%

二、用户角色

角色 类型 说明
平台管理员 创券方 平台运营人员,可创建全平台范围或指定运营商范围的优惠券
运营商管理员 创券方 运营商后台人员,只能创建本运营商范围内可用的优惠券
会员用户 使用方 通过充电获得积分,在 App/小程序中兑换并使用券
合作洗车商 核销方 扫码核销洗车券(可通过独立 H5 页面或 API 对接)

三、券可用范围权限设计

3.1 核心规则

创建人角色 可设置的最大可用范围 说明
平台管理员 全平台 可选:全平台 / 指定运营商 / 指定站点
运营商管理员 本运营商 可选:本运营商全部站点 / 指定站点(仅限本运营商下)

3.2 可用范围枚举

范围值 说明 可创建角色
1 - 全平台 所有运营商、所有站点均可使用 仅平台管理员
2 - 指定运营商 仅指定运营商下的站点可使用 平台管理员(可多选);运营商管理员(固定为本运营商)
3 - 指定站点 仅指定站点可使用 平台管理员;运营商管理员(仅限本运营商下的站点)

3.3 业务规则

  1. 运营商管理员创建券时,scope_type 最高只能选 2(本运营商),不能选 1(全平台)
  2. 运营商管理员创建的指定站点券,scope_merchant_id 强制写入本运营商 ID不可修改
  3. 平台管理员可查看所有运营商的券;运营商管理员只能查看和管理本运营商创建的券
  4. 用户兑换或领取券时,系统根据券的 scope_type 及关联数据校验该用户是否符合领取资格(如充电站是否在券可用范围内)
  5. 充电折扣券使用时额外校验:本次充电所在站点是否在券的可用范围内

四、功能范围(首期 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 洗车券核销流程

用户出示洗车券二维码
    → 洗车商扫码 → 调用核销接口
    → 系统校验:券是否有效 + 是否已使用 + 是否在有效期内
    → 更新券状态为「已使用」,记录核销时间、核销门店
    → 核销成功响应

六、数据库设计

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 int 兑换所需积分(洗车券用)
discount_rate decimal(4,2) 折扣比例(充电折扣券用,如 0.85 表示 85 折)
stock_total int 总库存(-1 表示不限制)
stock_remain int 剩余库存
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=不限)
status tinyint 状态0=下架 1=上架
description varchar(500) 券使用说明
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

6.3 用户券表 member_coupon

字段 类型 说明
id bigint PK 主键
coupon_no varchar(32) 券编号(唯一,用于核销)
template_id bigint 关联券模板 ID
member_id bigint 关联会员 ID
coupon_type tinyint 券类型快照1=洗车券 2=充电折扣券
points_cost int 兑换时消耗的积分(快照,折扣券为 0
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 依赖现有表

  • member_points_info:积分余额更新
  • member_points_record:新增消耗类型(type=3 积分兑换)
  • pile_merchant_info:运营商信息(校验 creator_merchant_id
  • pile_station_info:站点信息(校验 scope 范围)

七、接口设计

App/小程序端

接口 方法 说明
/coupon/template/list GET 获取用户可兑换券模板列表(按用户所属范围过滤)
/coupon/exchange POST 积分兑换入参templateId
/coupon/my/list GET 我的券包列表入参status
/coupon/my/detail/{couponNo} GET 券详情(含核销二维码)

管理后台

接口 方法 说明
/admin/coupon/template/list GET 券模板列表(运营商管理员仅返回本运营商数据)
/admin/coupon/template/add POST 新增券模板(含 scope 权限校验)
/admin/coupon/template/edit PUT 编辑券模板
/admin/coupon/template/changeStatus PUT 上下架
/admin/coupon/record/list GET 兑换记录查询(运营商管理员仅查本运营商数据)
/admin/coupon/verify POST 核销券入参couponNo
/admin/coupon/stats GET 数据统计(运营商管理员仅查本运营商数据)

八、关键业务规则

  1. 原子性保障:扣减积分与生成券记录必须在同一事务中完成,防止积分扣了但券没生成
  2. 库存防超卖:使用数据库乐观锁(UPDATE ... WHERE stock_remain >= 1)或 Redis 原子操作控制库存
  3. 幂等兑换:前端提交带客户端请求 ID防止重复点击导致多次扣分
  4. 券码安全coupon_no 使用 UUID 或带校验位的随机串,不可预测
  5. 过期处理Quartz 定时任务每日凌晨扫描并更新过期券状态
  6. 积分变更日志:兑换成功后写入 member_points_record,确保积分流水可追溯
  7. Scope 权限隔离
    • 运营商管理员提交创建/编辑接口时,后端强制校验 scope_type <= 2,且 creator_merchant_id 必须与登录账号所属运营商一致,防止越权
    • 运营商管理员只能查看和操作本运营商创建的券模板,列表接口自动按 creator_merchant_id 过滤
  8. 折扣券使用校验v1.2 实现):充电结算时校验充电站是否在折扣券的 scope 范围内,不在范围内的券不可抵扣

九、非功能需求

项目 要求
性能 兑换接口 P99 < 500ms
并发 支持同一券模板 100 QPS 并发兑换
可靠性 积分扣减与券生成保证原子性
安全 核销接口需鉴权;创券接口需强制 scope 边界校验,防止运营商越权

十、数据库建表语句

-- ----------------------------
-- 券模板表
-- ----------------------------
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`          INT                    DEFAULT NULL COMMENT '兑换所需积分(洗车券使用)',
    `discount_rate`        DECIMAL(4, 2)          DEFAULT NULL COMMENT '折扣比例充电折扣券使用如0.85表示85折',
    `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=不限',
    `status`               TINYINT       NOT NULL DEFAULT 0 COMMENT '状态0=下架 1=上架',
    `description`          VARCHAR(500)           DEFAULT NULL 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`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COMMENT = '优惠券可用范围明细表';


-- ----------------------------
-- 用户券表
-- ----------------------------
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=删除',
    PRIMARY KEY (`id`),
    UNIQUE KEY `uk_coupon_no` (`coupon_no`),
    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 = '会员优惠券表';

索引说明

  • coupon_template.idx_creator_merchant:运营商管理员查看本运营商券模板列表的高频查询
  • coupon_template_scope.uk_template_scope:防止同一模板下重复添加相同范围记录
  • member_coupon.idx_expire_time:供 Quartz 定时任务扫描过期券使用
  • member_coupon.uk_coupon_no:唯一索引防止重复券码、保证核销安全

十一、后续迭代规划

阶段 功能
v1.1 第三方洗车平台 API 对接(自动核销)
v1.2 充电折扣券:运营商/平台创建折扣券,充电结算时自动抵扣,校验站点范围
v1.3 运营活动:充电满额赠券、节日送券
v2.0 积分商城完整体验(多商品、排行榜)