运营希望指定某几个商品,用户原价购买后赠送会员,不同商品赠送不同级别的会员(如商品 A 送 FAMILY、商品 B 送 PREMIUM)。
| 决策点 | 结论 |
|---|---|
| 赠送级别 | 任意有效等级(从 membership_levels 表下拉选择,过滤 FREE) |
| 时长 | 不送时长,送级别;有效期按该级别年费周期(默认 365 天) |
| 赠送对象 | 家庭创建者(users.member_level 权威字段,与现有开通流程一致,全家共享) |
| 已有会员 | 家庭已有同级或更高级有效会员时不送;无会员或等级更低则送(升级) |
| 原价判定 | 无折扣无抵扣:pointsUsed == 0 && couponId == null && discountAmount 为空 |
| 退款 | 本期不回收会员(与测评额度逻辑一致) |
product_gift_rulesCREATE TABLE IF NOT EXISTS product_gift_rules (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
product_id BIGINT NOT NULL COMMENT '关联products表',
level_code VARCHAR(50) NOT NULL COMMENT '赠送会员等级编码(FAMILY/PREMIUM...)',
status TINYINT(1) DEFAULT 1 COMMENT '1=启用 0=停用',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_product_level (product_id, level_code),
INDEX idx_product_id (product_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品赠送会员规则';
assessment_products(测评商品扩展表)完全同构:一商品一规则(UNIQUE 约束),status 支持随时停用赠品活动而不动商品。entity/ProductGiftRule.java — MyBatis-Plus 实体(@TableName("product_gift_rules"))mapper/ProductGiftRuleMapper.java — Mapperservice/ProductGiftRuleService.java — 规则查询/保存/停用(@Resource 注入,遵循项目 DI 规范)DatabaseInitializer.runMigrations() 添加建表语句(幂等 CREATE TABLE IF NOT EXISTS + log.info),并同步 schema.sql。POST /api/admin/product-gift-rule/save(productId, levelCode, status)— 新增/更新规则ProductOrderService.handlePaymentSuccess(orderNo, transactionId)在现有"测评商品发放额度"逻辑之后追加赠送会员逻辑(两者模式完全一致,失败仅记日志、不阻塞支付流程):
支付成功
→ 查 product_gift_rules(productId 匹配 && status=1)
→ 校验"原价购买":order.pointsUsed==0 && order.couponId==null
&& (order.discountAmount==null || order.discountAmount==0)
→ 调 membershipService.grantMembershipByGift(...)
→ 异常捕获:log.error,不抛错
MembershipService.grantMembershipByGift仿照 processPaymentCallback 核心(写 FamilyMembership + 更新 users + 写升级记录),差异点:
| 项目 | 行为 |
|---|---|
| 参数 | (familyId, userId, levelCode, sourceOrderNo, transactionId) |
| 目标用户 | 家庭创建者(查 family.creatorId;与现有流程一致) |
| 前置校验 | 家庭创建者无有效会员,或有效等级低于赠送等级(等级高低按 membership_levels.id 排序)→ 否则跳过 |
| 有效期 | 按级别年费周期:默认 +365 天(沿用 upgradeToFamily 的 365 天常量逻辑) |
| 订阅记录 | 插入 family_memberships(paymentStatus 沿用现有 'paid' 值,不引入新枚举) |
| 升级记录 | member_upgrade_records.upgrade_type = 'gift'(新增类型) |
| 佣金 | 跳过佣金结算(赠送非销售) |
membership_levels 表按 id 自增顺序即等级从低到高(FREE < FAMILY < PREMIUM < PROVIDER)。比较逻辑:
当前等级 = getMemberLevel(userId)(校验过期,返回 FREE 视为无有效会员)
当前等级 id >= 赠送等级 id → 跳过不送
否则 → 赠送(升级)
ProductService 详情组装时查询 product_gift_rules(status=1),将 giftRule.levelCode + 等级名附带进详情 DTO。pages/discover-detail/product-detail/product-detail.vue 显示"购买即送 XX 会员"徽标(等级名来自 membership_levels 或前端映射)。ProductEdit.vue 新增"赠送会员"区块:
POST /api/membership/levels,过滤 FREE,显示 level_namestatus(1=启用 0=停用)POST /api/admin/product-gift-rule/save| 场景 | 行为 |
|---|---|
| 家庭已有同级/更高级会员 | 不送(订单正常完成) |
| 家庭无会员 / 等级更低 | 赠送(低等级则升级) |
| 用积分/优惠券/折扣购买 | 不送(非原价) |
| 赠品规则停用(status=0) | 不送 |
| 赠送失败(异常) | 记日志,支付流程不受影响 |
| 退款 | 本期不回收会员(与测评额度逻辑一致) |
mvn clean compile 通过(项目规范)npm run build:mp-weixin 构建通过;cfc-web 构建通过mvn clean package -DskipTests(部署脚本命令)通过entity/ProductGiftRule.javamapper/ProductGiftRuleMapper.javaservice/ProductGiftRuleService.javaconfig/DatabaseInitializer.java(建表迁移)resources/schema.sql(同步建表)service/ProductOrderService.java(handlePaymentSuccess 追加赠送逻辑)service/MembershipService.java(新增 grantMembershipByGift)controller/admin/ 规则管理接口service/ProductService.java(详情附带 giftRule)pages/discover-detail/product-detail/product-detail.vue(赠送徽标)src/views/admin/ProductEdit.vue(赠送会员区块)src/api/ 或对应 API 封装(save 规则接口)