| @@ -88,17 +88,17 @@ public class WebMvcConfig implements WebMvcConfigurer { | |||||
| .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | .without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) | ||||
| ); | ); | ||||
| //序列化将Long转String类型 | //序列化将Long转String类型 | ||||
| simpleModule.addSerializer(Long.class, ToStringSerializer.instance); | |||||
| simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); | |||||
| SimpleModule bigIntegerModule = new SimpleModule(); | |||||
| // simpleModule.addSerializer(Long.class, ToStringSerializer.instance); | |||||
| // simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); | |||||
| // SimpleModule bigIntegerModule = new SimpleModule(); | |||||
| //序列化将BigInteger转String类型 | //序列化将BigInteger转String类型 | ||||
| bigIntegerModule.addSerializer(BigInteger.class, ToStringSerializer.instance); | |||||
| SimpleModule bigDecimalModule = new SimpleModule(); | |||||
| // bigIntegerModule.addSerializer(BigInteger.class, ToStringSerializer.instance); | |||||
| // SimpleModule bigDecimalModule = new SimpleModule(); | |||||
| //序列化将BigDecimal转String类型 | //序列化将BigDecimal转String类型 | ||||
| bigDecimalModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); | |||||
| // bigDecimalModule.addSerializer(BigDecimal.class, ToStringSerializer.instance); | |||||
| objectMapper.registerModule(simpleModule); | objectMapper.registerModule(simpleModule); | ||||
| objectMapper.registerModule(bigDecimalModule); | |||||
| objectMapper.registerModule(bigIntegerModule); | |||||
| // objectMapper.registerModule(bigDecimalModule); | |||||
| // objectMapper.registerModule(bigIntegerModule); | |||||
| jackson2HttpMessageConverter.setObjectMapper(objectMapper); | jackson2HttpMessageConverter.setObjectMapper(objectMapper); | ||||
| converters.add(jackson2HttpMessageConverter); | converters.add(jackson2HttpMessageConverter); | ||||
| } | } | ||||
| @@ -1,11 +1,17 @@ | |||||
| package com.iformall; | package com.iformall; | ||||
| import com.iformall.domain.po.msg.FmInsideCouponVerifyMsg; | |||||
| import com.iformall.enums.EnumMsgRecordType; | |||||
| import com.iformall.service.msg.impl.FmInsideCouponVerifyMsgServiceImpl; | |||||
| import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; | import com.ulisesbocchio.jasyptspringboot.annotation.EnableEncryptableProperties; | ||||
| import org.mybatis.spring.annotation.MapperScan; | import org.mybatis.spring.annotation.MapperScan; | ||||
| import org.rocketmq.starter.annotation.EnableRocketMQ; | import org.rocketmq.starter.annotation.EnableRocketMQ; | ||||
| import org.springframework.beans.BeansException; | |||||
| import org.springframework.beans.factory.annotation.Autowired; | |||||
| import org.springframework.beans.factory.annotation.Value; | import org.springframework.beans.factory.annotation.Value; | ||||
| import org.springframework.boot.SpringApplication; | import org.springframework.boot.SpringApplication; | ||||
| import org.springframework.boot.autoconfigure.SpringBootApplication; | import org.springframework.boot.autoconfigure.SpringBootApplication; | ||||
| import org.springframework.context.ConfigurableApplicationContext; | |||||
| import org.springframework.context.annotation.Bean; | import org.springframework.context.annotation.Bean; | ||||
| import springfox.documentation.swagger2.annotations.EnableSwagger2; | import springfox.documentation.swagger2.annotations.EnableSwagger2; | ||||
| @@ -52,8 +58,23 @@ public class ConsumerApplication { | |||||
| return uploadDir; | return uploadDir; | ||||
| } | } | ||||
| public static void main(String[] args) { | public static void main(String[] args) { | ||||
| SpringApplication.run(ConsumerApplication.class, args); | |||||
| ConfigurableApplicationContext context = SpringApplication.run(ConsumerApplication.class, args); | |||||
| // FmInsideCouponVerifyMsg verifyMsg = new FmInsideCouponVerifyMsg(); | |||||
| // verifyMsg.setMsgType(EnumMsgRecordType.INSIDE_COUPON_VERIFY.getCode()); | |||||
| // //verifyMsg.updateTenantInfo(tenantEntity); | |||||
| // verifyMsg.setTenantId("789"); | |||||
| // verifyMsg.setCouponOrderId(436060427278385152L); | |||||
| // verifyMsg.setBUserId(435982884998905856L); | |||||
| // try { | |||||
| // context.getBean(FmInsideCouponVerifyMsgServiceImpl.class).send(verifyMsg); | |||||
| // } catch (BeansException e) { | |||||
| // // TODO Auto-generated catch block | |||||
| // e.printStackTrace(); | |||||
| // } catch (Exception e) { | |||||
| // // TODO Auto-generated catch block | |||||
| // e.printStackTrace(); | |||||
| // } | |||||
| } | } | ||||
| } | } | ||||
| @@ -86,8 +86,8 @@ aws: | |||||
| wechat: | wechat: | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentAppId: "wxdfc8fb4e62d6b52b" | |||||
| componentSecret: "98daa62b316dd6feabaad708327ce233" | |||||
| componentToken: "formall2018" | componentToken: "formall2018" | ||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | ||||
| redis: | redis: | ||||
| @@ -4,11 +4,7 @@ spring: | |||||
| #include: rabbitMQ | #include: rabbitMQ | ||||
| # JDBC | # JDBC | ||||
| datasource: | datasource: | ||||
| <<<<<<< HEAD | |||||
| url: jdbc:mysql://zc349w82qvn56ftl5e64-rw4rm.rwlb.rds.aliyuncs.com:3306/mallink?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| ======= | |||||
| url: jdbc:mysql://202.165.179.86:3306/mallinkDevGroup1?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| >>>>>>> develop | |||||
| url: jdbc:mysql://zc349w82qvn56ftl5e64-rw4rm.rwlb.rds.aliyuncs.com:3306/mallinkDevGroup1?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| username: ENC(dZ8fmrtuBMQYaRytKQgTqg==) | username: ENC(dZ8fmrtuBMQYaRytKQgTqg==) | ||||
| password: ENC(IKH7HxMZwIqMttMc9+QsqWa1KMsJvTs4) | password: ENC(IKH7HxMZwIqMttMc9+QsqWa1KMsJvTs4) | ||||
| type: com.alibaba.druid.pool.DruidDataSource | type: com.alibaba.druid.pool.DruidDataSource | ||||
| @@ -94,8 +90,8 @@ jasypt: | |||||
| wechat: | wechat: | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentAppId: "wxdfc8fb4e62d6b52b" | |||||
| componentSecret: "98daa62b316dd6feabaad708327ce233" | |||||
| componentToken: "formall2018" | componentToken: "formall2018" | ||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | ||||
| redis: | redis: | ||||
| @@ -67,22 +67,22 @@ public class CouponOrderExpiringSchedule { | |||||
| } | } | ||||
| @Scheduled(cron = "0 0/2 * * * ?") // 每天凌晨00:05 | |||||
| @Scheduled(cron = "0 5 0 * * ?") // 每天凌晨00:05 | |||||
| // @Scheduled(cron = "*/10 * * * * ?") // 测试10秒中一次 | // @Scheduled(cron = "*/10 * * * * ?") // 测试10秒中一次 | ||||
| public void couponOrderRefundSchedule() { | public void couponOrderRefundSchedule() { | ||||
| List<WxCouponOrder> list = wxCouponOrderMapper.findExpiredCouponOrderByValidDate(); | List<WxCouponOrder> list = wxCouponOrderMapper.findExpiredCouponOrderByValidDate(); | ||||
| CouponOrderExpiringSchedule proxy = (CouponOrderExpiringSchedule) AopContext.currentProxy(); | CouponOrderExpiringSchedule proxy = (CouponOrderExpiringSchedule) AopContext.currentProxy(); | ||||
| list.stream().forEach(co -> { | list.stream().forEach(co -> { | ||||
| refund(co,proxy); | |||||
| proxy.refund(co); | |||||
| }); | }); | ||||
| } | } | ||||
| @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = {Exception.class}) | ||||
| public void refund(WxCouponOrder order,CouponOrderExpiringSchedule proxy) { | |||||
| public void refund(WxCouponOrder order) { | |||||
| // 券过期,更改状态及库存退回 | // 券过期,更改状态及库存退回 | ||||
| proxy.couponOrderExpire(order, false); | |||||
| couponOrderExpire(order, false); | |||||
| // 退款 | // 退款 | ||||
| proxy.couponOrderExpireRefund(order, true); | |||||
| couponOrderExpireRefund(order, true); | |||||
| } | } | ||||
| @@ -86,8 +86,8 @@ aws: | |||||
| wechat: | wechat: | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentAppId: "wxdfc8fb4e62d6b52b" | |||||
| componentSecret: "98daa62b316dd6feabaad708327ce233" | |||||
| componentToken: "formall2018" | componentToken: "formall2018" | ||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | ||||
| redis: | redis: | ||||
| @@ -580,6 +580,33 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| * @param couponIdStr | * @param couponIdStr | ||||
| */ | */ | ||||
| private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) { | private void stockReduce(WxCUser user, WxCoupon coupon, String couponIdStr) { | ||||
| //如果缓存中不存在这个key,则从coupon中取 | |||||
| if(!redisLock.hasCouponStockCache(coupon.getId())) { | |||||
| //此处需要加锁,防止并发设置 | |||||
| long time = System.currentTimeMillis() + RedisLock.TIMEOUT; | |||||
| String timeStr = String.valueOf(time); | |||||
| boolean stocksetlock = redisLock.lock("couponLockStockSet_"+coupon.getId(), timeStr); | |||||
| if (stocksetlock) { | |||||
| if (!redisLock.hasCouponStockCache(coupon.getId())) { | |||||
| WxCoupon cou = wxCouponMapper.selectById(coupon.getId()); | |||||
| if (null == cou) { | |||||
| redisLock.unlock("couponLockStockSet_"+coupon.getId(), timeStr); | |||||
| throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"未查询到券!"+coupon.getId()); | |||||
| } | |||||
| try { | |||||
| redisLock.setCouponStock(coupon.getId(), cou.getRemainInventory()); | |||||
| }catch(Exception e) { | |||||
| logger.error("set couponstock to redis fail.",e); | |||||
| throw new MallinkException(ErrorCode.ORDER_IS_FAIL.getCode(),"redis设置券库存失败"+coupon.getId()); | |||||
| }finally { | |||||
| redisLock.unlock("couponLockStockSet_"+coupon.getId(), timeStr); | |||||
| } | |||||
| }else { | |||||
| redisLock.unlock("couponLockStockSet_"+coupon.getId(), timeStr); | |||||
| } | |||||
| } | |||||
| } | |||||
| long redisStock = redisLock.getCouponStock(Long.parseLong(couponIdStr)); | long redisStock = redisLock.getCouponStock(Long.parseLong(couponIdStr)); | ||||
| if (redisStock<=0) { | if (redisStock<=0) { | ||||
| @@ -1224,10 +1251,18 @@ public class WxOrderServiceImpl implements WxOrderService { | |||||
| if (Objects.nonNull(wxCouponSendVo) && Objects.equals(EnumCouponSendSendType.MERCHANT.getCode(), wxCouponSendVo.getSendType())) { | if (Objects.nonNull(wxCouponSendVo) && Objects.equals(EnumCouponSendSendType.MERCHANT.getCode(), wxCouponSendVo.getSendType())) { | ||||
| // 商户注券减库存操作 | // 商户注券减库存操作 | ||||
| stockMerchantReduce(user, coupon, couponIdStr, wxCouponSendVo); | |||||
| try { | |||||
| stockMerchantReduce(user, coupon, couponIdStr, wxCouponSendVo); | |||||
| }catch(Exception e) { | |||||
| throw new MallinkException(ErrorCode.COUPON_STOCK_ERR.getCode(), e.getMessage()); | |||||
| } | |||||
| } else { | } else { | ||||
| // 减库存操作 | // 减库存操作 | ||||
| stockReduce(user, coupon, couponIdStr); | |||||
| try { | |||||
| stockReduce(user, coupon, couponIdStr); | |||||
| }catch(Exception e) { | |||||
| throw new MallinkException(ErrorCode.COUPON_STOCK_ERR.getCode(), e.getMessage()); | |||||
| } | |||||
| } | } | ||||
| int payment = coupon.getSalePrice(); | int payment = coupon.getSalePrice(); | ||||
| @@ -112,43 +112,24 @@ public class RedisLock { | |||||
| * 原子性增加 | * 原子性增加 | ||||
| */ | */ | ||||
| public long increase(String key, long delta) { | 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; | |||||
| return stringRedisTemplate.opsForValue().increment(key, delta); | |||||
| } | } | ||||
| /** | /** | ||||
| * 原子性减少 | * 原子性减少 | ||||
| */ | */ | ||||
| public long decrease(String key, long delta) { | public long decrease(String key, long delta) { | ||||
| try { | |||||
| return stringRedisTemplate.opsForValue().decrement(key, delta); | |||||
| }catch(Exception e) { | |||||
| logger.error("[Redis分布式锁] decrease error,{}",e); | |||||
| } | |||||
| return 0; | |||||
| return stringRedisTemplate.opsForValue().decrement(key, delta); | |||||
| } | } | ||||
| public long getCouponStock(long couponId) { | 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; | |||||
| String longstr = stringRedisTemplate.opsForValue().get(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId)); | |||||
| return Long.parseLong(longstr); | |||||
| } | } | ||||
| public void setCouponStock(long couponId,long number) { | 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); | |||||
| } | |||||
| stringRedisTemplate.opsForValue().set(EnumCacheKey.COUPON_STOCK.getMessage()+String.valueOf(couponId), String.valueOf(number)); | |||||
| } | } | ||||
| public boolean hasCouponStockCache(long couponId) { | public boolean hasCouponStockCache(long couponId) { | ||||
| @@ -3,7 +3,7 @@ spring: | |||||
| include: rabbitMQ | include: rabbitMQ | ||||
| # JDBC | # JDBC | ||||
| datasource: | datasource: | ||||
| url: jdbc:mysql://rm-2zel9i9t555zy7lft.mysql.rds.aliyuncs.com:3306/mallink?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| url: jdbc:mysql://zc349w82qvn56ftl5e64-rw4rm.rwlb.rds.aliyuncs.com:3306/mallink?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useUnicode=true&useSSL=false&useAffectedRows=true | |||||
| username: ENC(dZ8fmrtuBMQYaRytKQgTqg==) | username: ENC(dZ8fmrtuBMQYaRytKQgTqg==) | ||||
| password: ENC(IKH7HxMZwIqMttMc9+QsqWa1KMsJvTs4) | password: ENC(IKH7HxMZwIqMttMc9+QsqWa1KMsJvTs4) | ||||
| type: com.alibaba.druid.pool.DruidDataSource | type: com.alibaba.druid.pool.DruidDataSource | ||||
| @@ -87,12 +87,12 @@ wechat: | |||||
| secret: "c689fabf3c4c9f5b6424ff2a36a26727" | secret: "c689fabf3c4c9f5b6424ff2a36a26727" | ||||
| url: "https://mall.youlane.cn" | url: "https://mall.youlane.cn" | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentAppId: "wxdfc8fb4e62d6b52b" | |||||
| componentSecret: "98daa62b316dd6feabaad708327ce233" | |||||
| componentToken: "formall2018" | componentToken: "formall2018" | ||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | ||||
| redis: | redis: | ||||
| host: 202.165.179.86 | |||||
| host: 101.201.103.81 | |||||
| port: 6379 | port: 6379 | ||||
| password: ENC(xPFDlgd6v8zUhJMbcKc0KmmQiqs9qHtk) | password: ENC(xPFDlgd6v8zUhJMbcKc0KmmQiqs9qHtk) | ||||
| timeout: 3600 | timeout: 3600 | ||||
| @@ -86,8 +86,8 @@ aws: | |||||
| wechat: | wechat: | ||||
| open: | open: | ||||
| componentAppId: "wx897e4673286c915d" | |||||
| componentSecret: "cdfdfda65c45689beb6766c4c427eed2" | |||||
| componentAppId: "wxdfc8fb4e62d6b52b" | |||||
| componentSecret: "98daa62b316dd6feabaad708327ce233" | |||||
| componentToken: "formall2018" | componentToken: "formall2018" | ||||
| componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | componentAesKey: "htKq8EjBMPNndfZQK9JiFojFaqFwFpw42VfeWFtx7HN" | ||||
| redis: | redis: | ||||