feat:支付选择优惠券

This commit is contained in:
dzc committed 2026-05-26 18:25:16 +08:00
1 parent 67320a4787
commit b83657b1ea
15 files changed
+173 -32

No files matched your search

@@ -219,6 +219,18 @@ public class HardOrderEntity extends Model<HardOrderEntity> {
@Schema(description = "离线自动支付是否成功")
private String offlinePaySuccess;
/**
* 优惠券id
*/
@Schema(description = "优惠券id")
private String couponRecordId;
/**
* 优惠金额
*/
@Schema(description = "优惠金额")
private Integer couponPrice;
/**
* 创建人
*/
@@ -45,6 +45,7 @@ import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleMapper;
import com.bsd.pjcloud.hard.mapper.order.HardOrderMapper;
import com.bsd.pjcloud.hard.service.*;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundDto;
import com.bsd.pjcloud.wallet.api.feign.RemoteActCouponCancelService;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletRechargeService;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletSettingService;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeVo;
@@ -86,6 +87,7 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
private final RemoteDeptService deptService;
private final RemoteWalletRechargeService walletRechargeService;
private final RemoteWalletSettingService walletSettingService;
private final RemoteActCouponCancelService actCouponCancelService;
@Override
@@ -641,6 +643,8 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
}
// 执行退款
getSelf().refund(saveObj.getId());
// 退优惠券
actCouponCancelService.remoteReturn(Long.valueOf(order.getCouponRecordId()));
return true;
}
@@ -86,6 +86,7 @@ import com.bsd.pjcloud.wallet.api.constant.mch.WalletMchSupType;
import com.bsd.pjcloud.wallet.api.dto.WalletBalanceTransactionDto;
import com.bsd.pjcloud.wallet.api.dto.act.ActCancelItemDto;
import com.bsd.pjcloud.wallet.api.dto.act.ActComCancelDto;
import com.bsd.pjcloud.wallet.api.dto.act.ActCouponRecordRemoteCancelDto;
import com.bsd.pjcloud.wallet.api.entity.WalletCodeUseEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
@@ -754,13 +755,20 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
if (ObjectUtil.isEmpty(wallet)) throw new RuntimeException("用户钱包不存在");
Integer balance = wallet.getBalance();
int totalPrice = dto.getCustomPrice();
int payPrice = dto.getCustomPrice();
// 用户是否使用优惠券
int useAmountPrice = 0;
if (ObjectUtil.isNotEmpty(dto.getActCouponRecordId())) {
ActCouponRecordVo actCouponRecord = remoteActCouponCancelService.remoteGetDetail(dto.getActCouponRecordId(), totalPrice).getData();
if (ObjectUtil.isEmpty(actCouponRecord)) {
throw new RuntimeException("优惠券不存在");
}
if (ObjectUtil.isNotEmpty(actCouponRecord.getUseAmountPrice()) && actCouponRecord.getUseAmountPrice() > 0) {
payPrice -= actCouponRecord.getUseAmountPrice();
if (payPrice < 0) payPrice = 0;
useAmountPrice = actCouponRecord.getUseAmountPrice();
}
}
String subject = "未知商品x?";
@@ -782,10 +790,14 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
.orderSourceTypeId(dto.getOrderSourceTypeId())
.totalPrice(totalPrice)
.extraPrice(0)
.payPrice(totalPrice)
.payPrice(payPrice)
.isOfflineOrder(YesNoEnum.NO.getCode())
.productDesc(subject)
.build();
if (ObjectUtil.isNotEmpty(dto.getActCouponRecordId())) {
orderObj.setCouponRecordId(String.valueOf(dto.getActCouponRecordId()));
orderObj.setCouponPrice(useAmountPrice);
}
orderObj.setWalletId(wallet.getId());
baseMapper.insert(orderObj);
// 钱包扣款
@@ -794,7 +806,11 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
getSelf().generateCode(orderObj);
// 核销优惠券
if (ObjectUtil.isNotEmpty(dto.getActCouponRecordId())) {
ActCouponRecordRemoteCancelDto remoteCancelDto = new ActCouponRecordRemoteCancelDto();
remoteCancelDto.setOrderId(orderObj.getId());
remoteCancelDto.setActCouponRecordId(dto.getActCouponRecordId());
remoteCancelDto.setUseAmountPrice(useAmountPrice);
remoteActCouponCancelService.remoteCancel(remoteCancelDto);
}
// 云音箱播报
CompletableFuture.runAsync(() -> yyxService.send(orderObj.getMerchantId(), orderObj.getPayPrice(), userDetail.getSysUser().getPhone()));
@@ -1048,6 +1064,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
boolean isVerify = isVerify(hardPayType);
if (isWalletPay) {
int payPrice = order.getTotalPrice() + order.getExtraPrice();
if (ObjectUtil.isNotEmpty(payPrice)) {
payPrice -= order.getCouponPrice();
}
if (payPrice > 0) {
WalletBalanceTransactionDto reduceBuild = new WalletBalanceTransactionDto();
reduceBuild.setWalletId(order.getWalletId());
@@ -1078,6 +1097,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
}
// 订单表订单状态
Integer payPrice = isWalletPay ? order.getTotalPrice() + order.getExtraPrice() : 0;
if (ObjectUtil.isNotEmpty(order.getCouponPrice())) {
payPrice -= order.getCouponPrice();
}
HardOrderEntity update = HardOrderEntity.builder().id(order.getId()).payTime(LocalDateTime.now()).orderStatus(HardOrderStatusEnum.PAY.getType()).payPrice(payPrice).build();
if (ObjectUtil.isNotEmpty(order.getOrderNo())) {
update.setOrderNo(order.getOrderNo());
@@ -228,6 +228,8 @@
a.is_offline_order,
a.product_desc,
a.offline_pay_success,
a.coupon_record_id,
a.coupon_price,
a.create_by,
a.create_time,
a.update_by,
@@ -20,6 +20,7 @@ public enum ActUserReceiveEnum {
USER_LIMIT_REACHED("4", "已领取,核销码在我的礼券中"),
USER_CANNOT_PARTICIPATE("5", "用户不能参加此活动"),
OTHER_ERROR("6", "其他错误"),
GET_DONE("7", "已领完"),
USER_CAN_JOIN_ACT("99", "用户可以参加活动"),
// 特殊
@@ -24,4 +24,11 @@ public interface RemoteActCouponCancelService {
@PostMapping("/act-coupon-record/remote/cancel")
R<Boolean> remoteCancel(@RequestBody ActCouponRecordRemoteCancelDto dto);
/**
* 退回优惠券
*/
@NoToken
@GetMapping("/act-coupon-record/remote/return")
R<Boolean> remoteReturn(@RequestParam Long actCouponRecordId);
}
@@ -2,12 +2,14 @@ package com.bsd.pjcloud.wallet.controller.act;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
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.constant.act.ActCouponTypeEnum;
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;
@@ -65,6 +67,13 @@ public class ActCouponController {
@PostMapping
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_add')")
public R<Long> save(@RequestBody ActCouponEntity actCoupon) {
if (ObjectUtil.isNotEmpty(actCoupon.getTotalCount())) {
actCoupon.setSurplusCount(actCoupon.getTotalCount());
}
// 如果是立减券,则使用门槛设置为0
if (ObjectUtil.equals(actCoupon.getType(), ActCouponTypeEnum.LIJIAN_QUAN.getCode())) {
actCoupon.setMinPoint(0);
}
return R.ok(actCouponService.create(actCoupon));
}
@@ -76,6 +85,10 @@ public class ActCouponController {
@PutMapping
@PreAuthorize("@pms.hasPermission('wallet_actCoupon_edit')")
public R updateById(@RequestBody ActCouponEntity actCoupon) {
// 如果是立减券,则使用门槛设置为0
if (ObjectUtil.equals(actCoupon.getType(), ActCouponTypeEnum.LIJIAN_QUAN.getCode())) {
actCoupon.setMinPoint(0);
}
actCouponService.modify(actCoupon);
return R.ok();
}
@@ -64,6 +64,13 @@ public class ActCouponRecordController {
return R.ok(actCouponRecordService.remoteCancel(dto));
}
@Inner()
@Operation(summary = "远程退回优惠券", description = "远程退回优惠券")
@GetMapping("/remote/return")
R<Boolean> remoteReturn(@RequestParam Long actCouponRecordId) {
return R.ok(actCouponRecordService.remoteCancel(actCouponRecordId));
}
/* ************************************* end 远程调用 end ************************************* */
/* ************************************* start app start ************************************* */
@@ -13,4 +13,12 @@ public interface ActCouponMapper extends PjcloudBaseMapper<ActCouponEntity> {
Page<ActCouponVo> pageJoin(Page page, @Param("dto") ActCouponDto dto);
/**
* 原子扣减库存(乐观锁)
*
* @param couponId 优惠券ID
* @return 影响行数,如果返回0表示库存不足
*/
int deductStock(@Param("couponId") Long couponId);
}
@@ -20,7 +20,7 @@ public interface ActCouponLifecycleService {
*
* @param record 优惠券记录实体
*/
void afterReceive(ActCouponRecordEntity record, SysUser sysUser);
void afterReceive(ActCouponEntity coupon, ActCouponRecordEntity record, SysUser sysUser);
/**
* 核销前的校验
@@ -34,6 +34,11 @@ public interface ActCouponRecordService extends IService<ActCouponRecordEntity>
*/
Boolean remoteCancel(ActCouponRecordRemoteCancelDto dto);
/**
* 远程退回优惠券
*/
Boolean remoteCancel(Long actCouponRecordId);
/**
* 远程查询优惠券详情
*/
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
@@ -138,7 +139,7 @@ public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMappe
// 4. 执行领取逻辑
ActCouponRecordEntity record = actCouponComUtilService.doReceive(actCouponEntity, sysUser);
// 5. 优惠券领取成功后,执行生命周期方法
actCouponComUtilService.afterReceive(record, sysUser);
actCouponComUtilService.afterReceive(actCouponEntity, record, sysUser);
// 6. 领取后特殊处理
specialReceive(actCouponEntity, record, sysUser);
// 7. 生成核销二维码
@@ -161,6 +162,31 @@ public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMappe
return true;
}
/**
* 远程退回优惠券
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean remoteCancel(Long actCouponRecordId) {
ActCouponRecordEntity detail = baseMapper.selectById(actCouponRecordId);
if (ObjectUtil.isEmpty(detail)) {
log.error("|{}|远程退回优惠券,优惠券不存在", TAG);
return false;
}
// 使用UpdateWrapper将useTime设置为null
LambdaUpdateWrapper<ActCouponRecordEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper
.eq(ActCouponRecordEntity::getId, detail.getId())
.set(ActCouponRecordEntity::getStatus, ActCouponRecordStatusEnum.UNUSED.getCode())
.set(ActCouponRecordEntity::getUseTime, null)
.set(ActCouponRecordEntity::getOrderId, null)
.set(ActCouponRecordEntity::getUseAmount, null);
int rows = baseMapper.update(null, updateWrapper);
return rows > 0;
}
/**
* 远程查询优惠券详情
*/
@@ -192,7 +218,7 @@ public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMappe
if (totalPrice < detail.getMinPoint() && detail.getMinPoint() > 0) {
return 0;
}
return detail.getUseAmount();
return detail.getFaceValue();
}
// 折扣券
else if (ObjectUtil.equals(detail.getCouponType(), ActCouponTypeEnum.ZHEKOU_QUAN.getCode())) {
@@ -203,7 +229,7 @@ public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMappe
}
// 立减券
else if (ObjectUtil.equals(detail.getCouponType(), ActCouponTypeEnum.LIJIAN_QUAN.getCode())) {
return detail.getUseAmount();
return detail.getFaceValue();
} else {
return 0;
}
@@ -335,6 +361,10 @@ public class ActCouponRecordServiceImpl extends ServiceImpl<ActCouponRecordMappe
ActUserReceiveEnum specialCouponStatus = specialCoupon(actCouponEntity, userInfo.getSysUser());
if (ObjectUtil.isNotEmpty(specialCouponStatus)) {
userReceiveEnum = specialCouponStatus;
} else if (ObjectUtil.isNotEmpty(actCouponEntity.getTotalCount()) && actCouponEntity.getTotalCount() > 0) {
if (ObjectUtil.isNotEmpty(actCouponEntity.getSurplusCount()) && actCouponEntity.getSurplusCount() <= 0) {
userReceiveEnum = ActUserReceiveEnum.GET_DONE;
}
}
}
}
@@ -3,6 +3,7 @@ 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.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
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;
@@ -42,6 +43,14 @@ public class ActCouponComUtilService implements ActCouponLifecycleService {
throw new RuntimeException("此优惠券活动不存在");
}
if (ObjectUtil.isNotEmpty(coupon.getTotalCount()) && coupon.getTotalCount() > 0) {
if (ObjectUtil.isNotEmpty(coupon.getSurplusCount())) {
if (coupon.getSurplusCount() <= 0) {
throw new RuntimeException("此优惠券已领完");
}
}
}
assertStatus(coupon);
assertReceiveWindow(coupon);
assertLimit(coupon, userId);
@@ -89,8 +98,12 @@ public class ActCouponComUtilService implements ActCouponLifecycleService {
* 领取优惠券后的处理
*/
@Override
public void afterReceive(ActCouponRecordEntity record, SysUser sysUser) {
public void afterReceive(ActCouponEntity coupon, ActCouponRecordEntity record, SysUser sysUser) {
// 使用原子更新操作,避免并发问题
int rows = actCouponMapper.deductStock(coupon.getId());
if (rows == 0) {
throw new RuntimeException("优惠券库存不足或已被领完");
}
}
/**
@@ -4,30 +4,30 @@
<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,
a.min_point,
a.total_count,
a.surplus_count,
a.limit_count,
a.limit_type,
a.valid_type,
a.valid_start_time,
a.valid_end_time,
a.valid_days,
a.use_scope_type,
a.scope_id,
a.status,
a.description,
a.create_by,
a.create_time,
a.update_by,
a.update_time,
a.del_flag,
a.tenant_id
a.unique_code,
a.bind_notify_id,
a.name,
a.type,
a.face_value,
a.min_point,
a.total_count,
a.surplus_count,
a.limit_count,
a.limit_type,
a.valid_type,
a.valid_start_time,
a.valid_end_time,
a.valid_days,
a.use_scope_type,
a.scope_id,
a.status,
a.description,
a.create_by,
a.create_time,
a.update_by,
a.update_time,
a.del_flag,
a.tenant_id
</sql>
<sql id="whereA">
@@ -51,4 +51,14 @@
</where>
order by a.create_time desc , a.id desc
</select>
<!-- 原子扣减库存(乐观锁) -->
<update id="deductStock">
UPDATE act_coupon
SET surplus_count = surplus_count - 1,
update_time = NOW()
WHERE id = #{couponId}
AND surplus_count > 0
</update>
</mapper>
@@ -63,8 +63,15 @@
<select id="findList" resultType="com.bsd.pjcloud.wallet.api.vo.act.ActCouponRecordVo">
<include refid="selectA"/>
from act_coupon_record a
left join act_coupon c on c.id = a.coupon_id and c.del_flag = '0'
<where>
<include refid="whereA"/>
<if test="dto.useScopeType != null and dto.useScopeType != '' ">
and c.use_scope_type = #{dto.useScopeType}
</if>
<if test="dto.scopeId != null and dto.scopeId != '' ">
and c.scope_id = #{dto.scopeId}
</if>
</where>
order by a.create_time desc , a.id desc
</select>