# 专属优惠充值 + 线下充值 功能说明 > 版本: v7 > 日期: 2026-06-03 > 作者: skyline --- ## 一、专属充值优惠活动 ### 1.1 功能概述 运营后台创建专属充值配置(如充值 100 元送 20 元),生成小程序二维码。用户扫码进入小程序,在有效期内以专属优惠完成充值。二维码支持多人使用,可限定目标用户范围。 ### 1.2 业务流程 ``` 运营后台 微信小程序 后端 | | | |-- 创建优惠活动 --------->| | | | | |-- 生成小程序二维码 ----->| | | (wxacode.getUnlimited)| | | | | | |-- 用户扫码 --> 小程序启动 --| | | App.vue 检测 scene | | | 跳转优惠充值页 | | | | | |-- GET /common/promotionInfo -> | | (查询活动详情) | | | | | |-- GET /payment/promotionPay -> | | (发起微信支付) | | | | | |-- 微信支付成功回调 --------->| | | wxNotify 检测 promotionId | | | 使用活动赠费更新余额 | ``` ### 1.3 数据库 **新表: `t_recharge_promotion`** | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键 | | title | VARCHAR(128) | 活动标题 | | recharge_amount | INT | 充值金额(分) | | grants_amount | INT | 赠送金额(分) | | target_mode | TINYINT | 1=所有人, 2=指定用户 | | target_user_ids | TEXT | 目标用户ID列表(JSON数组),null=不限制 | | start_time | DATETIME | 活动开始时间 | | end_time | DATETIME | 活动结束时间 | | status | TINYINT | 0=禁用, 1=启用 | | token | VARCHAR(32) | 二维码场景值(唯一标识) | | qr_code_url | VARCHAR(512) | 二维码图片URL | `t_wallet_detail` 新增字段: | 字段 | 类型 | 说明 | |------|------|------| | source | VARCHAR(16) | WX_PAY / MANUAL | | promotion_id | BIGINT | 关联优惠活动ID,null=非活动充值 | ### 1.4 API 接口 #### 管理后台 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/rechargePromotion/list` | promotion.list | 分页查询活动列表 | | GET | `/rechargePromotion/detail/{id}` | — | 查询活动详情 | | POST | `/rechargePromotion/add` | promotion.add | 新增活动(自动生成token) | | POST | `/rechargePromotion/modify` | promotion.modify | 修改活动 | | GET | `/rechargePromotion/remove/{id}` | promotion.remove | 删除活动 | | GET | `/rechargePromotion/generateQR/{id}` | promotion.list | 生成小程序二维码(上传OSS) | #### 小程序端 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/common/promotionInfo?token=xxx` | 查询优惠活动详情(含有效期校验) | | GET | `/payment/promotionPay?promotionToken=xxx&stationId=xxx` | 发起优惠活动充值支付,返回JSAPI参数 | ### 1.5 关键逻辑 **支付回调区分优惠活动与普通充值:** `WxPayServiceImpl.wxNotify()` 中先检查 `walletDetail.promotionId`: - 不为 null → 用优惠活动的 `grantsAmount` - 为 null → 走原有站点充值配置逻辑 **用户资格校验:** `RechargePromotionServiceImpl.checkUserEligible()`: - `targetMode=1`(所有人)→ 直接通过 - `targetMode=2`(指定用户)→ 从 `targetUserIds` JSON 数组解析,检查当前用户ID是否在列表中 --- ## 二、线下充值 ### 2.1 功能概述 用户通过微信转账给运营人员,运营人员在管理后台代为充值,直接更新用户钱包余额。钱不经过微信商户,无微信交易记录。 ### 2.2 业务流程 ``` 用户 --(微信转账)--> 运营人员 | |-- 登录管理后台 → 线下充值页面 |-- 输入用户手机号 → 查询用户 |-- 输入充值金额 + 赠送金额 + 备注 |-- 确认提交 | 后端: 创建 WalletDetail (source=MANUAL, status=已确认) 更新 Account 余额 创建 SplitRecord (走V2结算) 不创建 PayLog (无微信交易) ``` ### 2.3 API 接口 #### 管理后台 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | POST | `/finance/offlineRecharge` | offlineRecharge | 线下充值 | **请求参数 (JSON Body):** ```json { "userId": 10001, "amount": 5000, "grantsAmount": 500, "remark": "微信转账充值,流水号: xxx" } ``` | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | userId | Long | 是 | 用户ID | | amount | Integer | 是 | 充值金额(分) | | grantsAmount | Integer | 否 | 赠送金额(分),默认0 | | remark | String | 否 | 备注信息 | ### 2.4 与线上充值的区别 | | 线上充值(微信支付) | 线下充值(人工) | |------|------|------| | WalletDetail.source | WX_PAY | MANUAL | | WalletDetail.status | 先创建待确认→回调后确认 | 直接已确认 | | PayLog | 创建 | **不创建** | | SplitRecord | 创建 | 创建 | | 退款 | 走微信退款流程 | 暂不支持系统退款(需人工处理) | | 操作日志 | 微信回调日志 | @SysLog "线下充值" | --- ## 三、部署清单 ### 3.1 数据库迁移 执行 SQL: `car-wash-entity/src/main/resources/sql/v7_recharge_promotion.sql` ### 3.2 权限分配 在角色管理中为运营人员分配以下权限: | 权限标识 | 名称 | 菜单 | |------|------|------| | promotion.list | 优惠活动管理 | 平台配置 → 专属优惠活动 | | promotion.add | 优惠活动新增 | 新增按钮 | | promotion.modify | 优惠活动修改 | 编辑按钮 | | promotion.remove | 优惠活动删除 | 删除按钮 | | offlineRecharge | 线下充值 | 财务管理 → 线下充值 | ### 3.3 小程序发布 1. 确保 `pages-user/wallet/promotion` 页面已注册到 `pages.json` 2. 重新发布小程序(新增页面需审核) 3. 确保 `wechat.miniapp.appid` 和 `wechat.miniapp.secret` 配置正确(用于生成小程序码) --- ## 四、注意事项 1. **二维码有效期**:微信小程序码本身无过期时间,有效期由活动的 `start_time` / `end_time` 控制,扫码时后端校验 2. **线下充值不可退款**:线下充值的资金未经过微信商户,系统无法通过微信退款接口原路退回。如需退线下充值款项,需人工线下处理 3. **线下充值的安全性**:依赖 `offlineRecharge` 权限控制,所有操作通过 `@SysLog` 记录审计日志,WalletDetail.remark 字段记录操作备注 4. **活动 token 唯一性**:创建活动时自动生成 16 位随机字符串作为 token,数据库有唯一索引防止冲突,失败时自动重试最多 5 次 5. **小程序码图片存储**:二维码图片上传到阿里云 OSS,URL 存入 `qr_code_url` 字段,重复请求直接返回已有 URL