| @@ -17,6 +17,8 @@ import com.iformall.service.WxCouponPasswordService; | |||||
| import com.iformall.service.WxCouponService; | import com.iformall.service.WxCouponService; | ||||
| import com.iformall.service.WxFlowService; | import com.iformall.service.WxFlowService; | ||||
| import com.iformall.utils.DateUtils; | import com.iformall.utils.DateUtils; | ||||
| import com.iformall.utils.RedisLock; | |||||
| import io.swagger.annotations.Api; | import io.swagger.annotations.Api; | ||||
| import io.swagger.annotations.ApiImplicitParam; | import io.swagger.annotations.ApiImplicitParam; | ||||
| import io.swagger.annotations.ApiImplicitParams; | import io.swagger.annotations.ApiImplicitParams; | ||||
| @@ -46,6 +48,8 @@ public class WxCouponController extends BaseController { | |||||
| private WxCouponChannelMapper wxCouponChannelMapper; | private WxCouponChannelMapper wxCouponChannelMapper; | ||||
| @Autowired | @Autowired | ||||
| private WxFlowService wxFlowService; | private WxFlowService wxFlowService; | ||||
| @Autowired | |||||
| RedisLock redisLock; | |||||
| @ApiOperation("分页列表接口") | @ApiOperation("分页列表接口") | ||||
| @GetMapping("list") | @GetMapping("list") | ||||
| @@ -95,7 +99,7 @@ public class WxCouponController extends BaseController { | |||||
| if (resultData.code != 200) { | if (resultData.code != 200) { | ||||
| return resultData; | return resultData; | ||||
| } | } | ||||
| redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); | |||||
| //启动投放审批 | //启动投放审批 | ||||
| if(wxCoupon.getFlowParams() !=null && wxCoupon.getFlowParams().size()>0) { | if(wxCoupon.getFlowParams() !=null && wxCoupon.getFlowParams().size()>0) { | ||||
| wxCoupon.getFlowParams().put("businessId",wxCoupon.getId()); | wxCoupon.getFlowParams().put("businessId",wxCoupon.getId()); | ||||
| @@ -124,7 +128,7 @@ public class WxCouponController extends BaseController { | |||||
| return new ResultData(ResultData.ERROR, "有活动正在上架,请先下架。"); | return new ResultData(ResultData.ERROR, "有活动正在上架,请先下架。"); | ||||
| } | } | ||||
| } | } | ||||
| redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); | |||||
| //启动审批流 | //启动审批流 | ||||
| if (wxCoupon.getFlowParams() != null && wxCoupon.getFlowParams().size() > 0) { | if (wxCoupon.getFlowParams() != null && wxCoupon.getFlowParams().size() > 0) { | ||||
| logger.info("------coupon.update().businessType:"+wxCoupon.getFlowParams().get("businessType")); | logger.info("------coupon.update().businessType:"+wxCoupon.getFlowParams().get("businessType")); | ||||
| @@ -139,6 +143,7 @@ public class WxCouponController extends BaseController { | |||||
| wxCoupon.setPutApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); | wxCoupon.setPutApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); | ||||
| } | } | ||||
| } | } | ||||
| return wxCouponService.saveOrUpdate(wxCoupon); | return wxCouponService.saveOrUpdate(wxCoupon); | ||||
| } | } | ||||
| @@ -246,7 +251,9 @@ public class WxCouponController extends BaseController { | |||||
| updateCoupon.setStockApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); | updateCoupon.setStockApplyStatus(EnumRentContractAppStatus.APPLYING.getCode()); | ||||
| updateCoupon.setUpdateDate(new Date()); | updateCoupon.setUpdateDate(new Date()); | ||||
| wxCouponService.update(updateCoupon); | wxCouponService.update(updateCoupon); | ||||
| redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); | |||||
| } else { | } else { | ||||
| redisLock.setCouponStock(wxCoupon.getId(), wxCoupon.getInventory()); | |||||
| return wxCouponService.updateCouponStockAndEndTime(wxCoupon); | return wxCouponService.updateCouponStockAndEndTime(wxCoupon); | ||||
| } | } | ||||
| @@ -43,7 +43,7 @@ public class WxCouponChannelController extends BaseController { | |||||
| @Autowired | @Autowired | ||||
| private WxMerchantService wxMerchantService; | private WxMerchantService wxMerchantService; | ||||
| @RedisCache(expireTime = 600) | |||||
| @RedisCache(expireTime = 100) | |||||
| @ApiOperation("分页列表接口") | @ApiOperation("分页列表接口") | ||||
| @GetMapping("list") | @GetMapping("list") | ||||
| @ApiImplicitParams({ | @ApiImplicitParams({ | ||||
| @@ -101,7 +101,7 @@ public class WxCouponChannelController extends BaseController { | |||||
| } | } | ||||
| } | } | ||||
| @RedisCache(expireTime = 600) | |||||
| @RedisCache(expireTime = 100) | |||||
| @ApiOperation("分页列表接口") | @ApiOperation("分页列表接口") | ||||
| @GetMapping("change") | @GetMapping("change") | ||||
| public ResultData change(@ModelAttribute WxCouponChannel wxCouponChannel) { | public ResultData change(@ModelAttribute WxCouponChannel wxCouponChannel) { | ||||
| @@ -41,7 +41,9 @@ public class MqBaseConsumer { | |||||
| private FmInsideNotifyPaySuccessMsgServiceImpl fmInsideNotifyPaySuccessMsgService; | private FmInsideNotifyPaySuccessMsgServiceImpl fmInsideNotifyPaySuccessMsgService; | ||||
| @Autowired | @Autowired | ||||
| private FmInsideNotifyRefundSuccessMsgServiceImpl fmInsideNotifyRefundSuccessMsgService; | private FmInsideNotifyRefundSuccessMsgServiceImpl fmInsideNotifyRefundSuccessMsgService; | ||||
| @Autowired | |||||
| UpdateCouponStockMsgServiceImpl updateCouponStockMsgService; | |||||
| public void doMessage(String message) { | public void doMessage(String message) { | ||||
| @@ -95,6 +97,9 @@ public class MqBaseConsumer { | |||||
| // 内部消息 - 微信退款通知 | // 内部消息 - 微信退款通知 | ||||
| FmInsideNotifyRefundSuccessMsg msg = (FmInsideNotifyRefundSuccessMsg)JsonUtil.readValue(message,FmInsideNotifyRefundSuccessMsg.class); | FmInsideNotifyRefundSuccessMsg msg = (FmInsideNotifyRefundSuccessMsg)JsonUtil.readValue(message,FmInsideNotifyRefundSuccessMsg.class); | ||||
| fmInsideNotifyRefundSuccessMsgService.send(msg); | 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()); | baseMsg.setMsgStatus(EnumMsgRecordStatus.CONSUME_SUCC.getCode()); | ||||
| wxMsgRecordMapper.updateByBaseMsg(baseMsg); | wxMsgRecordMapper.updateByBaseMsg(baseMsg); | ||||
| @@ -1,33 +1,45 @@ | |||||
| package com.iformall.schedule; | package com.iformall.schedule; | ||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.aop.framework.AopContext; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.scheduling.annotation.Scheduled; | |||||
| import org.springframework.stereotype.Component; | |||||
| import org.springframework.transaction.annotation.Propagation; | |||||
| import org.springframework.transaction.annotation.Transactional; | |||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | ||||
| import com.iformall.common.ErrorCode; | import com.iformall.common.ErrorCode; | ||||
| import com.iformall.domain.po.WxAppinfo; | import com.iformall.domain.po.WxAppinfo; | ||||
| import com.iformall.domain.po.WxCUser; | |||||
| import com.iformall.domain.po.WxOrder; | import com.iformall.domain.po.WxOrder; | ||||
| import com.iformall.domain.po.WxPayAccount; | |||||
| import com.iformall.domain.po.WxPayOrder; | |||||
| import com.iformall.enums.EnumAppType; | import com.iformall.enums.EnumAppType; | ||||
| import com.iformall.enums.EnumOrderStatus; | import com.iformall.enums.EnumOrderStatus; | ||||
| import com.iformall.enums.EnumOrderType; | import com.iformall.enums.EnumOrderType; | ||||
| import com.iformall.enums.EnumPayStatus; | |||||
| import com.iformall.enums.EnumRefundStatus; | import com.iformall.enums.EnumRefundStatus; | ||||
| import com.iformall.exception.MallinkException; | import com.iformall.exception.MallinkException; | ||||
| import com.iformall.mapper.WxAppinfoMapper; | import com.iformall.mapper.WxAppinfoMapper; | ||||
| import com.iformall.mapper.WxCUserMapper; | |||||
| import com.iformall.mapper.WxCouponMapper; | import com.iformall.mapper.WxCouponMapper; | ||||
| import com.iformall.mapper.WxOrderGroupMapper; | import com.iformall.mapper.WxOrderGroupMapper; | ||||
| import com.iformall.mapper.WxOrderMapper; | import com.iformall.mapper.WxOrderMapper; | ||||
| import com.iformall.mapper.WxPayAccountMapper; | |||||
| import com.iformall.mapper.WxPayOrderMapper; | |||||
| import com.iformall.service.WxAppinfoService; | |||||
| import com.iformall.service.WxOrderService; | import com.iformall.service.WxOrderService; | ||||
| import com.iformall.service.WxPayOrderService; | |||||
| import com.iformall.service.WxRefundOrderService; | import com.iformall.service.WxRefundOrderService; | ||||
| import com.iformall.service.helper.WxPayOrderServiceHelper; | |||||
| import com.iformall.utils.DateUtils; | import com.iformall.utils.DateUtils; | ||||
| import org.slf4j.Logger; | |||||
| import org.slf4j.LoggerFactory; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.scheduling.annotation.Scheduled; | |||||
| import org.springframework.stereotype.Component; | |||||
| import org.springframework.transaction.annotation.Propagation; | |||||
| import org.springframework.transaction.annotation.Transactional; | |||||
| import java.util.Date; | |||||
| import java.util.HashMap; | |||||
| import java.util.List; | |||||
| import java.util.Map; | |||||
| @Component | @Component | ||||
| public class OrderExpiringSchedule { | public class OrderExpiringSchedule { | ||||
| @@ -53,7 +65,22 @@ public class OrderExpiringSchedule { | |||||
| @Autowired | @Autowired | ||||
| WxCouponMapper wxCouponMapper; | WxCouponMapper wxCouponMapper; | ||||
| @Autowired | |||||
| WxPayOrderMapper wxPayOrderMapper; | |||||
| @Autowired | |||||
| WxCUserMapper wxCUserMapper; | |||||
| @Autowired | |||||
| WxAppinfoService wxAppinfoService; | |||||
| @Autowired | |||||
| WxPayAccountMapper wxPayAccountMapper; | |||||
| @Autowired | |||||
| WxPayOrderService wxPayOrderService; | |||||
| @Scheduled(cron = "0 */5 * * * *?") // 每5分钟检查一次 | @Scheduled(cron = "0 */5 * * * *?") // 每5分钟检查一次 | ||||
| //@Scheduled(cron = "*/10 * * * * ?") // 测试10秒中一次 | //@Scheduled(cron = "*/10 * * * * ?") // 测试10秒中一次 | ||||
| public void orderExpireSchedule() { | public void orderExpireSchedule() { | ||||
| @@ -62,13 +89,16 @@ public class OrderExpiringSchedule { | |||||
| Date curDate = new Date(); | Date curDate = new Date(); | ||||
| Date expireDate = new Date(curDate.getTime() - NORMAL_TIME_OUT ); | Date expireDate = new Date(curDate.getTime() - NORMAL_TIME_OUT ); | ||||
| //直接调用不会触发事务,spring事务基于AOP代理 | |||||
| OrderExpiringSchedule proxy = (OrderExpiringSchedule) AopContext.currentProxy(); | |||||
| // 券相关订单, 15分钟未支付, 订单过期 | // 券相关订单, 15分钟未支付, 订单过期 | ||||
| dateMap.put("type", EnumOrderType.COUPON.getCode()); | dateMap.put("type", EnumOrderType.COUPON.getCode()); | ||||
| dateMap.put("endDate", expireDate); | dateMap.put("endDate", expireDate); | ||||
| List<WxOrder> orderCoupons = wxOrderMapper.findListOfUnpaidOrderByDate(dateMap); | List<WxOrder> orderCoupons = wxOrderMapper.findListOfUnpaidOrderByDate(dateMap); | ||||
| orderCoupons.stream().forEach( o -> { | orderCoupons.stream().forEach( o -> { | ||||
| try { | try { | ||||
| orderExpired(o); | |||||
| proxy.orderExpired(o); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("券过期失败" + e.getMessage()); | logger.error("券过期失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -82,7 +112,7 @@ public class OrderExpiringSchedule { | |||||
| List<WxOrder> orderPresss = wxOrderMapper.findListOfCompleteUnPaidPressOrderByDate(dateMap); | List<WxOrder> orderPresss = wxOrderMapper.findListOfCompleteUnPaidPressOrderByDate(dateMap); | ||||
| orderPresss.stream().forEach( o -> { | orderPresss.stream().forEach( o -> { | ||||
| try { | try { | ||||
| orderPressExpired(o, true); | |||||
| proxy.orderPressExpired(o, true); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("砍价券支付过期失败" + e.getMessage()); | logger.error("砍价券支付过期失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -93,7 +123,7 @@ public class OrderExpiringSchedule { | |||||
| orderPresss = wxOrderMapper.findListOfUnpaidPressOrderByDate(dateMap); | orderPresss = wxOrderMapper.findListOfUnpaidPressOrderByDate(dateMap); | ||||
| orderPresss.stream().forEach( o -> { | orderPresss.stream().forEach( o -> { | ||||
| try { | try { | ||||
| orderPressExpired(o, false); | |||||
| proxy.orderPressExpired(o, false); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("砍价券过期失败" +e.getMessage()); | logger.error("砍价券过期失败" +e.getMessage()); | ||||
| } | } | ||||
| @@ -105,7 +135,7 @@ public class OrderExpiringSchedule { | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_UNPAID.getCode()); | order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_UNPAID.getCode()); | ||||
| wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | ||||
| try { | try { | ||||
| orderGroupExpired((WxOrder) o); | |||||
| proxy.orderGroupExpired((WxOrder) o); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("拼团取消失败" + e.getMessage()); | logger.error("拼团取消失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -117,7 +147,7 @@ public class OrderExpiringSchedule { | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_OVERTIME.getCode()); | order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_OVERTIME.getCode()); | ||||
| wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | ||||
| try { | try { | ||||
| returnMoney((WxOrder) o); | |||||
| proxy.returnMoney((WxOrder) o); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("拼团过期退款失败" + e.getMessage()); | logger.error("拼团过期退款失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -128,7 +158,7 @@ public class OrderExpiringSchedule { | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_CANCEL.getCode()); | order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_CANCEL.getCode()); | ||||
| wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | ||||
| try { | try { | ||||
| returnMoney((WxOrder) o); | |||||
| proxy.returnMoney((WxOrder) o); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("拼团券下架退款失败" + e.getMessage()); | logger.error("拼团券下架退款失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -138,7 +168,7 @@ public class OrderExpiringSchedule { | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_FAILD.getCode()); | order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_FAILD.getCode()); | ||||
| wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | wxOrderMapper.selectList(new QueryWrapper(order)).stream().forEach(o -> { | ||||
| try { | try { | ||||
| returnMoney((WxOrder) o); | |||||
| proxy.returnMoney((WxOrder) o); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("参团失败退款失败" + e.getMessage()); | logger.error("参团失败退款失败" + e.getMessage()); | ||||
| } | } | ||||
| @@ -147,9 +177,49 @@ public class OrderExpiringSchedule { | |||||
| } | } | ||||
| public boolean wxPaySuccess(WxOrder order) { | |||||
| WxPayOrder payOrderq = new WxPayOrder(); | |||||
| payOrderq.setOrderId(order.getId()); | |||||
| List<WxPayOrder> payorderlist = wxPayOrderMapper.findList(payOrderq); | |||||
| if (null == payorderlist || payorderlist.size() <=0 ) { | |||||
| return false; | |||||
| } | |||||
| WxPayOrder wxpayOrder = payorderlist.get(0); | |||||
| if (null == wxpayOrder) { | |||||
| return false; | |||||
| } | |||||
| WxCUser user = wxCUserMapper.selectById(wxpayOrder.getCUserId()); | |||||
| //找不到用户,问题单,不能删 | |||||
| if (null == user) { | |||||
| return true; | |||||
| } | |||||
| WxAppinfo appInfo = wxAppinfoService.getByAppId(user.getAppId()); | |||||
| //找不到appInfo,问题单,不能删 | |||||
| if(appInfo == null) { | |||||
| return true; | |||||
| } | |||||
| WxPayAccount payAccount = wxPayAccountMapper.selectById(appInfo.getPayId()); | |||||
| //找不到appInfo,问题单,不能删 | |||||
| if (null == payAccount) { | |||||
| return true; | |||||
| } | |||||
| Map<String, String> retMap = WxPayOrderServiceHelper.wxOrderPayStatusMap(wxpayOrder, order, appInfo, payAccount); | |||||
| int status = WxPayOrderServiceHelper.getPayStatusFromMap(retMap, wxpayOrder.getPayOrderNo()); | |||||
| //如果是支付成功,则更新信息 | |||||
| if (EnumPayStatus.PAY_STATUS_SUCCESS.getCode() == status) { | |||||
| wxPayOrderService.handleSuccessOrder(wxpayOrder, order, retMap); | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| public void orderExpired(WxOrder order) { | public void orderExpired(WxOrder order) { | ||||
| if (wxPaySuccess(order)) { | |||||
| return; | |||||
| } | |||||
| // 库存退回 | // 库存退回 | ||||
| wxOrderService.stockBack(order); | wxOrderService.stockBack(order); | ||||
| @@ -168,6 +238,10 @@ public class OrderExpiringSchedule { | |||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| public void orderPressExpired(WxOrder order, boolean payExpired) { | public void orderPressExpired(WxOrder order, boolean payExpired) { | ||||
| if (wxPaySuccess(order)) { | |||||
| return; | |||||
| } | |||||
| Date curDate = new Date(); | Date curDate = new Date(); | ||||
| WxOrder updateOrder = new WxOrder(); | WxOrder updateOrder = new WxOrder(); | ||||
| updateOrder.setId(order.getId()); | updateOrder.setId(order.getId()); | ||||
| @@ -188,7 +262,11 @@ public class OrderExpiringSchedule { | |||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| public void orderGroupExpired(WxOrder order) { | public void orderGroupExpired(WxOrder order) { | ||||
| Date curDate = new Date(); | |||||
| if (wxPaySuccess(order)) { | |||||
| return; | |||||
| } | |||||
| Date curDate = new Date(); | |||||
| WxOrder updateOrder = new WxOrder(); | WxOrder updateOrder = new WxOrder(); | ||||
| updateOrder.setId(order.getId()); | updateOrder.setId(order.getId()); | ||||
| updateOrder.updateTenantInfo(order); | updateOrder.updateTenantInfo(order); | ||||
| @@ -205,7 +283,10 @@ public class OrderExpiringSchedule { | |||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| public void returnMoney(WxOrder order) { | public void returnMoney(WxOrder order) { | ||||
| //支付成功的才退款 | |||||
| if (!wxPaySuccess(order)) { | |||||
| return; | |||||
| } | |||||
| //获取APP信息 | //获取APP信息 | ||||
| WxAppinfo appinfo = new WxAppinfo(); | WxAppinfo appinfo = new WxAppinfo(); | ||||
| appinfo.updateTenantInfo(order); | appinfo.updateTenantInfo(order); | ||||
| @@ -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; | |||||
| } | |||||
| @@ -9,7 +9,7 @@ import java.util.Map; | |||||
| public enum EnumCacheKey { | public enum EnumCacheKey { | ||||
| C_INDEX_PAGE_LIST(0, "couponChannelList_"), | C_INDEX_PAGE_LIST(0, "couponChannelList_"), | ||||
| COUPON_STOCK(1,"couponStock_") | |||||
| ; | ; | ||||
| private static Map<Integer,EnumCacheKey> map = new HashMap<Integer,EnumCacheKey>(); | private static Map<Integer,EnumCacheKey> map = new HashMap<Integer,EnumCacheKey>(); | ||||
| static { | static { | ||||
| @@ -5,6 +5,7 @@ package com.iformall.enums; | |||||
| */ | */ | ||||
| public enum EnumMsgMqTopic { | public enum EnumMsgMqTopic { | ||||
| DEFAULT("topic-1", "默认"), | DEFAULT("topic-1", "默认"), | ||||
| STOCK("stock","库存"), | |||||
| ; | ; | ||||
| public static EnumMsgMqTopic getEnum(String code) { | public static EnumMsgMqTopic getEnum(String code) { | ||||
| @@ -17,6 +17,7 @@ public enum EnumMsgRecordType { | |||||
| INSIDE_C_LOGIN(102, "C端用户登录"), | INSIDE_C_LOGIN(102, "C端用户登录"), | ||||
| INSIDE_NOTIFY_PAY_SUCCESS(103, "微信支付回调"), | INSIDE_NOTIFY_PAY_SUCCESS(103, "微信支付回调"), | ||||
| INSIDE_NOTIFY_REFUND_SUCCESS(104, "微信退款回调"), | INSIDE_NOTIFY_REFUND_SUCCESS(104, "微信退款回调"), | ||||
| COUPON_STOCK(105, "券库存更新"), | |||||
| ; | ; | ||||
| public static EnumMsgRecordType getEnum(Integer code) { | public static EnumMsgRecordType getEnum(Integer code) { | ||||
| @@ -20,7 +20,7 @@ public interface WxCouponMapper extends CommonMapper<WxCoupon, Long> { | |||||
| WxCouponCVo findVoDetail(@Param("id")Long id); | WxCouponCVo findVoDetail(@Param("id")Long id); | ||||
| WxCouponCVo findVoStatusDetail(@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); | Integer backInventory(@Param("id")Long id, @Param("number")Integer number,@Param("remainInventory")Integer remainInventory); | ||||
| @@ -212,7 +212,7 @@ public interface WxCouponService { | |||||
| * @param number 减少数量 | * @param number 减少数量 | ||||
| * @param remainInventory 当前库存 | * @param remainInventory 当前库存 | ||||
| */ | */ | ||||
| void reduceRemainInventory(Long id, Integer number, Integer remainInventory); | |||||
| void reduceRemainInventory(Long id, Integer number); | |||||
| /** | /** | ||||
| * 库存退回 | * 库存退回 | ||||
| @@ -15,6 +15,8 @@ import com.iformall.enums.EnumPayWay; | |||||
| public interface WxPayOrderService { | public interface WxPayOrderService { | ||||
| public void handleSuccessOrder(WxPayOrder oldRecord,WxOrder order,Map<String, String> retMap); | |||||
| public WxPayOrder handleWxOrderQuery(WxPayOrder oldRecord,WxOrder order,WxAppinfo appInfo,WxPayAccount payAccount,IdWorker idworker,boolean repeatPay); | public WxPayOrder handleWxOrderQuery(WxPayOrder oldRecord,WxOrder order,WxAppinfo appInfo,WxPayAccount payAccount,IdWorker idworker,boolean repeatPay); | ||||
| /** | /** | ||||
| @@ -181,7 +181,7 @@ public class WxCouponSendServiceImpl implements WxCouponSendService { | |||||
| } | } | ||||
| //更新库存 | //更新库存 | ||||
| wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory, wxCouponOfDb.getRemainInventory()); | |||||
| wxCouponService.reduceRemainInventory(wxCouponOfDb.getId(), merchantLnventory); | |||||
| } | } | ||||
| @Override | @Override | ||||
| @@ -714,8 +714,8 @@ public class WxCouponServiceImpl implements WxCouponService { | |||||
| @Override | @Override | ||||
| @Transactional(isolation= Isolation.SERIALIZABLE, propagation = Propagation.REQUIRED, rollbackFor = {Exception.class}) | @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) { | if (num == 0) { | ||||
| throw new MallinkException(ErrorCode.ORDER_SAVE_ERR); | throw new MallinkException(ErrorCode.ORDER_SAVE_ERR); | ||||
| } | } | ||||
| @@ -34,8 +34,6 @@ import java.util.stream.Collectors; | |||||
| public class WxOrderGroupServiceImpl implements WxOrderGroupService { | public class WxOrderGroupServiceImpl implements WxOrderGroupService { | ||||
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); | private final Logger logger = LoggerFactory.getLogger(this.getClass()); | ||||
| private final Long notOrderGroupId = 0L; | |||||
| private final int endPeople = 0; | |||||
| @Autowired | @Autowired | ||||
| WxOrderGroupMapper wxOrderGroupMapper; | WxOrderGroupMapper wxOrderGroupMapper; | ||||
| @@ -72,22 +70,30 @@ public class WxOrderGroupServiceImpl implements WxOrderGroupService { | |||||
| public void createOrderGroup(WxOrder order, WxCoupon wxCoupon) { | public void createOrderGroup(WxOrder order, WxCoupon wxCoupon) { | ||||
| Long orderGroupId = order.getOrderGroupId(); | Long orderGroupId = order.getOrderGroupId(); | ||||
| //查看是否首次拼团,orderGroupId不等于0代表不是首次拼团,因为第二个加入的拼团者肯定有团单号 | //查看是否首次拼团,orderGroupId不等于0代表不是首次拼团,因为第二个加入的拼团者肯定有团单号 | ||||
| if (!orderGroupId.equals(notOrderGroupId)) { | |||||
| if (!orderGroupId.equals(0L)) { | |||||
| // 非首次拼团 | // 非首次拼团 | ||||
| // 参与拼团 | // 参与拼团 | ||||
| WxOrderGroup wxOrderGroup = wxOrderGroupMapper.selectById(orderGroupId); | WxOrderGroup wxOrderGroup = wxOrderGroupMapper.selectById(orderGroupId); | ||||
| if(wxOrderGroup.getRemainPeople() < endPeople + 1) { | |||||
| logger.info(">>>>>>>>>>>>参团失败 修改订单状态后定时退款 团单号" + orderGroupId); | |||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | |||||
| if (null == wxOrderGroup) { | |||||
| //错误,更新状态,定时任务会发起退款 | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_FAILD.getCode()); | |||||
| wxOrderMapper.updateById(order); | |||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD.getCode(),"WxOrderGroup is null."+orderGroupId); | |||||
| } | |||||
| if(wxOrderGroup.getRemainPeople()<1) { | |||||
| //错误,更新状态,定时任务会发起退款 | |||||
| order.setOrderStatus(EnumOrderStatus.ORDER_STATUS_COOPERATING_FAILD.getCode()); | |||||
| wxOrderMapper.updateById(order); | |||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD.getCode(),"参团人数已满,将稍后退款。"); | |||||
| } | } | ||||
| Integer remainPeople = wxOrderGroup.getRemainPeople()-1; | Integer remainPeople = wxOrderGroup.getRemainPeople()-1; | ||||
| wxOrderGroup.setStatus(remainPeople == endPeople ? EnumOrderStatus.ORDER_STATUS_COOPERATING_COMPLETE.getCode() : wxOrderGroup.getStatus()); | |||||
| wxOrderGroup.setStatus(remainPeople == 0 ? EnumOrderStatus.ORDER_STATUS_COOPERATING_COMPLETE.getCode() : wxOrderGroup.getStatus()); | |||||
| wxOrderGroup.setUpdateDate(new Date()); | wxOrderGroup.setUpdateDate(new Date()); | ||||
| logger.info("非首次拼团,更新拼团信息, 剩余人数-1"); | logger.info("非首次拼团,更新拼团信息, 剩余人数-1"); | ||||
| int row = wxOrderGroupMapper.updateOrderGroup(wxOrderGroup); | int row = wxOrderGroupMapper.updateOrderGroup(wxOrderGroup); | ||||
| if (row != 1) { | if (row != 1) { | ||||
| logger.info(">>>>>>>>>>>>参团失败 修改订单状态后定时退款 团单号" + orderGroupId); | |||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | |||||
| //TODO 这个时候应该要发起异步退款。 | |||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD.getCode(),"参团失败,将稍后退款."); | |||||
| } | } | ||||
| // 更新订单信息 | // 更新订单信息 | ||||
| @@ -100,7 +106,7 @@ public class WxOrderGroupServiceImpl implements WxOrderGroupService { | |||||
| logger.error("更新订单信息失败", e); | logger.error("更新订单信息失败", e); | ||||
| throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | throw new MallinkException(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | ||||
| } | } | ||||
| if (remainPeople == endPeople) { | |||||
| if (remainPeople == 0) { | |||||
| actionAfterOrderGroupSuccess(order, orderGroupId); | actionAfterOrderGroupSuccess(order, orderGroupId); | ||||
| } | } | ||||
| } else { | } else { | ||||
| @@ -233,7 +239,7 @@ public class WxOrderGroupServiceImpl implements WxOrderGroupService { | |||||
| if (orderTemp == null) { | if (orderTemp == null) { | ||||
| return new ResultData(ErrorCode.ORDER_IS_NOT_FIND); | return new ResultData(ErrorCode.ORDER_IS_NOT_FIND); | ||||
| } | } | ||||
| if (orderTemp.getOrderGroupId().equals(notOrderGroupId)) { | |||||
| if (orderTemp.getOrderGroupId().equals(0L)) { | |||||
| return new ResultData(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | return new ResultData(ErrorCode.ORDER_GROUP_COOPERATING_FAILD); | ||||
| } | } | ||||
| WxCouponChannel wxCouponChannel = wxCouponChannelMapper.selectById(orderTemp.getCouponChannelId()); | WxCouponChannel wxCouponChannel = wxCouponChannelMapper.selectById(orderTemp.getCouponChannelId()); | ||||
| @@ -1,5 +1,31 @@ | |||||
| package com.iformall.service.impl; | 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.JSON; | ||||
| import com.alibaba.fastjson.JSONObject; | import com.alibaba.fastjson.JSONObject; | ||||
| import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; | 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.common.ResultData; | ||||
| import com.iformall.domain.dto.OrderSaveDto; | import com.iformall.domain.dto.OrderSaveDto; | ||||
| import com.iformall.domain.dto.WxSharingOrderDto; | 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.base.TenantEntity; | ||||
| import com.iformall.domain.po.msg.FmInsideOrderSuccessMsg; | 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.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.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 | @Service | ||||
| public class WxOrderServiceImpl implements WxOrderService { | 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 | @Override | ||||
| public void stockBack(WxOrder updateOrder) { | public void stockBack(WxOrder updateOrder) { | ||||
| @@ -575,7 +737,7 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| Long couponId = updateOrder.getProductId(); | Long couponId = updateOrder.getProductId(); | ||||
| String couponIdStr = "" + couponId; | String couponIdStr = "" + couponId; | ||||
| //加锁 | //加锁 | ||||
| long time = System.currentTimeMillis() + RedisLock.TIMEOUT; | |||||
| long time = System.currentTimeMillis() + 300*RedisLock.TIMEOUT; | |||||
| String timeStr = String.valueOf(time); | String timeStr = String.valueOf(time); | ||||
| if (!redisLock.lock(couponIdStr, timeStr)) { | if (!redisLock.lock(couponIdStr, timeStr)) { | ||||
| @@ -602,6 +764,7 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| logger.error("此券加库存失败, couponId: " + couponIdStr); | logger.error("此券加库存失败, couponId: " + couponIdStr); | ||||
| throw new MallinkException(ErrorCode.ORDER_IS_FAIL); | throw new MallinkException(ErrorCode.ORDER_IS_FAIL); | ||||
| } | } | ||||
| redisLock.increase(EnumCacheKey.COUPON_STOCK.getMessage()+coupon.getId(), 1); | |||||
| } catch (Exception e) { | } catch (Exception e) { | ||||
| logger.error("数据库更新失败,库存+1失败, e:" + e.getMessage()); | logger.error("数据库更新失败,库存+1失败, e:" + e.getMessage()); | ||||
| throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "库存恢复失败"); | throw new MallinkException(ErrorCode.DB_FAIL.getCode(), "库存恢复失败"); | ||||
| @@ -1717,6 +1880,7 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION); | throw new MallinkException(ErrorCode.SYS_REPEAT_SUBMIT_EXCEPTION); | ||||
| } | } | ||||
| cUserTokenRedisTemplate.opsForValue().set(key, user, 1000, TimeUnit.MILLISECONDS); | cUserTokenRedisTemplate.opsForValue().set(key, user, 1000, TimeUnit.MILLISECONDS); | ||||
| order = saveFreeOrderForCoupon(user, coupon, orderSaveDto.getCouponChannelId(), orderSaveDto.getFormId(), null); | order = saveFreeOrderForCoupon(user, coupon, orderSaveDto.getCouponChannelId(), orderSaveDto.getFormId(), null); | ||||
| if (order != null) { | if (order != null) { | ||||
| // 6. 下订单完成,发送内部消息 | // 6. 下订单完成,发送内部消息 | ||||
| @@ -120,6 +120,7 @@ public class WxPayOrderServiceImpl implements WxPayOrderService { | |||||
| //方法必须为public AopContext.currentProxy() | //方法必须为public AopContext.currentProxy() | ||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| @Override | |||||
| public void handleSuccessOrder(WxPayOrder oldRecord,WxOrder order,Map<String, String> retMap) { | public void handleSuccessOrder(WxPayOrder oldRecord,WxOrder order,Map<String, String> retMap) { | ||||
| Date curDate = new Date(); | Date curDate = new Date(); | ||||
| if (oldRecord.getPayOrderStatus() != EnumPayStatus.PAY_STATUS_SUCCESS.getCode()) { | if (oldRecord.getPayOrderStatus() != EnumPayStatus.PAY_STATUS_SUCCESS.getCode()) { | ||||
| @@ -19,6 +19,7 @@ import com.iformall.pay.WxRefundOrderP; | |||||
| import com.iformall.pay.WxRefundOrderSP; | import com.iformall.pay.WxRefundOrderSP; | ||||
| import com.iformall.service.WxOrderService; | import com.iformall.service.WxOrderService; | ||||
| import com.iformall.service.WxRefundOrderService; | import com.iformall.service.WxRefundOrderService; | ||||
| import com.iformall.service.helper.WxPayOrderServiceHelper; | |||||
| import com.iformall.utils.BeanUtils; | import com.iformall.utils.BeanUtils; | ||||
| import com.iformall.utils.CipherUtil; | import com.iformall.utils.CipherUtil; | ||||
| import com.iformall.utils.Utility; | import com.iformall.utils.Utility; | ||||
| @@ -317,6 +318,36 @@ public class WxRefundOrderServiceImpl implements WxRefundOrderService { | |||||
| logger.error("已经核销过的券: couponOrder-" + couponOrderId); | logger.error("已经核销过的券: couponOrder-" + couponOrderId); | ||||
| throw new MallinkException(ErrorCode.COUPON_ORDER_IS_USED); | throw new MallinkException(ErrorCode.COUPON_ORDER_IS_USED); | ||||
| } | } | ||||
| //TODO 查询订单状态是否是支付成功的状态,支付成功的状态才能退款。 | |||||
| // 查找支付订单 | |||||
| WxPayOrder payOrderQ = new WxPayOrder(); | |||||
| payOrderQ.updateTenantInfo(wxCouponOrder); | |||||
| payOrderQ.setCUserId(wxOrder.getCUserId()); | |||||
| payOrderQ.setOrderId(wxOrder.getId()); | |||||
| payOrderQ.setPayOrderStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode()); | |||||
| WxPayOrder payOrder = null; | |||||
| List<WxPayOrder> list = wxPayOrderMapper.findList(payOrderQ); | |||||
| if (list.size() > 0) { | |||||
| if (list.size() > 1) { | |||||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(),"异常:该订单找到多条支付信息wxpayorder!"); | |||||
| } | |||||
| payOrder = list.get(0); | |||||
| } | |||||
| if (payOrder == null) { | |||||
| logger.error("支付订单不存在(payOrder不存在)"); | |||||
| return new ResultData(ErrorCode.PAY_ORDER_NOT_FOUND); | |||||
| } | |||||
| WxPayAccount payAccount = wxPayAccountMapper.selectById(appInfo.getPayId()); | |||||
| if (null == payAccount) { | |||||
| throw new MallinkException(ErrorCode.PAY_ORDER_QUERY_ERROR.getCode(), "payAccount为空[payId:"+appInfo.getPayId()+"]"); | |||||
| } | |||||
| int status = WxPayOrderServiceHelper.wxOrderPayStatus(payOrder, wxOrder, appInfo, payAccount); | |||||
| if (EnumPayStatus.PAY_STATUS_SUCCESS.getCode() != status) { | |||||
| return new ResultData(ErrorCode.PAY_ORDER_ERROR.getCode(),"该订单查询微信支付状态为未支付成功,无法退款。"); | |||||
| } | |||||
| final IdWorker idWorker = IdWorker.get(); | final IdWorker idWorker = IdWorker.get(); | ||||
| Long refundOrderId = idWorker.nextId(); | Long refundOrderId = idWorker.nextId(); | ||||
| @@ -377,26 +408,7 @@ public class WxRefundOrderServiceImpl implements WxRefundOrderService { | |||||
| } | } | ||||
| } | } | ||||
| // 查找支付订单 | |||||
| WxPayOrder payOrderQ = new WxPayOrder(); | |||||
| payOrderQ.updateTenantInfo(wxCouponOrder); | |||||
| payOrderQ.setCUserId(wxOrder.getCUserId()); | |||||
| payOrderQ.setOrderId(wxOrder.getId()); | |||||
| payOrderQ.setPayOrderStatus(EnumPayStatus.PAY_STATUS_SUCCESS.getCode()); | |||||
| WxPayOrder payOrder = null; | |||||
| List<WxPayOrder> list = wxPayOrderMapper.findList(payOrderQ); | |||||
| if (list.size() > 0) { | |||||
| if (list.size() > 1) { | |||||
| logger.error("TODO: payOrder成功记录应该只有一条????!!!"); | |||||
| } | |||||
| logger.warn(""); | |||||
| payOrder = list.get(0); | |||||
| } | |||||
| if (payOrder == null) { | |||||
| logger.error("支付订单不存在(payOrder不存在)"); | |||||
| return new ResultData(ErrorCode.PAY_ORDER_NOT_FOUND); | |||||
| } | |||||
| WxRefundOrder record = new WxRefundOrder(); | WxRefundOrder record = new WxRefundOrder(); | ||||
| record.setPayOrderNo(String.valueOf(payOrder.getId())); | record.setPayOrderNo(String.valueOf(payOrder.getId())); | ||||
| @@ -428,7 +440,7 @@ public class WxRefundOrderServiceImpl implements WxRefundOrderService { | |||||
| logger.error("退款订单数据库插入出错: " + record.toString()); | logger.error("退款订单数据库插入出错: " + record.toString()); | ||||
| throw new MallinkException(ErrorCode.DB_FAIL); | throw new MallinkException(ErrorCode.DB_FAIL); | ||||
| } | } | ||||
| WxPayAccount payAccount = wxPayAccountMapper.selectById(appInfo.getPayId()); | |||||
| if (StringUtils.isBlank(payAccount.getApiKey())) { | if (StringUtils.isBlank(payAccount.getApiKey())) { | ||||
| logger.error("支付密钥为空"); | logger.error("支付密钥为空"); | ||||
| throw new MallinkException(ErrorCode.API_KEY_NOT_FOUND.getCode(), "支付密钥为空,请联系商城管理员"); | throw new MallinkException(ErrorCode.API_KEY_NOT_FOUND.getCode(), "支付密钥为空,请联系商城管理员"); | ||||
| @@ -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()); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -8,6 +8,8 @@ import org.springframework.data.redis.core.ValueOperations; | |||||
| import org.springframework.stereotype.Component; | import org.springframework.stereotype.Component; | ||||
| import org.springframework.util.StringUtils; | import org.springframework.util.StringUtils; | ||||
| import com.iformall.enums.EnumCacheKey; | |||||
| import java.util.concurrent.TimeUnit; | import java.util.concurrent.TimeUnit; | ||||
| /** | /** | ||||
| @@ -24,7 +26,7 @@ public class RedisLock { | |||||
| private StringRedisTemplate stringRedisTemplate; | private StringRedisTemplate stringRedisTemplate; | ||||
| public static final int TIMEOUT = 10*1000;//超时时间 10s | public static final int TIMEOUT = 10*1000;//超时时间 10s | ||||
| /** | /** | ||||
| * 加锁 | * 加锁 | ||||
| * @param key productId - 商品的唯一标志 | * @param key productId - 商品的唯一标志 | ||||
| @@ -102,5 +104,49 @@ public class RedisLock { | |||||
| logger.error("[Redis分布式锁] 解锁出现异常了,{}",e); | 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); | |||||
| } | |||||
| } | |||||
| } | } | ||||