feat:用户优惠券

This commit is contained in:
dzc committed 2026-01-28 20:07:21 +08:00
1 parent 53512b7f9c
commit 86e8f08242
25 files changed
+964 -33

No files matched your search

@@ -0,0 +1,34 @@
package com.bsd.pjcloud.wallet.api.constant.act;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 优惠券使用状态
*/
@Getter
@AllArgsConstructor
public enum ActCouponRecordStatusEnum {
UNUSED("1", "未使用"),
USED("2", "已使用"),
EXPIRED("3", "已过期"),
INVALID("4", "已作废"),
;
private final String code;
private final String desc;
public static ActCouponRecordStatusEnum valueOfByType(String code) {
return Arrays.stream(ActCouponRecordStatusEnum.values())
.filter(v -> ObjectUtil.equal(v.getCode(), code))
.findFirst().orElse(null);
}
public static ActCouponRecordStatusEnum descOf(String description) {
return Arrays.stream(ActCouponRecordStatusEnum.values())
.filter(v -> ObjectUtil.equal(v.getDesc(), description))
.findFirst().orElse(null);
}
}
@@ -0,0 +1,41 @@
package com.bsd.pjcloud.wallet.api.constant.act;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
import java.util.List;
/**
* 核销活动状体啊
*/
@Getter
@AllArgsConstructor
public enum ActTypeEnum {
CANCEL_ACT("1", "核销活动", 1),
COUPON_ACT("2", "优惠券活动", 2),
;
private final String code;
private final String desc;
private final int priority;
public static ActTypeEnum valueOfByType(String code) {
return Arrays.stream(ActTypeEnum.values())
.filter(v -> ObjectUtil.equal(v.getCode(), code))
.findFirst().get();
}
public static ActTypeEnum descOf(String description) {
return Arrays.stream(ActTypeEnum.values())
.filter(v -> ObjectUtil.equal(v.getDesc(), description))
.findFirst().get();
}
public static List<ActTypeEnum> sortList(){
return Arrays.asList(ActTypeEnum.values()).stream().sorted((o1, o2) -> o2.priority - o1.priority).toList();
}
}
@@ -0,0 +1,40 @@
package com.bsd.pjcloud.wallet.api.constant.act;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* 用户活动状态
*/
@Getter
@AllArgsConstructor
public enum ActUserReceiveEnum {
ACT_EXPIRED("1", "活动不在进行中"),
ACT_NOT_EXIST("2", "活动不存在"),
ACT_NOT_STARTED("3", "活动未开启"),
USER_LIMIT_REACHED("4", "用户参加活动已达上限"),
USER_CANNOT_PARTICIPATE("5", "用户不能参加此活动"),
OTHER_ERROR("6", "其他错误"),
USER_CAN_JOIN_ACT("99", "用户可以参加活动"),
;
private final String code;
private final String desc;
public static ActUserReceiveEnum valueOfByType(String code) {
return Arrays.stream(ActUserReceiveEnum.values())
.filter(v -> ObjectUtil.equal(v.getCode(), code))
.findFirst().orElse(null);
}
public static ActUserReceiveEnum descOf(String description) {
return Arrays.stream(ActUserReceiveEnum.values())
.filter(v -> ObjectUtil.equal(v.getDesc(), description))
.findFirst().orElse(null);
}
}
@@ -0,0 +1,20 @@
package com.bsd.pjcloud.wallet.api.dto.act;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class ActCouponReceiveDto {
/**
* 领取优惠券的用户id
*/
private Long receiveUserId;
/**
* 优惠券id
*/
@NotNull(message = "优惠券id不能为空")
private Long couponId;
}
@@ -27,6 +27,18 @@ public class ActCouponEntity extends Model<ActCouponEntity> {
@Schema(description = "优惠券ID")
private Long id;
/**
* 优惠券活动唯一code
*/
@Schema(description = "优惠券活动唯一code")
private String uniqueCode;
/**
* 绑定的通知id
*/
@Schema(description = "绑定的通知id")
private Long bindNotifyId;
/**
* 优惠券名称
*/
@@ -51,9 +51,9 @@ public class ActCouponRecordEntity extends Model<ActCouponRecordEntity> {
private String couponCode;
/**
* 使用状态 (0:未使用 1:已使用 2:已过期 3:已作废)
* 使用状态(1:未使用2:已使用3:已过期4:已作废)
*/
@Schema(description = "使用状态 (0:未使用 1:已使用 2:已过期 3:已作废)")
@Schema(description = "使用状态(1:未使用2:已使用3:已过期4:已作废)")
private String status;
/**
@@ -0,0 +1,14 @@
package com.bsd.pjcloud.wallet.api.vo.act;
import com.bsd.pjcloud.wallet.api.constant.act.ActTypeEnum;
import lombok.Data;
@Data
public class ActSimpleNotfiyVo<T> {
private ActTypeEnum actType;
private Long actId;
private T detail;
}
@@ -0,0 +1,53 @@
package com.bsd.pjcloud.wallet.controller.act;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.wallet.api.constant.act.ActTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo;
import com.bsd.pjcloud.wallet.api.vo.act.ActSimpleNotfiyVo;
import com.bsd.pjcloud.wallet.service.ActComService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* 活动公用方法
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/act/com")
@Tag(description = "act-com", name = "活动公用方法")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ActComController {
private final ActComService actComService;
@Operation(summary = "根据公告id查询对应的活动", description = "根据公告id查询对应的活动")
@GetMapping("/app/get-act-by-notify-id")
public R<ActSimpleNotfiyVo> getActByNotifyId(@RequestParam Long notifyId) {
return R.ok(actComService.getActByNotifyId(notifyId));
}
@Operation(summary = "获取活动详情", description = "获取活动详情")
@GetMapping("/app/get-act-detail")
public R<ActSimpleNotfiyVo> getActDetail(@RequestParam ActTypeEnum actType, @RequestParam Long actId) {
return R.ok(actComService.getActDetail(actType, actId));
}
@Operation(summary = "判断用户是否可以参加活动", description = "判断用户是否可以参加活动")
@GetMapping("/app/is-eligible-for-act")
public R<String> isEligibleForAct(@RequestParam ActTypeEnum actType, @RequestParam Long actId) {
PjcloudUser user = SecurityUtils.getUser();
return R.ok(actComService.isEligibleForAct(actType, actId, user.getId()));
}
}
@@ -6,9 +6,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo;
import com.bsd.pjcloud.wallet.service.ActCouponService;
import org.springframework.security.access.prepost.PreAuthorize;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
@@ -41,9 +43,8 @@ public class ActCouponController {
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_view')")
public R getActCouponPage(@ParameterObject Page page, @ParameterObject ActCouponDto dto) {
LambdaQueryWrapper<ActCouponEntity> wrapper = Wrappers.lambdaQuery();
return R.ok(actCouponService.page(page, wrapper));
public R<Page<ActCouponVo>> getActCouponPage(@ParameterObject Page page, @ParameterObject ActCouponDto dto) {
return R.ok(actCouponService.pageJoin(page, dto));
}
/**
@@ -63,8 +64,8 @@ public class ActCouponController {
@SysLog("新增优惠券活动")
@PostMapping
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_add')")
public R save(@RequestBody ActCouponEntity actCoupon) {
return R.ok(actCouponService.save(actCoupon));
public R<Long> save(@RequestBody ActCouponEntity actCoupon) {
return R.ok(actCouponService.create(actCoupon));
}
/**
@@ -75,7 +76,8 @@ public class ActCouponController {
@PutMapping
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_edit')")
public R updateById(@RequestBody ActCouponEntity actCoupon) {
return R.ok(actCouponService.updateById(actCoupon));
actCouponService.modify(actCoupon);
return R.ok();
}
/**
@@ -86,7 +88,8 @@ public class ActCouponController {
@DeleteMapping
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_del')")
public R removeById(@RequestBody Long[] ids) {
return R.ok(actCouponService.removeBatchByIds(CollUtil.toList(ids)));
actCouponService.delByIds(CollUtil.toList(ids));
return R.ok();
}
/**
@@ -6,10 +6,15 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponReceiveDto;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponRecordDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
import com.bsd.pjcloud.wallet.service.ActCouponRecordService;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import com.bsd.pjcloud.common.excel.annotation.ResponseExcel;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
@@ -27,19 +32,33 @@ import java.util.List;
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/actCouponRecord" )
@Tag(description = "actCouponRecord" , name = "优惠券领取记录表管理" )
@RequestMapping("/act-coupon-record")
@Tag(description = "act-coupon-record", name = "优惠券领取记录表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ActCouponRecordController {
private final ActCouponRecordService actCouponRecordService;
private final ActCouponRecordService actCouponRecordService;
/* ************************************* start app start ************************************* */
@Operation(summary = "领取优惠券", description = "领取优惠券")
@PostMapping("/app/receive")
@Idempotent(expireTime = 2, key = "'/act/receive/app/receive/' + #dto.getReceiveUserId()", info = "正在领取中,请勿重复操作")
public R<Long> appReceive(@Valid @RequestBody ActCouponReceiveDto dto) {
PjcloudUser user = SecurityUtils.getUser();
dto.setReceiveUserId(user.getId());
return R.ok(actCouponRecordService.appReceive(dto));
}
/* ************************************* end app end ************************************* */
/**
* 分页查询
*/
@Operation(summary = "分页查询" , description = "分页查询" )
@GetMapping("/page" )
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_view')" )
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_view')")
public R getActCouponRecordPage(@ParameterObject Page page, @ParameterObject ActCouponRecordDto dto) {
LambdaQueryWrapper<ActCouponRecordEntity> wrapper = Wrappers.lambdaQuery();
return R.ok(actCouponRecordService.page(page, wrapper));
@@ -49,20 +68,20 @@ public class ActCouponRecordController {
/**
* 通过id查询优惠券领取记录表
*/
@Operation(summary = "通过id查询" , description = "通过id查询" )
@GetMapping("/{id}" )
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_view')" )
public R getById(@PathVariable("id" ) Long id) {
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_view')")
public R getById(@PathVariable("id") Long id) {
return R.ok(actCouponRecordService.getById(id));
}
/**
* 新增优惠券领取记录表
*/
@Operation(summary = "新增优惠券领取记录表" , description = "新增优惠券领取记录表" )
@SysLog("新增优惠券领取记录表" )
@Operation(summary = "新增优惠券领取记录表", description = "新增优惠券领取记录表")
@SysLog("新增优惠券领取记录表")
@PostMapping
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_add')" )
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_add')")
public R save(@RequestBody ActCouponRecordEntity actCouponRecord) {
return R.ok(actCouponRecordService.save(actCouponRecord));
}
@@ -70,10 +89,10 @@ public class ActCouponRecordController {
/**
* 修改优惠券领取记录表
*/
@Operation(summary = "修改优惠券领取记录表" , description = "修改优惠券领取记录表" )
@SysLog("修改优惠券领取记录表" )
@Operation(summary = "修改优惠券领取记录表", description = "修改优惠券领取记录表")
@SysLog("修改优惠券领取记录表")
@PutMapping
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_edit')" )
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_edit')")
public R updateById(@RequestBody ActCouponRecordEntity actCouponRecord) {
return R.ok(actCouponRecordService.updateById(actCouponRecord));
}
@@ -81,10 +100,10 @@ public class ActCouponRecordController {
/**
* 通过id删除优惠券领取记录表
*/
@Operation(summary = "通过id删除优惠券领取记录表" , description = "通过id删除优惠券领取记录表" )
@SysLog("通过id删除优惠券领取记录表" )
@Operation(summary = "通过id删除优惠券领取记录表", description = "通过id删除优惠券领取记录表")
@SysLog("通过id删除优惠券领取记录表")
@DeleteMapping
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_del')" )
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_del')")
public R removeById(@RequestBody Long[] ids) {
return R.ok(actCouponRecordService.removeBatchByIds(CollUtil.toList(ids)));
}
@@ -94,8 +113,8 @@ public class ActCouponRecordController {
*/
@ResponseExcel
@GetMapping("/export")
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_export')" )
public List<ActCouponRecordEntity> export(ActCouponRecordEntity actCouponRecord,Long[] ids) {
@PreAuthorize("@pms.hasPermission('wallet_actCouponRecord_export')")
public List<ActCouponRecordEntity> export(ActCouponRecordEntity actCouponRecord, Long[] ids) {
return actCouponRecordService.list(Wrappers.lambdaQuery(actCouponRecord).in(ArrayUtil.isNotEmpty(ids), ActCouponRecordEntity::getId, ids));
}
}
@@ -1,10 +1,16 @@
package com.bsd.pjcloud.wallet.mapper.act;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@Mapper
public interface ActCouponMapper extends PjcloudBaseMapper<ActCouponEntity> {
Page<ActCouponVo> pageJoin(Page page, @Param("dto") ActCouponDto dto);
}
@@ -14,4 +14,6 @@ public interface MchDeptViewMapper {
List<WalletMchDeptViewVo> selectList(@Param("dto") WalletMchDeptViewDto dto);
WalletMchDeptViewVo selectByUserId(@Param("userId") Long userId);
}
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.wallet.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelItemEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCancelItemVo;
@@ -22,6 +23,11 @@ public interface ActCancelItemService extends IService<ActCancelItemEntity> {
*/
ActCancelItemVo appFindUnique(Long actId, PjcloudUser user);
/**
* 判断用户是否可以参加活动
*/
ActUserReceiveEnum isEligibleForAct(Long actId, Long userId);
/**
* app查询本店优惠券列表
*/
@@ -0,0 +1,23 @@
package com.bsd.pjcloud.wallet.service;
import com.bsd.pjcloud.wallet.api.constant.act.ActTypeEnum;
import com.bsd.pjcloud.wallet.api.vo.act.ActSimpleNotfiyVo;
public interface ActComService {
/**
* 根据公告id查询对应的活动
*/
ActSimpleNotfiyVo getActByNotifyId(Long notifyId);
/**
* 获取活动详情
*/
ActSimpleNotfiyVo getActDetail(ActTypeEnum actType, Long actId);
/**
* 判断用户是否可以参加活动
*/
String isEligibleForAct(ActTypeEnum actType, Long actId, Long userId);
}
@@ -0,0 +1,35 @@
package com.bsd.pjcloud.wallet.service;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
public interface ActCouponLifecycleService {
/**
* 领取优惠券前的校验
* @param couponId 优惠券ID
* @param userId 用户ID
* @return 优惠券实体
*/
ActCouponEntity preReceive(Long couponId, Long userId);
/**
* 领取优惠券后的处理
* @param record 优惠券记录实体
*/
void afterReceive(ActCouponRecordEntity record);
/**
* 核销前的校验
* @param record 优惠券记录实体
* @return 优惠券实体
*/
ActCouponRecordEntity preCancel(ActCouponRecordEntity record);
/**
* 核销后的处理
* @param record 优惠券记录实体
*/
void afterCancel(ActCouponRecordEntity record);
}
@@ -1,8 +1,19 @@
package com.bsd.pjcloud.wallet.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponReceiveDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
public interface ActCouponRecordService extends IService<ActCouponRecordEntity> {
/**
* 领取优惠券
*/
Long appReceive(ActCouponReceiveDto dto);
/**
* 判断用户是否可以参加活动
*/
ActUserReceiveEnum isEligibleForAct(Long actId, Long userId);
}
@@ -1,8 +1,38 @@
package com.bsd.pjcloud.wallet.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo;
import java.util.ArrayList;
public interface ActCouponService extends IService<ActCouponEntity> {
/**
* 分页查询
*/
Page<ActCouponVo> pageJoin(Page page, ActCouponDto dto);
/**
* 新增优惠券
*/
Long create(ActCouponEntity actCoupon);
/**
* 修改优惠券
*/
void modify(ActCouponEntity actCoupon);
/**
* 删除优惠券
*/
void delByIds(ArrayList<Long> list);
/**
* 根据公告id查询核销活动
*/
ActCouponEntity getDetailByNoticeId(Long notifyId);
}
@@ -16,6 +16,7 @@ import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActCancelStatusEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActStatusEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelEntity;
@@ -152,10 +153,10 @@ public class ActCancelItemServiceImpl extends ServiceImpl<ActCancelItemMapper, A
// 判断是否在时间内
LocalDateTime now = LocalDateTime.now();
if (act.getExpireTime().isBefore(LocalDateTime.now())) {
throw new RuntimeException("核销失败,活动已结束");
throw new RuntimeException("领取失败,活动已结束");
}
if (act.getStartTime().isAfter(LocalDateTime.now())) {
throw new RuntimeException("核销失败,活动未开始");
throw new RuntimeException("领取失败,活动未开始");
}
// 获取商户
@@ -210,6 +211,31 @@ public class ActCancelItemServiceImpl extends ServiceImpl<ActCancelItemMapper, A
return result;
}
/**
* 判断用户是否可以参加活动
*/
@Override
public ActUserReceiveEnum isEligibleForAct(Long actId, Long userId) {
// 查询活动
ActCancelEntity act = actCancelMapper.selectById(actId);
if (ObjectUtil.isEmpty(act)) {
return ActUserReceiveEnum.ACT_NOT_EXIST;
}
ActCancelItemEntity detail = baseMapper.selectOne(new LambdaQueryWrapper<ActCancelItemEntity>()
.eq(ActCancelItemEntity::getActId, actId)
.eq(ActCancelItemEntity::getReceiverUserId, userId)
);
if (ObjectUtil.isEmpty(detail)) {
return ActUserReceiveEnum.USER_LIMIT_REACHED;
}
if (ObjectUtil.isNotEmpty(detail.getExpireTime())) {
if (detail.getExpireTime().isBefore(LocalDateTime.now())) {
return ActUserReceiveEnum.ACT_EXPIRED;
}
}
return ActUserReceiveEnum.USER_CAN_JOIN_ACT;
}
/**
* app查询本店优惠券列表
*/
@@ -0,0 +1,101 @@
package com.bsd.pjcloud.wallet.service.impl.act;
import cn.hutool.core.util.ObjectUtil;
import com.bsd.pjcloud.wallet.api.constant.act.ActTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.entity.act.ActCancelEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActSimpleNotfiyVo;
import com.bsd.pjcloud.wallet.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 活动公用方法
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class ActComServiceImpl implements ActComService {
private final ActCancelService actCancelService;
private final ActCancelItemService actCancelItemService;
private final ActCouponService actCouponService;
private final ActCouponRecordService actCouponRecordService;
/**
* 根据公告id查询对应的活动
*/
@Override
public ActSimpleNotfiyVo getActByNotifyId(Long notifyId) {
ActSimpleNotfiyVo result = new ActSimpleNotfiyVo<>();
List<ActTypeEnum> actEnumList = ActTypeEnum.sortList();
for (ActTypeEnum act : actEnumList) {
if (ObjectUtil.equals(act, ActTypeEnum.CANCEL_ACT)) {
ActCancelEntity detail = actCancelService.getDetailByNoticeId(notifyId);
if (ObjectUtil.isNotEmpty(detail)) {
result.setActId(detail.getId());
result.setActType(act);
result.setDetail(detail);
return result;
}
}
if (ObjectUtil.equals(act, ActTypeEnum.COUPON_ACT)) {
ActCouponEntity detail = actCouponService.getDetailByNoticeId(notifyId);
if (ObjectUtil.isNotEmpty(detail)) {
result.setActId(detail.getId());
result.setActType(act);
result.setDetail(detail);
return result;
}
}
}
return null;
}
/**
* 获取活动详情
*/
@Override
public ActSimpleNotfiyVo getActDetail(ActTypeEnum actType, Long actId) {
ActSimpleNotfiyVo result = new ActSimpleNotfiyVo<>();
result.setActId(actId);
if (ObjectUtil.equals(actType, ActTypeEnum.CANCEL_ACT)) {
ActCancelEntity detail = actCancelService.getById(actId);
if (ObjectUtil.isNotEmpty(detail)) {
result.setActType(actType);
result.setDetail(detail);
return result;
}
}
if (ObjectUtil.equals(actType, ActTypeEnum.COUPON_ACT)) {
ActCouponEntity detail = actCouponService.getById(actId);
if (ObjectUtil.isNotEmpty(detail)) {
result.setActType(actType);
result.setDetail(detail);
return result;
}
}
return null;
}
/**
* 判断用户是否可以参加活动
*/
@Override
public String isEligibleForAct(ActTypeEnum actType, Long actId, Long userId) {
ActUserReceiveEnum status = ActUserReceiveEnum.USER_CANNOT_PARTICIPATE;
// 核销活动目前只能参加一次
if (ObjectUtil.equals(actType, ActTypeEnum.CANCEL_ACT)) {
status = actCancelItemService.isEligibleForAct(actId, userId);
}
if (ObjectUtil.equals(actType, ActTypeEnum.COUPON_ACT)) {
status = actCouponRecordService.isEligibleForAct(actId, userId);
}
return status.getCode();
}
}
@@ -1,15 +1,102 @@
package com.bsd.pjcloud.wallet.service.impl.act;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.common.core.constant.enums.MerchantTypeEnum;
import com.bsd.pjcloud.common.core.exception.ServiceException;
import com.bsd.pjcloud.wallet.api.constant.act.ActUserReceiveEnum;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponReceiveDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
import com.bsd.pjcloud.wallet.api.vo.MchManageVo;
import com.bsd.pjcloud.wallet.mapper.act.ActCouponMapper;
import com.bsd.pjcloud.wallet.mapper.act.ActCouponRecordMapper;
import com.bsd.pjcloud.wallet.service.ActCouponRecordService;
import com.bsd.pjcloud.wallet.service.MchManageService;
import com.bsd.pjcloud.wallet.utils.act.service.ActCouponComUtilService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 优惠券领取记录表
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMapper, ActCouponRecordEntity> implements ActCouponRecordService {
private final ActCouponMapper actCouponMapper;
private final MchManageService mchManageService;
private final ActCouponComUtilService actCouponComUtilService;
private final RemoteUserService remoteUserService;
/**
* 领取优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Long appReceive(ActCouponReceiveDto dto) {
// 1. 获取当前用户
UserInfo userInfo = remoteUserService.getByUserId(dto.getReceiveUserId()).getData();
if (ObjectUtil.isEmpty(userInfo)) {
throw new ServiceException("用户不存在");
}
SysUser sysUser = userInfo.getSysUser();
// 2. 预处理
ActCouponEntity actCouponEntity = actCouponComUtilService.preReceive(dto.getCouponId(), dto.getReceiveUserId());
if (ObjectUtil.isEmpty(actCouponEntity)) {
return null;
}
// 3. 特殊优惠券判断
ActUserReceiveEnum specialCouponStatus = specialCoupon(actCouponEntity, sysUser);
if (ObjectUtil.isNotEmpty(specialCouponStatus) && ObjectUtil.notEqual(specialCouponStatus, ActUserReceiveEnum.USER_CAN_JOIN_ACT)) {
throw new ServiceException(specialCouponStatus.getDesc());
}
// 4. 执行领取逻辑
ActCouponRecordEntity record = actCouponComUtilService.doReceive(actCouponEntity, sysUser);
// 5. 优惠券领取成功后,执行生命周期方法
actCouponComUtilService.afterReceive(record);
return record.getId();
}
public ActUserReceiveEnum specialCoupon(ActCouponEntity actCouponEntity, SysUser sysUser) {
// 食堂生日券
if (ObjectUtil.equals(actCouponEntity.getUniqueCode(), "1qRgFo")) {
// 判断是否为食堂用户
MchManageVo userMch = mchManageService.getFirstMchByDeptId(sysUser.getDeptId(), List.of(MerchantTypeEnum.CANTEEN));
if (ObjectUtil.isEmpty(userMch)) {
return ActUserReceiveEnum.USER_CANNOT_PARTICIPATE;
}
// 判断用户最近一年充值是否为500,1500,800,2400
return ActUserReceiveEnum.USER_CAN_JOIN_ACT;
}
return null;
}
/**
* 判断用户是否可以参加活动
*/
@Override
public ActUserReceiveEnum isEligibleForAct(Long actId, Long userId) {
ActUserReceiveEnum userReceiveEnum = actCouponComUtilService.isEligibleForAct(actId, userId);
if (ObjectUtil.equals(userReceiveEnum, ActUserReceiveEnum.USER_CAN_JOIN_ACT)) {
UserInfo userInfo = remoteUserService.getByUserId(userId).getData();
ActCouponEntity actCouponEntity = actCouponMapper.selectById(actId);
if (ObjectUtil.isNotEmpty(userInfo) && ObjectUtil.isNotEmpty(actCouponEntity)) {
userReceiveEnum = specialCoupon(actCouponEntity, userInfo.getSysUser());
}
}
return userReceiveEnum;
}
}
@@ -1,15 +1,91 @@
package com.bsd.pjcloud.wallet.service.impl.act;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponDto;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo;
import com.bsd.pjcloud.wallet.mapper.act.ActCouponMapper;
import com.bsd.pjcloud.wallet.service.ActCouponService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 优惠券活动
*/
@Service
public class ActCouponServiceImpl extends ServiceImpl<ActCouponMapper, ActCouponEntity> implements ActCouponService {
@Override
public Page<ActCouponVo> pageJoin(Page page, ActCouponDto dto) {
return baseMapper.pageJoin(page, dto);
}
/**
* 新增优惠券
*/
@Override
public Long create(ActCouponEntity actCoupon) {
actCoupon.setUniqueCode(generateUniqueCode());
baseMapper.insert(actCoupon);
return actCoupon.getId();
}
/**
* 修改优惠券
*/
@Override
public void modify(ActCouponEntity actCoupon) {
// 是否存在
ActCouponEntity exist = baseMapper.selectById(actCoupon.getId());
if (ObjectUtil.isEmpty(exist)) {
throw new RuntimeException("优惠券不存在");
}
if (ObjectUtil.isEmpty(exist.getUniqueCode())) {
actCoupon.setUniqueCode(generateUniqueCode());
}
baseMapper.updateById(actCoupon);
}
/**
* 删除优惠券
*/
@Override
public void delByIds(ArrayList<Long> list) {
List<ActCouponEntity> couponList = baseMapper.selectByIds(list);
if (ObjectUtil.isEmpty(couponList)) return;
for (ActCouponEntity coupon : couponList) {
// 如果有人领取了优惠券,则不能删除
}
baseMapper.deleteByIds(list);
}
/**
* 根据公告id查询核销活动
*/
@Override
public ActCouponEntity getDetailByNoticeId(Long notifyId) {
return baseMapper.selectOne(new LambdaQueryWrapper<ActCouponEntity>()
.eq(ActCouponEntity::getBindNotifyId, notifyId)
);
}
/**
* 生成唯一code
*/
private String generateUniqueCode() {
String string = RandomUtil.randomString(6);
while (true) {
Long isExist = baseMapper.selectCount(new LambdaQueryWrapper<ActCouponEntity>().eq(ActCouponEntity::getUniqueCode, string));
if (isExist == null || isExist == 0) {
return string;
}
}
}
}
@@ -0,0 +1,27 @@
package com.bsd.pjcloud.wallet.utils.act.impl;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
/**
* 优惠券生命周期
*/
public interface ActCouponLifecycleService {
/**
* 领取优惠券前的校验
*
* @param couponId 优惠券ID
* @param userId 用户ID
* @return 优惠券实体
*/
ActCouponEntity preReceive(Long couponId, Long userId);
/**
* 领取优惠券后的处理
*
* @param record 优惠券记录实体
*/
void afterReceive(ActCouponRecordEntity record);
}
@@ -0,0 +1,252 @@
package com.bsd.pjcloud.wallet.utils.act.service;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.wallet.api.constant.act.*;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponEntity;
import com.bsd.pjcloud.wallet.api.entity.act.ActCouponRecordEntity;
import com.bsd.pjcloud.wallet.mapper.act.ActCouponMapper;
import com.bsd.pjcloud.wallet.mapper.act.ActCouponRecordMapper;
import com.bsd.pjcloud.wallet.service.ActCouponLifecycleService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
/**
* @author liusiyang_dxm
* @since 2026/1/28
*/
@Service
@AllArgsConstructor
public class ActCouponComUtilService implements ActCouponLifecycleService {
private final ActCouponMapper actCouponMapper;
private final ActCouponRecordMapper actCouponRecordMapper;
/**
* 领取优惠券前的校验
*/
@Override
public ActCouponEntity preReceive(Long couponId, Long userId) {
ActCouponEntity coupon = actCouponMapper.selectById(couponId);
if (ObjectUtil.isEmpty(coupon)) {
throw new RuntimeException("此优惠券活动不存在");
}
assertStatus(coupon);
assertReceiveWindow(coupon);
assertLimit(coupon, userId);
return coupon;
}
/**
* 领取优惠券
*/
public ActCouponRecordEntity doReceive(ActCouponEntity coupon, SysUser sysUser) {
ActCouponRecordEntity record = new ActCouponRecordEntity();
record.setCouponId(coupon.getId());
record.setUserId(sysUser.getUserId());
record.setUserName(sysUser.getName());
record.setCouponCode(UUID.fastUUID().toString());
record.setStatus(ActCouponRecordStatusEnum.UNUSED.getCode());
// 优惠券冗余字段(不更新)
record.setCouponName(coupon.getName());
record.setCouponType(coupon.getType());
record.setFaceValue(coupon.getFaceValue());
record.setMinPoint(coupon.getMinPoint());
// 这个时间要根据`优惠券过期类型来设置`
// 1-固定日期
if (ObjectUtil.equals(ActCouponValidTypeEnum.FIXED_DATE.getCode(), coupon.getValidType())) {
record.setStartTime(coupon.getValidStartTime());
record.setEndTime(coupon.getValidEndTime());
}
// 2-领取后生效
if (ObjectUtil.equals(ActCouponValidTypeEnum.AFTER_RECEIVE.getCode(), coupon.getValidType())) {
Integer validDays = coupon.getValidDays();
record.setStartTime(LocalDateTime.now());
record.setEndTime(record.getStartTime().plusDays(validDays));
}
actCouponRecordMapper.insert(record);
return record;
}
/**
* 领取优惠券后的处理
*/
@Override
public void afterReceive(ActCouponRecordEntity record) {
}
/**
* 核销前的校验
*/
@Override
public ActCouponRecordEntity preCancel(ActCouponRecordEntity record) {
ActCouponRecordEntity couponRecord = actCouponRecordMapper.selectById(record.getCouponId());
if (ObjectUtil.isEmpty(couponRecord)) {
throw new RuntimeException("优惠券不存在");
}
// 基本校验:状态是否可用
if (ObjectUtil.notEqual(ActCouponRecordStatusEnum.UNUSED.getCode(), couponRecord.getStatus())) {
throw new RuntimeException("优惠券不可用");
}
// 校验有效期
LocalDateTime now = LocalDateTime.now();
if (ObjectUtil.isNotEmpty(couponRecord.getStartTime()) && now.isBefore(couponRecord.getStartTime())) {
throw new RuntimeException("优惠券尚未生效");
}
if (ObjectUtil.isNotEmpty(couponRecord.getEndTime()) && now.isAfter(couponRecord.getEndTime())) {
throw new RuntimeException("优惠券已过期");
}
return couponRecord;
}
public void doCancel(Long couponRecordId, Long orderId, int carTotalPrice) {
ActCouponRecordEntity couponRecord = actCouponRecordMapper.selectById(couponRecordId);
if (ObjectUtil.isEmpty(couponRecord)) {
throw new RuntimeException("此优惠券不存在");
}
ActCouponRecordEntity updateObj = new ActCouponRecordEntity();
updateObj.setId(couponRecordId);
updateObj.setStatus(ActCouponRecordStatusEnum.USED.getCode());
updateObj.setUseTime(LocalDateTime.now());
updateObj.setOrderId(String.valueOf(orderId));
updateObj.setUseAmount(carTotalPrice);
actCouponRecordMapper.updateById(updateObj);
}
/**
* 核销后的处理
*/
@Override
public void afterCancel(ActCouponRecordEntity record) {
}
/**
* 查询用户是否可以参加活动
*/
public ActUserReceiveEnum isEligibleForAct(Long couponId, Long userId) {
ActCouponEntity coupon = actCouponMapper.selectById(couponId);
if (ObjectUtil.isEmpty(coupon)) {
return ActUserReceiveEnum.ACT_NOT_EXIST;
}
try {
assertStatus(coupon);
} catch (Exception e) {
return ActUserReceiveEnum.ACT_NOT_STARTED;
}
try {
assertReceiveWindow(coupon);
} catch (Exception e) {
return ActUserReceiveEnum.ACT_EXPIRED;
}
try {
assertLimit(coupon, userId);
} catch (Exception e) {
return ActUserReceiveEnum.USER_LIMIT_REACHED;
}
return ActUserReceiveEnum.USER_CAN_JOIN_ACT;
}
/**
* 校验优惠券活动状态为进行中
*/
private void assertStatus(ActCouponEntity coupon) {
if (!ObjectUtil.equals(ActCouponStatusEnum.JINXINGZHONG.getCode(), coupon.getStatus())) {
throw new RuntimeException("此优惠券不在进行中");
}
}
/**
* 校验领取时间窗(固定日期型需在有效期内)
*/
private void assertReceiveWindow(ActCouponEntity coupon) {
if (ObjectUtil.equals(ActCouponValidTypeEnum.FIXED_DATE.getCode(), coupon.getValidType())) {
LocalDateTime now = LocalDateTime.now();
if (ObjectUtil.isNotEmpty(coupon.getValidStartTime()) && now.isBefore(coupon.getValidStartTime())) {
throw new RuntimeException("此优惠券活动尚未开始");
}
if (ObjectUtil.isNotEmpty(coupon.getValidEndTime()) && now.isAfter(coupon.getValidEndTime())) {
throw new RuntimeException("此优惠券活动已结束");
}
}
}
/**
* 校验用户在限领规则下是否超过可领取次数
*/
private void assertLimit(ActCouponEntity coupon, Long userId) {
if (ObjectUtil.isEmpty(coupon.getLimitCount()) || coupon.getLimitCount() <= 0) {
return;
}
LocalDateTime start = buildLimitStartTime(coupon.getLimitType());
LambdaQueryWrapper<ActCouponRecordEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ActCouponRecordEntity::getCouponId, coupon.getId()).eq(ActCouponRecordEntity::getUserId, userId);
if (ObjectUtil.isNotEmpty(start)) {
wrapper.ge(ActCouponRecordEntity::getCreateTime, start);
}
Long cnt = actCouponRecordMapper.selectCount(wrapper);
if (cnt != null && cnt >= coupon.getLimitCount()) {
throw new RuntimeException(buildLimitExceededMessage(coupon.getLimitType()));
}
}
/**
* 构造限领类型的统计起始时间(一次型不限制返回 null)
*/
private LocalDateTime buildLimitStartTime(String limitType) {
if (ObjectUtil.equals(ActCouponLimitTypeEnum.ONCE.getCode(), limitType)) {
return null;
}
LocalDate today = LocalDate.now();
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_DAY.getCode(), limitType)) {
return LocalDateTime.of(today, LocalTime.MIN);
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_WEEK.getCode(), limitType)) {
LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
return LocalDateTime.of(monday, LocalTime.MIN);
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_MONTH.getCode(), limitType)) {
LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfMonth());
return LocalDateTime.of(firstDay, LocalTime.MIN);
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_YEAR.getCode(), limitType)) {
LocalDate firstDay = today.with(TemporalAdjusters.firstDayOfYear());
return LocalDateTime.of(firstDay, LocalTime.MIN);
}
return null;
}
private String buildLimitExceededMessage(String limitType) {
if (ObjectUtil.equals(ActCouponLimitTypeEnum.ONCE.getCode(), limitType)) {
return "活动期间已达领取上限";
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_DAY.getCode(), limitType)) {
return "今日已达领取上限";
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_WEEK.getCode(), limitType)) {
return "本周已达领取上限";
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_MONTH.getCode(), limitType)) {
return "本月已达领取上限";
}
if (ObjectUtil.equals(ActCouponLimitTypeEnum.EVERY_YEAR.getCode(), limitType)) {
return "本年已达领取上限";
}
return "已达到领取上限";
}
}
@@ -2,9 +2,10 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.wallet.mapper.act.ActCouponMapper">
<sql id="selectA">
select a.id,
a.unique_code,
a.bind_notify_id,
a.name,
a.type,
a.face_value,
@@ -37,6 +38,16 @@
<if test="dto.type != null and dto.type != '' ">
and a.type = #{dto.type}
</if>
<if test="dto.status != null and dto.status != '' ">
and a.status = #{dto.status}
</if>
</sql>
<select id="pageJoin" resultType="com.bsd.pjcloud.wallet.api.vo.act.ActCouponVo">
<include refid="selectA"/>
from act_coupon a
<where>
<include refid="whereA"/>
</where>
</select>
</mapper>
@@ -52,4 +52,6 @@
order by a.mchDeptId, a.level
</select>
<select id="selectByUserId" resultType="com.bsd.pjcloud.wallet.api.vo.mch.WalletMchDeptViewVo">
</select>
</mapper>