diff --git a/mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java b/mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java index fd92888e1..4fcb9b40f 100644 --- a/mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java +++ b/mallinkAdmin/src/main/java/com/iformall/controller/market/WxCouponController.java @@ -17,6 +17,8 @@ import com.iformall.service.WxCouponPasswordService; import com.iformall.service.WxCouponService; import com.iformall.service.WxFlowService; import com.iformall.utils.DateUtils; +import com.iformall.utils.RedisLock; + import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParams; @@ -46,6 +48,8 @@ public class WxCouponController extends BaseController { private WxCouponChannelMapper wxCouponChannelMapper; @Autowired private WxFlowService wxFlowService; + @Autowired + RedisLock redisLock; @ApiOperation("分页列表接口") @GetMapping("list") @@ -95,7 +99,7 @@ public class WxCouponController extends BaseController { if (resultData.code != 200) { return resultData; } - + redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); //启动投放审批 if(wxCoupon.getFlowParams() !=null && wxCoupon.getFlowParams().size()>0) { wxCoupon.getFlowParams().put("businessId",wxCoupon.getId()); @@ -124,7 +128,7 @@ public class WxCouponController extends BaseController { return new ResultData(ResultData.ERROR, "有活动正在上架,请先下架。"); } } - + redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); //启动审批流 if (wxCoupon.getFlowParams() != null && wxCoupon.getFlowParams().size() > 0) { logger.info("------coupon.update().businessType:"+wxCoupon.getFlowParams().get("businessType")); @@ -139,6 +143,7 @@ public class WxCouponController extends BaseController { wxCoupon.setPutApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); } } + return wxCouponService.saveOrUpdate(wxCoupon); } @@ -246,7 +251,9 @@ public class WxCouponController extends BaseController { updateCoupon.setStockApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); updateCoupon.setUpdateDate(new Date()); wxCouponService.update(updateCoupon); + redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); } else { + redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); return wxCouponService.updateCouponStockAndEndTime(wxCoupon); } diff --git a/mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java b/mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java index 2f615f4aa..e297af145 100644 --- a/mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java +++ b/mallinkCApi/src/main/java/com/iformall/controller/WxCouponChannelController.java @@ -43,7 +43,7 @@ public class WxCouponChannelController extends BaseController { @Autowired private WxMerchantService wxMerchantService; - @RedisCache(expireTime = 600) + @RedisCache(expireTime = 100) @ApiOperation("分页列表接口") @GetMapping("list") @ApiImplicitParams({ @@ -101,7 +101,7 @@ public class WxCouponChannelController extends BaseController { } } - @RedisCache(expireTime = 600) + @RedisCache(expireTime = 100) @ApiOperation("分页列表接口") @GetMapping("change") public ResultData change(@ModelAttribute WxCouponChannel wxCouponChannel) { diff --git a/mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java b/mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java index 8f49733a8..33df70929 100644 --- a/mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java +++ b/mallinkMQConsumer/src/main/java/com/iformall/mq/MqBaseConsumer.java @@ -41,7 +41,9 @@ public class MqBaseConsumer { private FmInsideNotifyPaySuccessMsgServiceImpl fmInsideNotifyPaySuccessMsgService; @Autowired private FmInsideNotifyRefundSuccessMsgServiceImpl fmInsideNotifyRefundSuccessMsgService; - + @Autowired + UpdateCouponStockMsgServiceImpl updateCouponStockMsgService; + public void doMessage(String message) { @@ -95,6 +97,9 @@ public class MqBaseConsumer { // 内部消息 - 微信退款通知 FmInsideNotifyRefundSuccessMsg msg = (FmInsideNotifyRefundSuccessMsg)JsonUtil.readValue(message,FmInsideNotifyRefundSuccessMsg.class); fmInsideNotifyRefundSuccessMsgService.send(msg); + } else if (EnumMsgRecordType.COUPON_STOCK.getCode().equals(baseMsg.getMsgType())) { + UpdateCouponStockMsg msg = (UpdateCouponStockMsg)JsonUtil.readValue(message,UpdateCouponStockMsg.class); + updateCouponStockMsgService.send(msg); } baseMsg.setMsgStatus(EnumMsgRecordStatus.CONSUME_SUCC.getCode()); wxMsgRecordMapper.updateByBaseMsg(baseMsg); diff --git a/mallinkService/src/main/java/com/iformall/domain/po/msg/UpdateCouponStockMsg.java b/mallinkService/src/main/java/com/iformall/domain/po/msg/UpdateCouponStockMsg.java new file mode 100644 index 000000000..354273ad6 --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/domain/po/msg/UpdateCouponStockMsg.java @@ -0,0 +1,31 @@ +package com.iformall.domain.po.msg; + +import cn.binarywang.wx.miniapp.bean.WxMaTemplateData; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; +import java.util.Map; + +/** + * 更新券库存消息 + * @author Stormeye + * @date 2019/4/9 13:55 + */ +@Data +@ToString(callSuper = true) +@EqualsAndHashCode(callSuper = true) +public class UpdateCouponStockMsg extends BaseMsg { + private static final long serialVersionUID = 223232132183760321L; + + public static final String OPERATE_TYPE_INCREASE = "increase"; + public static final String OPERATE_TYPE_DECREASE = "decrease"; + + @io.swagger.annotations.ApiModelProperty(value="券Id",name="couponId") + private Long couponId; + @io.swagger.annotations.ApiModelProperty(value="更新数量",name="number") + private long number; + @io.swagger.annotations.ApiModelProperty(value="操作类型 1-增加库存 ,2-减少库存",name="operateType") + private String operateType; +} diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java b/mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java index 0f9ac04f6..d6e2d88ac 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumCacheKey.java @@ -9,7 +9,7 @@ import java.util.Map; public enum EnumCacheKey { C_INDEX_PAGE_LIST(0, "couponChannelList_"), - + COUPON_STOCK(1,"couponStock_") ; private static Map map = new HashMap(); static { diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java b/mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java index 30525cfdd..e342de989 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumMsgMqTopic.java @@ -5,6 +5,7 @@ package com.iformall.enums; */ public enum EnumMsgMqTopic { DEFAULT("topic-1", "默认"), + STOCK("stock","库存"), ; public static EnumMsgMqTopic getEnum(String code) { diff --git a/mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java b/mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java index dcb6cffb9..95b4583da 100644 --- a/mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java +++ b/mallinkService/src/main/java/com/iformall/enums/EnumMsgRecordType.java @@ -17,6 +17,7 @@ public enum EnumMsgRecordType { INSIDE_C_LOGIN(102, "C端用户登录"), INSIDE_NOTIFY_PAY_SUCCESS(103, "微信支付回调"), INSIDE_NOTIFY_REFUND_SUCCESS(104, "微信退款回调"), + COUPON_STOCK(105, "券库存更新"), ; public static EnumMsgRecordType getEnum(Integer code) { diff --git a/mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java b/mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java index 148b34fb8..46eabed99 100644 --- a/mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java +++ b/mallinkService/src/main/java/com/iformall/mapper/WxCouponMapper.java @@ -20,7 +20,7 @@ public interface WxCouponMapper extends CommonMapper { WxCouponCVo findVoDetail(@Param("id")Long id); WxCouponCVo findVoStatusDetail(@Param("id")Long id); - Integer reduceInventory(@Param("id")Long id,@Param("number")Integer number,@Param("remainInventory")Integer remainInventory); + Integer reduceInventory(@Param("id")Long id,@Param("number")Integer number); Integer backInventory(@Param("id")Long id, @Param("number")Integer number,@Param("remainInventory")Integer remainInventory); diff --git a/mallinkService/src/main/java/com/iformall/service/WxCouponService.java b/mallinkService/src/main/java/com/iformall/service/WxCouponService.java index aec2933b7..a403d896b 100644 --- a/mallinkService/src/main/java/com/iformall/service/WxCouponService.java +++ b/mallinkService/src/main/java/com/iformall/service/WxCouponService.java @@ -212,7 +212,7 @@ public interface WxCouponService { * @param number 减少数量 * @param remainInventory 当前库存 */ - void reduceRemainInventory(Long id, Integer number, Integer remainInventory); + void reduceRemainInventory(Long id, Integer number); /** * 库存退回 diff --git a/mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java index abd1d586d..d9cd6510f 100644 --- a/mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java +++ b/mallinkService/src/main/java/com/iformall/service/impl/WxCouponSendServiceImpl.java @@ -181,7 +181,7 @@ public class WxCouponSendServiceImpl implements WxCouponSendService { } //更新库存 - wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory, wxCouponOfDb.getRemainInventory()); + wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory); } @Override diff --git a/mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java index 4b31c2d05..321fb8267 100644 --- a/mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java +++ b/mallinkService/src/main/java/com/iformall/service/impl/WxCouponServiceImpl.java @@ -714,8 +714,8 @@ public class WxCouponServiceImpl implements WxCouponService { @Override @Transactional(isolation= Isolation.SERIALIZABLE, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class}) - public void reduceRemainInventory(Long id, Integer number, Integer remainInventory) { - int num = wxCouponMapper.reduceInventory(id, number, remainInventory); + public void reduceRemainInventory(Long id, Integer number) { + int num = wxCouponMapper.reduceInventory(id, number); if (num == 0) { throw new MallinkException(ErrorCode.ORDER_SAVE_ERR); } diff --git a/mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java index 5f7673db8..84882f22b 100644 --- a/mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java +++ b/mallinkService/src/main/java/com/iformall/service/impl/WxOrderServiceImpl.java @@ -1,5 +1,31 @@ package com.iformall.service.impl; +import java.math.BigDecimal; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; @@ -10,34 +36,99 @@ import com.iformall.common.IdWorker; import com.iformall.common.ResultData; import com.iformall.domain.dto.OrderSaveDto; import com.iformall.domain.dto.WxSharingOrderDto; -import com.iformall.domain.po.*; +import com.iformall.domain.po.WxAppinfo; +import com.iformall.domain.po.WxCUser; +import com.iformall.domain.po.WxCardInfo; +import com.iformall.domain.po.WxCoupon; +import com.iformall.domain.po.WxCouponChannel; +import com.iformall.domain.po.WxCouponMerchant; +import com.iformall.domain.po.WxCouponOrder; +import com.iformall.domain.po.WxCouponPassword; +import com.iformall.domain.po.WxCouponSend; +import com.iformall.domain.po.WxCreditHistory; +import com.iformall.domain.po.WxMerchant; +import com.iformall.domain.po.WxMerchantBUser; +import com.iformall.domain.po.WxOrder; +import com.iformall.domain.po.WxOrderGroup; +import com.iformall.domain.po.WxOrderPress; +import com.iformall.domain.po.WxPayAccount; +import com.iformall.domain.po.WxPayOrder; import com.iformall.domain.po.base.TenantEntity; import com.iformall.domain.po.msg.FmInsideOrderSuccessMsg; -import com.iformall.domain.vo.*; -import com.iformall.enums.*; +import com.iformall.domain.po.msg.UpdateCouponStockMsg; +import com.iformall.domain.vo.WxCouponSendVo; +import com.iformall.domain.vo.WxMerchantVo; +import com.iformall.domain.vo.WxOrderCouponPressVo; +import com.iformall.domain.vo.WxOrderCouponVo; +import com.iformall.domain.vo.WxOrderPayExpVo; +import com.iformall.domain.vo.WxOrderPayVo; +import com.iformall.domain.vo.WxOrderQueryVo; +import com.iformall.enums.EnumAssignTagsTrigger; +import com.iformall.enums.EnumCacheKey; +import com.iformall.enums.EnumCouponChannelStatus; +import com.iformall.enums.EnumCouponConditionType; +import com.iformall.enums.EnumCouponMerchantStatus; +import com.iformall.enums.EnumCouponOrderStatus; +import com.iformall.enums.EnumCouponPasswordStatus; +import com.iformall.enums.EnumCouponPasswordSupport; +import com.iformall.enums.EnumCouponSendSendType; +import com.iformall.enums.EnumCouponSendType; +import com.iformall.enums.EnumCouponStatus; +import com.iformall.enums.EnumCouponTransfer; +import com.iformall.enums.EnumCouponType; +import com.iformall.enums.EnumMerchantStatus; +import com.iformall.enums.EnumMerchantType; +import com.iformall.enums.EnumMsgMqKey; +import com.iformall.enums.EnumMsgMqTag; +import com.iformall.enums.EnumMsgMqTopic; +import com.iformall.enums.EnumMsgRecordType; +import com.iformall.enums.EnumOrderPressType; +import com.iformall.enums.EnumOrderStatus; +import com.iformall.enums.EnumOrderType; +import com.iformall.enums.EnumPayShare; +import com.iformall.enums.EnumPayStatus; +import com.iformall.enums.EnumPayType; +import com.iformall.enums.EnumProfitSharingOrderType; +import com.iformall.enums.EnumScoreType; +import com.iformall.enums.EnumUserType; +import com.iformall.enums.EnumValidStatus; import com.iformall.exception.MallinkException; -import com.iformall.mapper.*; +import com.iformall.mapper.WxAppinfoMapper; +import com.iformall.mapper.WxCUserBasicInfoMapper; +import com.iformall.mapper.WxCUserMapper; +import com.iformall.mapper.WxCardInfoMapper; +import com.iformall.mapper.WxCouponChannelMapper; +import com.iformall.mapper.WxCouponMapper; +import com.iformall.mapper.WxCouponMerchantMapper; +import com.iformall.mapper.WxCouponOrderMapper; +import com.iformall.mapper.WxCouponPasswordMapper; +import com.iformall.mapper.WxMallMapper; +import com.iformall.mapper.WxMerchantBUserMapper; +import com.iformall.mapper.WxMerchantMapper; +import com.iformall.mapper.WxOrderGroupMapper; +import com.iformall.mapper.WxOrderMapper; +import com.iformall.mapper.WxOrderPressMapper; +import com.iformall.mapper.WxPayAccountMapper; +import com.iformall.mapper.WxPayOrderMapper; import com.iformall.mq.MqBaseProducer; -import com.iformall.service.*; -import com.iformall.utils.*; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.math.BigDecimal; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; +import com.iformall.service.ExcelService; +import com.iformall.service.WxAppinfoService; +import com.iformall.service.WxCUserService; +import com.iformall.service.WxCUserTagsService; +import com.iformall.service.WxCouponActionLogService; +import com.iformall.service.WxCouponSendService; +import com.iformall.service.WxCreditHistoryService; +import com.iformall.service.WxMsgLimitService; +import com.iformall.service.WxOrderService; +import com.iformall.service.WxPayOrderService; +import com.iformall.service.WxProfitSharingOrderService; +import com.iformall.service.WxScoreRulesService; +import com.iformall.utils.Constant; +import com.iformall.utils.DateUtils; +import com.iformall.utils.PayUtils; +import com.iformall.utils.PressUtils; +import com.iformall.utils.RedisLock; +import com.iformall.utils.Utility; @Service public class WxOrderServiceImpl implements WxOrderService { @@ -479,94 +570,165 @@ public class WxOrderServiceImpl implements WxOrderService { } } - /** - * 减库存 - * - * @param user - * @param coupon - * @param couponIdStr - */ - private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) { - long time = System.currentTimeMillis() + RedisLock.TIMEOUT; - String timeStr = String.valueOf(time); - // 库存加锁 - if (!redisLock.lock(couponIdStr, timeStr)) { - logger.error("此券被锁定, couponId: " + couponIdStr); - throw new MallinkException(ErrorCode.TOO_MANY_REQUEST); - } - - // 检查 优惠券 库存 - if (coupon.getRemainInventory() <= 0) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - logger.error("此券库存为0, couponId: " + couponIdStr); - throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY); - } - - int count = 0; - // 有价券 - if (coupon.getSalePrice() > 0) { - // 检查是否未支付订单 - try { - count = getUserOrderCount(user, coupon); - } catch (Exception e) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - throw new MallinkException(ErrorCode.DB_FAIL); - } - if (count > 0) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count); - throw new MallinkException(ErrorCode.ORDER_UNPAID); - } - } - //检查拼团中的数量 - try { - count = getUserOrderGroupCount(user, coupon); - } catch (Exception e) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - throw new MallinkException(ErrorCode.DB_FAIL); - } - if (count >= coupon.getUseLimitQuantity()) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); - throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); - } - - // 检查用户已购买数量 - try { - count = getUserCouponOrderCount(user, coupon); - } catch (Exception e) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - throw new MallinkException(ErrorCode.DB_FAIL); - } - if (count >= coupon.getUseLimitQuantity()) { - //解锁 - redisLock.unlock(couponIdStr, timeStr); - logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); - throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); - } - - try { - // 减库存 - int num = wxCouponMapper.reduceInventory(coupon.getId(), 1, coupon.getRemainInventory()); - if (num <= 0) { - logger.error("此券减库存失败, couponId: " + couponIdStr); - throw new MallinkException(ErrorCode.ORDER_IS_FAIL); - } - } catch (RuntimeException e) { - logger.error("此券减库存失败, couponId: " + couponIdStr); - throw new MallinkException(ErrorCode.ORDER_IS_FAIL); - } finally { - // 解锁 - redisLock.unlock(couponIdStr, timeStr); - } - } + * 减库存 + * + * @param user + * @param coupon + * @param couponIdStr + */ + private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) { + + long redisStock = redisLock.getCouponStock(Long.parseLong(couponIdStr)); + if (redisStock<=0) { + throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"券库存已为零!"); + } + // 检查 优惠券 库存 + if (coupon.getRemainInventory() <= 0) { + throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY); + } + + int count = 0; + // 有价券 + if (coupon.getSalePrice() > 0) { + // 检查是否未支付订单 + try { + count = getUserOrderCount(user, coupon); + } catch (Exception e) { + throw new MallinkException(ErrorCode.DB_FAIL); + } + if (count > 0) { + logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count); + throw new MallinkException(ErrorCode.ORDER_UNPAID); + } + } + //检查拼团中的数量 + try { + count = getUserOrderGroupCount(user, coupon); + } catch (Exception e) { + throw new MallinkException(ErrorCode.DB_FAIL); + } + if (count >= coupon.getUseLimitQuantity()) { + logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); + throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); + } + + // 检查用户已购买数量 + try { + count = getUserCouponOrderCount(user, coupon); + } catch (Exception e) { + throw new MallinkException(ErrorCode.DB_FAIL); + } + if (count >= coupon.getUseLimitQuantity()) { + logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); + throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); + } + + try { + //减库存,从redis中取,然后发mq消息同步到数据库 + long stock = redisLock.decrease(EnumCacheKey.COUPON_STOCK.getMessage()+coupon.getId(), 1); + if (stock <= 0 ) { + throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"券库存已为零!"); + } + UpdateCouponStockMsg msg = new UpdateCouponStockMsg(); + msg.setCouponId(coupon.getId()); + msg.setNumber(1); + msg.setOperateType(UpdateCouponStockMsg.OPERATE_TYPE_DECREASE); + msg.setMsgType(EnumMsgRecordType.COUPON_STOCK.getCode()); + mqBaseProducer.sendMessage(msg, EnumMsgMqTopic.STOCK.getCode(), "*", "decrease_stock_"+coupon.getId()); + } catch (RuntimeException e) { + logger.error("此券减库存失败, couponId: " + couponIdStr); + throw new MallinkException(ErrorCode.ORDER_IS_FAIL); + } + } + +// /** +// * 减库存 +// * +// * @param user +// * @param coupon +// * @param couponIdStr +// */ +// private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) { +// long time = System.currentTimeMillis() + RedisLock.TIMEOUT; +// String timeStr = String.valueOf(time); +// // 库存加锁 +// if (!redisLock.lock(couponIdStr, timeStr)) { +// logger.error("此券被锁定, couponId: " + couponIdStr); +// throw new MallinkException(ErrorCode.TOO_MANY_REQUEST); +// } +// +// // 检查 优惠券 库存 +// if (coupon.getRemainInventory() <= 0) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// logger.error("此券库存为0, couponId: " + couponIdStr); +// throw new MallinkException(ErrorCode.REMAIN_IS_EMPTY); +// } +// +// int count = 0; +// // 有价券 +// if (coupon.getSalePrice() > 0) { +// // 检查是否未支付订单 +// try { +// count = getUserOrderCount(user, coupon); +// } catch (Exception e) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// throw new MallinkException(ErrorCode.DB_FAIL); +// } +// if (count > 0) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// logger.error("此券有未支付的订单, couponId: " + couponIdStr + ", count: " + count); +// throw new MallinkException(ErrorCode.ORDER_UNPAID); +// } +// } +// //检查拼团中的数量 +// try { +// count = getUserOrderGroupCount(user, coupon); +// } catch (Exception e) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// throw new MallinkException(ErrorCode.DB_FAIL); +// } +// if (count >= coupon.getUseLimitQuantity()) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); +// throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); +// } +// +// // 检查用户已购买数量 +// try { +// count = getUserCouponOrderCount(user, coupon); +// } catch (Exception e) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// throw new MallinkException(ErrorCode.DB_FAIL); +// } +// if (count >= coupon.getUseLimitQuantity()) { +// //解锁 +// redisLock.unlock(couponIdStr, timeStr); +// logger.error("此券购买数量已超限, couponId: " + couponIdStr + ", count: " + count); +// throw new MallinkException(ErrorCode.ORDER_IS_LIMITED); +// } +// +// try { +// // 减库存 +// int num = wxCouponMapper.reduceInventory(coupon.getId(), 1); +// if (num <= 0) { +// logger.error("此券减库存失败, couponId: " + couponIdStr); +// throw new MallinkException(ErrorCode.ORDER_IS_FAIL); +// } +// } catch (RuntimeException e) { +// logger.error("此券减库存失败, couponId: " + couponIdStr); +// throw new MallinkException(ErrorCode.ORDER_IS_FAIL); +// } finally { +// // 解锁 +// redisLock.unlock(couponIdStr, timeStr); +// } +// } @Override public void stockBack(WxOrder updateOrder) { @@ -575,7 +737,7 @@ public class WxOrderServiceImpl implements WxOrderService { Long couponId = updateOrder.getProductId(); String couponIdStr = "" + couponId; //加锁 - long time = System.currentTimeMillis() + RedisLock.TIMEOUT; + long time = System.currentTimeMillis() + 300*RedisLock.TIMEOUT; String timeStr = String.valueOf(time); if (!redisLock.lock(couponIdStr, timeStr)) { @@ -602,6 +764,7 @@ public class WxOrderServiceImpl implements WxOrderService { logger.error("此券加库存失败, couponId: " + couponIdStr); throw new MallinkException(ErrorCode.ORDER_IS_FAIL); } + redisLock.increase(EnumCacheKey.COUPON_STOCK.getMessage()+coupon.getId(), 1); } catch (Exception e) { logger.error("数据库更新失败,库存+1失败, e:" + e.getMessage()); throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "库存恢复失败"); @@ -1717,6 +1880,7 @@ public class WxOrderServiceImpl implements WxOrderService { throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION); } cUserTokenRedisTemplate.opsForValue().set(key, user, 1000, TimeUnit.MILLISECONDS); + order = saveFreeOrderForCoupon(user, coupon, orderSaveDto.getCouponChannelId(), orderSaveDto.getFormId(), null); if (order != null) { // 6. 下订单完成,发送内部消息 diff --git a/mallinkService/src/main/java/com/iformall/service/msg/impl/UpdateCouponStockMsgServiceImpl.java b/mallinkService/src/main/java/com/iformall/service/msg/impl/UpdateCouponStockMsgServiceImpl.java new file mode 100644 index 000000000..fe46cf65c --- /dev/null +++ b/mallinkService/src/main/java/com/iformall/service/msg/impl/UpdateCouponStockMsgServiceImpl.java @@ -0,0 +1,35 @@ +package com.iformall.service.msg.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.iformall.domain.po.msg.BaseMsg; +import com.iformall.domain.po.msg.UpdateCouponStockMsg; +import com.iformall.enums.EnumPayWay; +import com.iformall.service.WxCouponService; +import com.iformall.service.msg.MsgSendService; + +/** + * + * @author Stormeye Wu + * @date 2019/5/10 + */ +@Service +public class UpdateCouponStockMsgServiceImpl implements MsgSendService { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private WxCouponService wxCouponService; + + @Override + public void send(BaseMsg baseMsg) throws Exception { + UpdateCouponStockMsg msg = (UpdateCouponStockMsg)baseMsg; + if (null != msg ) { + if (msg.getOperateType().equals(UpdateCouponStockMsg.OPERATE_TYPE_DECREASE)) { + wxCouponService.reduceRemainInventory(msg.getCouponId(), (int)msg.getNumber()); + } + } + } +} diff --git a/mallinkService/src/main/java/com/iformall/utils/RedisLock.java b/mallinkService/src/main/java/com/iformall/utils/RedisLock.java index 47b18bc17..07a05aff5 100644 --- a/mallinkService/src/main/java/com/iformall/utils/RedisLock.java +++ b/mallinkService/src/main/java/com/iformall/utils/RedisLock.java @@ -8,6 +8,8 @@ import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; +import com.iformall.enums.EnumCacheKey; + import java.util.concurrent.TimeUnit; /** @@ -24,7 +26,7 @@ public class RedisLock { private StringRedisTemplate stringRedisTemplate; public static final int TIMEOUT = 10*1000;//超时时间 10s - + /** * 加锁 * @param key productId - 商品的唯一标志 @@ -102,5 +104,49 @@ public class RedisLock { logger.error("[Redis分布式锁] 解锁出现异常了,{}",e); } } - + + + /** + * 原子性增加 + */ + public long increase(String key, long delta) { + try { + return stringRedisTemplate.opsForValue().increment(key, delta); + }catch(Exception e) { + logger.error("[Redis分布式锁] increase error,{}",e); + } + return 0; + } + + /** + * 原子性减少 + */ + public long decrease(String key, long delta) { + try { + stringRedisTemplate.opsForValue().decrement(key, delta); + }catch(Exception e) { + logger.error("[Redis分布式锁] decrease error,{}",e); + } + return 0; + } + + + public long getCouponStock(long couponId) { + try { + String longstr = stringRedisTemplate.opsForValue().get(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId)); + return Long.parseLong(longstr); + }catch (Exception e) { + logger.error("[Redis分布式锁] getCouponStock error,{}",e); + } + return 0; + } + + public void setCouponStock(long couponId,long number) { + try { + stringRedisTemplate.opsForValue().set(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId), String.valueOf(number)); + }catch(Exception e) { + logger.error("[Redis分布式锁] setCouponStock error,{}",e); + } + + } }