diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/controller/PlayCouponInfoController.java b/play-admin/src/main/java/com/starry/admin/modules/shop/controller/PlayCouponInfoController.java index 34d8c84..4cc61f4 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/controller/PlayCouponInfoController.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/controller/PlayCouponInfoController.java @@ -144,6 +144,10 @@ public class PlayCouponInfoController { } entity.setDiscountContent(discountContent); entity.setCouponOnLineState("1"); + // 领取白名单校验:当为2时必须配置白名单 + if ("2".equals(vo.getClaimConditionType()) && (vo.getCustomWhitelist() == null || vo.getCustomWhitelist().isEmpty())) { + throw new CustomException("领取条件为白名单时,必须设置领取白名单"); + } boolean success = playCouponInfoService.create(entity); if (success) { return R.ok(); diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/module/entity/PlayCouponInfoEntity.java b/play-admin/src/main/java/com/starry/admin/modules/shop/module/entity/PlayCouponInfoEntity.java index f22e149..5d0ed94 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/module/entity/PlayCouponInfoEntity.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/module/entity/PlayCouponInfoEntity.java @@ -118,10 +118,16 @@ public class PlayCouponInfoEntity extends BaseEntity { private Integer clerkObtainedMaxQuantity; /** - * 领取条件类型(0:所有人可领取,1:指定条件领取) + * 领取条件类型:0=所有人可领取;1=按条件领取;2=仅白名单可领取。 */ private String claimConditionType; + /** + * 领取白名单(claimConditionType=2 生效)。JSON存储用户ID列表。 + */ + @TableField(typeHandler = StringTypeHandler.class) + private List customWhitelist; + /** * 顾客等级选择状态(0:未选择,1:选择) */ diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/module/enums/CouponClaimConditionType.java b/play-admin/src/main/java/com/starry/admin/modules/shop/module/enums/CouponClaimConditionType.java new file mode 100644 index 0000000..299646b --- /dev/null +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/module/enums/CouponClaimConditionType.java @@ -0,0 +1,25 @@ +package com.starry.admin.modules.shop.module.enums; + +/** + * 优惠券领取条件类型 + */ +public enum CouponClaimConditionType { + ALL("0"), + FILTER("1"), + WHITELIST("2"); + + private final String code; + + CouponClaimConditionType(String code) { + this.code = code; + } + + public String code() { return code; } + + public static CouponClaimConditionType of(String code) { + for (CouponClaimConditionType t : values()) { + if (t.code.equals(code)) return t; + } + return ALL; + } +} diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoAddVo.java b/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoAddVo.java index 972dbd4..34f474c 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoAddVo.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoAddVo.java @@ -120,12 +120,17 @@ public class PlayCouponInfoAddVo { private Integer clerkObtainedMaxQuantity; /** - * 领取条件类型(0:所有人可领取,1:指定条件领取) + * 领取条件类型:0=所有人;1=按条件;2=白名单。 */ @NotNull(message = "领取条件类型不能为空") - @Pattern(regexp = "[0|1]", message = "店员范围只能为0或者1") + @Pattern(regexp = "^(0|1|2)$", message = "领取条件类型只能为0或1或2") private String claimConditionType; + /** + * 领取白名单(claimConditionType=2 时校验):用户ID列表 + */ + private List customWhitelist; + /** * 顾客等级选择状态(0:未选择,1:选择) */ diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoReturnVo.java b/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoReturnVo.java index 8e00e02..18bb1d1 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoReturnVo.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/module/vo/PlayCouponInfoReturnVo.java @@ -109,10 +109,16 @@ public class PlayCouponInfoReturnVo { private Integer clerkObtainedMaxQuantity; /** - * 领取条件类型(0:所有人可领取,1:指定条件领取) + * 领取条件类型:0=所有人;1=按条件;2=白名单。 */ private String claimConditionType; + /** + * 领取白名单(claimConditionType=2 生效) + */ + @TableField(typeHandler = StringTypeHandler.class) + private List customWhitelist; + /** * 顾客等级选择状态(0:未选择,1:选择) */ diff --git a/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayCouponInfoServiceImpl.java b/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayCouponInfoServiceImpl.java index c7a3911..9dbbb9d 100644 --- a/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayCouponInfoServiceImpl.java +++ b/play-admin/src/main/java/com/starry/admin/modules/shop/service/impl/PlayCouponInfoServiceImpl.java @@ -10,6 +10,7 @@ import com.starry.admin.common.exception.CustomException; import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; import com.starry.admin.modules.shop.mapper.PlayCouponInfoMapper; import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity; +import com.starry.admin.modules.shop.module.enums.CouponClaimConditionType; import com.starry.admin.modules.shop.module.vo.PlayCouponInfoQueryVo; import com.starry.admin.modules.shop.module.vo.PlayCouponInfoReturnVo; import com.starry.admin.modules.shop.service.IPlayCouponInfoService; @@ -84,26 +85,32 @@ public class PlayCouponInfoServiceImpl extends ServiceImpl obtainedCoupons = couponDetailsService - .selectByCustomId(ThreadLocalRequestDetail.getCustomUserInfo().getId()); + .selectByCustomId(currentCustomId); List couponInfoEntities = couponInfoService.queryAll(); List returnVos = new ArrayList<>(couponInfoEntities.size()); for (PlayCouponInfoEntity couponInfoEntity : couponInfoEntities) { if ("0".equals(couponInfoEntity.getCouponOnLineState())) { continue; } + // 领取白名单:非白名单用户不可见 + if (CouponClaimConditionType.WHITELIST.code().equals(couponInfoEntity.getClaimConditionType())) { + List wl = couponInfoEntity.getCustomWhitelist(); + if (wl == null || !wl.contains(currentCustomId)) { + continue; + } + } WxCouponReceiveReturnVo vo = ConvertUtil.entityToVo(couponInfoEntity, WxCouponReceiveReturnVo.class); for (PlayCouponDetailsReturnVo obtainedCoupon : obtainedCoupons) { if (obtainedCoupon.getCouponId().equals(couponInfoEntity.getId())) { diff --git a/play-admin/src/main/resources/db/migration/V2__add_coupon_whitelist.sql b/play-admin/src/main/resources/db/migration/V2__add_coupon_whitelist.sql new file mode 100644 index 0000000..20b141c --- /dev/null +++ b/play-admin/src/main/resources/db/migration/V2__add_coupon_whitelist.sql @@ -0,0 +1,5 @@ +-- Add whitelist for coupon self-claim restriction (specific users only) +-- MySQL dialect +ALTER TABLE `play_coupon_info` + ADD COLUMN `custom_whitelist` TEXT NULL COMMENT '领取白名单用户ID(JSON)'; + diff --git a/play-admin/src/test/java/com/starry/admin/modules/shop/service/CouponWhitelistTest.java b/play-admin/src/test/java/com/starry/admin/modules/shop/service/CouponWhitelistTest.java new file mode 100644 index 0000000..b4656c3 --- /dev/null +++ b/play-admin/src/test/java/com/starry/admin/modules/shop/service/CouponWhitelistTest.java @@ -0,0 +1,100 @@ +package com.starry.admin.modules.shop.service; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import com.starry.admin.common.conf.ThreadLocalRequestDetail; +import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity; +import com.starry.admin.modules.shop.module.entity.PlayCouponInfoEntity; +import com.starry.admin.modules.shop.service.IPlayCommodityInfoService; +import com.starry.admin.modules.shop.service.IPlayCouponDetailsService; +import com.starry.admin.modules.shop.service.IPlayCouponInfoService; +import com.starry.admin.modules.shop.service.impl.PlayCouponInfoServiceImpl; +import com.starry.admin.modules.weichat.controller.WxCouponController; +import com.starry.admin.modules.weichat.entity.WxCouponReceiveReturnVo; +import com.starry.common.result.R; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class CouponWhitelistTest { + + @Test + @DisplayName("领取校验-白名单:白名单用户可领,非白名单被拒") + void testWhitelistCheckInService() { + PlayCouponInfoServiceImpl service = new PlayCouponInfoServiceImpl(); + + PlayCouponInfoEntity coupon = new PlayCouponInfoEntity(); + coupon.setClaimConditionType("2"); + coupon.setCustomWhitelist(Arrays.asList("userA", "userB")); + + PlayCustomUserInfoEntity userA = new PlayCustomUserInfoEntity(); + userA.setId("userA"); + + PlayCustomUserInfoEntity userX = new PlayCustomUserInfoEntity(); + userX.setId("userX"); + + String ok = service.getReasonForNotObtainingCoupons(coupon, userA); + String deny = service.getReasonForNotObtainingCoupons(coupon, userX); + + assertEquals("", ok, "白名单用户应允许领取"); + assertEquals("非指定用户", deny, "非白名单用户应被拒绝"); + } + + @Mock + private IPlayCouponDetailsService couponDetailsService; + + @Mock + private IPlayCouponInfoService couponInfoService; + + @Mock + private IPlayCommodityInfoService commodityInfoService; + + @InjectMocks + private WxCouponController wxCouponController; + + @AfterEach + void clearTL() { ThreadLocalRequestDetail.remove(); } + + @Test + @DisplayName("列表过滤-白名单:非白名单用户不可见") + void testWhitelistHiddenInQueryAll() { + // 当前用户 + PlayCustomUserInfoEntity current = new PlayCustomUserInfoEntity(); + current.setId("uid-1"); + ThreadLocalRequestDetail.setRequestDetail(current); + + // 构造两张券:一张白名单包含uid-1,一张不包含 + PlayCouponInfoEntity visible = new PlayCouponInfoEntity(); + visible.setId("c1"); + visible.setCouponOnLineState("1"); + visible.setClaimConditionType("2"); + visible.setCustomWhitelist(Collections.singletonList("uid-1")); + + PlayCouponInfoEntity hidden = new PlayCouponInfoEntity(); + hidden.setId("c2"); + hidden.setCouponOnLineState("1"); + hidden.setClaimConditionType("2"); + hidden.setCustomWhitelist(Collections.singletonList("other")); + + when(couponDetailsService.selectByCustomId("uid-1")).thenReturn(new ArrayList<>()); + when(couponInfoService.queryAll()).thenReturn(Arrays.asList(visible, hidden)); + + R resp = wxCouponController.queryAll(); + @SuppressWarnings("unchecked") + List list = (List) resp.getData(); + + assertNotNull(list); + assertEquals(1, list.size(), "非白名单券应被过滤不可见"); + assertEquals("c1", list.get(0).getId()); + } +}