From 1a34ca17ad994d5b79ccdd7eecd6c7eca96c242f Mon Sep 17 00:00:00 2001 From: wuxicheng <1441859745@qq.com> Date: Thu, 30 Mar 2023 10:59:53 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=A2=E5=8D=95=E4=B8=9A=E5=8A=A1=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bnyer-common/bnyer-common-core/pom.xml | 5 + .../bnyer/common/core/dto/VipOrderDto.java | 34 +++ .../bnyer/common/core/enums/ResponseEnum.java | 50 +++++ .../core/utils/bean/EntityConvertUtil.java | 203 ++++++++++++++++++ .../com/bnyer/common/core/vo/VipOrderVo.java | 42 ++++ .../common/core/web/domain/AjaxResult.java | 10 + .../common/redis/adapter/CacheTtlAdapter.java | 20 ++ .../common/redis/bo/CacheNameWithTtlBo.java | 28 +++ .../common/redis/configure/RedisConfig.java | 143 ++++++++++-- .../common/redis/constant/CacheNames.java | 20 ++ .../redis/constant/OrderCacheNames.java | 25 +++ .../redis/service/RedisCacheService.java | 49 +++++ .../common/redis/service/RedisService.java | 29 ++- .../main/resources/META-INF/spring.factories | 3 +- bnyer-common/bnyer-common-rocketmq/pom.xml | 32 +++ .../rocketmq/config/RocketMqAdapter.java | 38 ++++ .../rocketmq/config/RocketMqConstant.java | 40 ++++ bnyer-common/pom.xml | 1 + .../img/constants/UserVipTypeConstant.java | 24 --- .../impl/UserVipServiceRecordImpl.java | 22 +- bnyer-services/bnyer-order/pom.xml | 7 +- .../bnyer/order/BnyerOrderApplication.java | 2 +- .../order/config/OrderCacheTtlAdapter.java | 27 +++ .../bnyer/order/config/RocketMqConfig.java | 28 +++ .../order/controller/VipOrderController.java | 78 +++++++ .../order/controller/testController.java | 4 - .../com/bnyer/order/enums/EnumSceneCode.java | 30 +++ .../com/bnyer/order/enums/EnumVipType.java | 32 +++ .../listener/vip/VipOrderCancelConsumer.java | 24 +++ .../vip/VipOrderPaySuccessConsumer.java | 18 ++ .../com/bnyer/order/service/TestService.java | 4 - .../bnyer/order/service/VipOrderService.java | 27 +++ .../order/service/impl/TestServiceImpl.java | 8 - .../service/impl/VipOrderServiceImpl.java | 97 +++++++++ bnyer-services/bnyer-pay/pom.xml | 6 + .../com/bnyer/pay/BnyerPayApplication.java | 2 +- .../com/bnyer/pay/config/RocketMqConfig.java | 28 +++ .../java/com/bnyer/pay/config/TestConfig.java | 4 - pom.xml | 13 ++ 39 files changed, 1168 insertions(+), 89 deletions(-) create mode 100644 bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/VipOrderDto.java create mode 100644 bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java create mode 100644 bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java create mode 100644 bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/vo/VipOrderVo.java create mode 100644 bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java create mode 100644 bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java create mode 100644 bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java create mode 100644 bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java create mode 100644 bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java create mode 100644 bnyer-common/bnyer-common-rocketmq/pom.xml create mode 100644 bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java create mode 100644 bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java delete mode 100644 bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java delete mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumSceneCode.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPaySuccessConsumer.java delete mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java delete mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java create mode 100644 bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java create mode 100644 bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java delete mode 100644 bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java diff --git a/bnyer-common/bnyer-common-core/pom.xml b/bnyer-common/bnyer-common-core/pom.xml index e522210..f475dbf 100644 --- a/bnyer-common/bnyer-common-core/pom.xml +++ b/bnyer-common/bnyer-common-core/pom.xml @@ -65,6 +65,11 @@ jackson-databind + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + com.alibaba diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/VipOrderDto.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/VipOrderDto.java new file mode 100644 index 0000000..1ad7cbc --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/dto/VipOrderDto.java @@ -0,0 +1,34 @@ +package com.bnyer.common.core.dto; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class VipOrderDto implements Serializable { + + @NotBlank(message = "手机号不能为空!") + @ApiModelProperty(value="手机号") + private String phone; + + @NotNull(message = "用户vipId不能为空!") + @ApiModelProperty(value="用户vip表id") + private Long userVipId; + + @NotBlank(message = "vip类型状态不能为空!") + @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") + private String type; + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java new file mode 100644 index 0000000..44f80ae --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java @@ -0,0 +1,50 @@ +package com.bnyer.common.core.enums; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +public enum ResponseEnum { + + //======================订单异常======================== + + /** + * 订单已过期,当前端看到该状态码的时候,提示订单信息已过期,请重新确认后提交,此时用户点击确定,前端刷新页面。 + */ + ORDER_EXPIRED(110001, "订单已过期"), + + /** + * 请勿重复提交订单, + * 1.当前端遇到该异常时,说明前端防多次点击没做好 + * 2.提示用户 订单已发生改变,请勿重复下单 + */ + REPEAT_ORDER(110002,"请勿重复提交订单"), + + ; + + private final int code; + + private final String msg; + + public int value() { + return code; + } + + public String getMsg() { + return msg; + } + + ResponseEnum(int code, String msg) { + this.code = code; + this.msg = msg; + } + + @Override + public String toString() { + return "ResponseEnum{" + + "code=" + code + + ", msg='" + msg + '\'' + + '}'; + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java new file mode 100644 index 0000000..6184b37 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/bean/EntityConvertUtil.java @@ -0,0 +1,203 @@ +package com.bnyer.common.core.utils.bean; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.util.CollectionUtils; + +import java.beans.BeanInfo; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.*; +import java.util.*; + +@Slf4j +public class EntityConvertUtil { + + /** + * 数组集合转化为指定对象集合 + * 指定的实体对象必须包含所以字段的构造方法,数组的元素的顺序将和构造方法顺序和类型一一对应 + * + * @param list 集合 + * @param clazz c + * @param 类型 + * @return List + * @description 用于jpa查询自定义vo用的 + */ + public static List castEntity(List list, Class clazz) { + List returnList = new ArrayList<>(); + if (list.size() == 0) { + return returnList; + } + Class[] c2 = null; + Constructor[] constructors = clazz.getConstructors(); + for (Constructor constructor : constructors) { + Class[] tClass = constructor.getParameterTypes(); + if (tClass.length == list.get(0).length) { + c2 = tClass; + break; + } + } + //构造方法实例化对象 + for (Object[] o : list) { + Constructor constructor = null; + try { + constructor = clazz.getConstructor(c2); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + try { + assert constructor != null; + returnList.add(constructor.newInstance(o)); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + return returnList; + } + + + /** + * @param object 要强转的对象 , entityClass 强转后的类型 + */ + public static T convertBean(Object object, Class entityClass) { + if (null == object) { + return null; + } + return JSON.parseObject(JSON.toJSONString(object), entityClass); + } + + + /** + * @param object 要转话的对象 + */ + public static Map objectToMap(Object object) { + return convertBean(object, Map.class); + } + + + /** + * @param map map集合, t 对象 + */ + public static T mapToObject(Map map, Class beanClass) throws InstantiationException, IllegalAccessException, InvocationTargetException { + T t = beanClass.newInstance(); + Field[] declaredFields = t.getClass().getDeclaredFields(); + for(Field field:declaredFields){ + int mod = field.getModifiers(); + if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){ + continue; + } + field.setAccessible(true); + field.set(t, map.get(field.getName())); + } + return t; + } + + + /** + * @param source 资源对象, target 目标对象, ignoreProperties 赋值new String[]{} + * @return T target对象 + */ + public static T copy(Object source, Class target, String... ignoreProperties) { + T targetInstance = null; + try { + targetInstance = target.newInstance(); + } catch (Exception e) { + e.printStackTrace(); + } + if (ArrayUtils.isEmpty(ignoreProperties)) { + assert targetInstance != null; + BeanUtils.copyProperties(source, targetInstance); + } else { + assert targetInstance != null; + BeanUtils.copyProperties(source, targetInstance, ignoreProperties); + } + return targetInstance; + + } + + /** + * 从List copy到List + * @param list List + * @param clazz B + * @return List + */ + public static List copy(List list,Class clazz){ + String oldOb = JSON.toJSONString(list); + return JSON.parseArray(oldOb, clazz); + } + + public static List copyList(List list, Class target, String... ignoreProperties) { + List targetList = new ArrayList<>(); + if (CollectionUtils.isEmpty(list)) { + return targetList; + } + for (E e : list) { + targetList.add(copy(e, target, ignoreProperties)); + } + return targetList; + } + + + /** + * 功能描述: Bean --> Map 1: 利用Introspector和PropertyDescriptor 将Bean --> Map + * + */ + public static Map transBean2Map(Object obj) { + if (obj == null) { + return null; + } + Map map = new HashMap(); + try { + BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass()); + PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors(); + for (PropertyDescriptor property : propertyDescriptors) { + String key = property.getName(); + // 过滤class属性 + if (!"class".equals(key)) { + // 得到property对应的getter方法 + Method getter = property.getReadMethod(); + Object value = getter.invoke(obj); + map.put(key, value.toString()); + } + } + } catch (Exception e) { + log.error("transBean2Map Error " + e); + } + return map; + } + + /** + * 属性复制,跳过为null的值 + * @param source 提供值的obj + * @param target 接收值的obj + * @param + * @return target + */ + public static T copyNotNullProperties(T source, T target){ + BeanUtils.copyProperties(source, target, getNullPropertyNames(source)); + return target; + } + + /** + * 获取值为Null的字段名称 + * @param source + * @return + */ + public static String[] getNullPropertyNames (Object source) { + final BeanWrapper src = new BeanWrapperImpl(source); + java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors(); + + Set emptyNames = new HashSet<>(); + for(java.beans.PropertyDescriptor pd : pds) { + Object srcValue = src.getPropertyValue(pd.getName()); + if (srcValue == null) emptyNames.add(pd.getName()); + } + String[] result = new String[emptyNames.size()]; + return emptyNames.toArray(result); + } +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/vo/VipOrderVo.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/vo/VipOrderVo.java new file mode 100644 index 0000000..9036dd6 --- /dev/null +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/vo/VipOrderVo.java @@ -0,0 +1,42 @@ +package com.bnyer.common.core.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +@Getter +@Setter +@NoArgsConstructor +public class VipOrderVo { + + @ApiModelProperty(value="手机号") + private String phone; + + @ApiModelProperty(value="用户vip表id") + private Long userVipId; + + @ApiModelProperty(value="vip类型状态(0->月卡;1->季卡;2->年卡)") + private String type; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="开始时间") + private Date startTime; + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @ApiModelProperty(value="到期时间") + private Date endTime; + + @ApiModelProperty(value="总金额") + private BigDecimal totalAmount; + +} diff --git a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java index 990ea28..633928c 100644 --- a/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java +++ b/bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/web/domain/AjaxResult.java @@ -2,6 +2,7 @@ package com.bnyer.common.core.web.domain; import java.util.HashMap; import com.bnyer.common.core.constant.HttpStatus; +import com.bnyer.common.core.enums.ResponseEnum; import com.bnyer.common.core.utils.StringUtils; /** @@ -159,4 +160,13 @@ public class AjaxResult extends HashMap { return new AjaxResult(code, msg, null); } + + /** + * 返回错误消息 + * @param responseEnum + * @return + */ + public static AjaxResult error(ResponseEnum responseEnum) { + return new AjaxResult(responseEnum.value(), responseEnum.getMsg(), null); + } } diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java new file mode 100644 index 0000000..7997513 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/adapter/CacheTtlAdapter.java @@ -0,0 +1,20 @@ +package com.bnyer.common.redis.adapter; + +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; + +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : 实现该接口之后,根据缓存的cacheName和ttl将缓存进行过期 + */ +public interface CacheTtlAdapter { + + /** + * 根据缓存的cacheName和ttl将缓存进行过期 + * @return 需要独立设置过期时间的缓存列表 + */ + List listCacheNameWithTtl(); + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java new file mode 100644 index 0000000..6c29cb2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/bo/CacheNameWithTtlBo.java @@ -0,0 +1,28 @@ +package com.bnyer.common.redis.bo; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : 通过 cacheName 配置 和 时间告诉缓存多久清楚一遍 + */ +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class CacheNameWithTtlBo { + + /** + * 缓存名称 + */ + private String cacheName; + + /** + * 过期时间 + */ + private Integer ttl; +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java index 17ef8ed..eafe6e2 100644 --- a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/configure/RedisConfig.java @@ -1,50 +1,149 @@ package com.bnyer.common.redis.configure; +import com.bnyer.common.redis.adapter.CacheTtlAdapter; +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.PropertyAccessor; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; + +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; /** * redis配置 - * + * * @author ruoyi */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { + @Bean - @SuppressWarnings(value = { "unchecked", "rawtypes" }) - public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) - { - RedisTemplate template = new RedisTemplate<>(); - template.setConnectionFactory(connectionFactory); + public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheTtlAdapter cacheTtlAdapter) { - FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + RedisCacheManager redisCacheManager = new RedisCacheManager( + RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), + // 默认策略,未配置的 key 会使用这个 + this.getRedisCacheConfigurationWithTtl(3600), + // 指定 key 策略 + this.getRedisCacheConfigurationMap(cacheTtlAdapter)); - ObjectMapper mapper = new ObjectMapper(); - mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); - mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); - serializer.setObjectMapper(mapper); + redisCacheManager.setTransactionAware(true); + return redisCacheManager; + } - // 使用StringRedisSerializer来序列化和反序列化redis的key值 - template.setKeySerializer(new StringRedisSerializer()); - template.setValueSerializer(serializer); + private Map getRedisCacheConfigurationMap(CacheTtlAdapter cacheTtlAdapter) { + if (cacheTtlAdapter == null) { + return Collections.emptyMap(); + } + Map redisCacheConfigurationMap = new HashMap<>(16); - // Hash的key也采用StringRedisSerializer的序列化方式 - template.setHashKeySerializer(new StringRedisSerializer()); - template.setHashValueSerializer(serializer); + for (CacheNameWithTtlBo cacheNameWithTtlBO : cacheTtlAdapter.listCacheNameWithTtl()) { + redisCacheConfigurationMap.put(cacheNameWithTtlBO.getCacheName(), + getRedisCacheConfigurationWithTtl(cacheNameWithTtlBO.getTtl())); + } + return redisCacheConfigurationMap; + } + private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) { + RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); + redisCacheConfiguration = redisCacheConfiguration + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())) + .entryTtl(Duration.ofSeconds(seconds)); + + return redisCacheConfiguration; + } + + /** + * 自定义redis序列化的机制,重新定义一个ObjectMapper.防止和MVC的冲突 + */ + @Bean + public RedisSerializer redisSerializer() { + ObjectMapper objectMapper = new ObjectMapper(); +// // 反序列化时候遇到不匹配的属性并不抛出异常 +// objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); +// // 序列化时候遇到空对象不抛出异常 +// objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); +// // 反序列化的时候如果是无效子类型,不抛出异常 +// objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false); +// // 不使用默认的dateTime进行序列化, +// objectMapper.configure(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false); +// // 使用JSR310提供的序列化类,里面包含了大量的JDK8时间序列化类 +// objectMapper.registerModule(new JavaTimeModule()); + // 启用反序列化所需的类型信息,在属性中添加@class + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, + JsonTypeInfo.As.PROPERTY); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + // 配置null值的序列化器 + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + serializer.setObjectMapper(objectMapper); + return serializer; + } + +// @Bean +// @SuppressWarnings(value = { "unchecked", "rawtypes" }) +// public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) +// { +// RedisTemplate template = new RedisTemplate<>(); +// template.setConnectionFactory(connectionFactory); +// +// FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); +// +// ObjectMapper mapper = new ObjectMapper(); +// mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); +// mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); +// serializer.setObjectMapper(mapper); +// +// // 使用StringRedisSerializer来序列化和反序列化redis的key值 +// template.setKeySerializer(new StringRedisSerializer()); +// template.setValueSerializer(serializer); +// +// // Hash的key也采用StringRedisSerializer的序列化方式 +// template.setHashKeySerializer(new StringRedisSerializer()); +// template.setHashValueSerializer(serializer); +// +// template.afterPropertiesSet(); +// return template; +// } + + @Bean + public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory, + RedisSerializer redisSerializer) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(redisConnectionFactory); + template.setDefaultSerializer(redisSerializer); + template.setValueSerializer(redisSerializer); + template.setHashValueSerializer(redisSerializer); + template.setKeySerializer(StringRedisSerializer.UTF_8); + template.setHashKeySerializer(StringRedisSerializer.UTF_8); template.afterPropertiesSet(); return template; } + + + @Bean + @ConditionalOnMissingBean + public CacheTtlAdapter cacheTtl() { + return Collections::emptyList; + } + } diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java new file mode 100644 index 0000000..c3051a2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/CacheNames.java @@ -0,0 +1,20 @@ +package com.bnyer.common.redis.constant; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +public class CacheNames { + /** + * + * 参考CacheKeyPrefix + * cacheNames 与 key 之间的默认连接字符 + */ + public final static String UNION = "::"; + + /** + * key内部的连接字符(自定义) + */ + public final static String UNION_KEY = ":"; +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java new file mode 100644 index 0000000..b601dd6 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/constant/OrderCacheNames.java @@ -0,0 +1,25 @@ +package com.bnyer.common.redis.constant; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +public class OrderCacheNames { + + /** + * 会员订单前缀 + */ + public static final String VIP_ORDER_PREFIX = "vip_cloud_order:"; + + /** + * 确认订单信息缓存 + */ + public static final String ORDER_CONFIRM_KEY = "order:confirm"; + + /** + * 订单uid + */ + public static final String ORDER_CONFIRM_UID_KEY = "order:uid_confirm"; + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java new file mode 100644 index 0000000..4ce0fa2 --- /dev/null +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisCacheService.java @@ -0,0 +1,49 @@ +package com.bnyer.common.redis.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Component; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Component +public class RedisCacheService { + private final CacheManager cacheManager; + + @Autowired + public RedisCacheService(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @SuppressWarnings({ "unchecked" }) + public T getCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) { + return null; + } + Cache.ValueWrapper valueWrapper = cache.get(key); + if (valueWrapper == null) { + return null; + } + return (T) valueWrapper.get(); + } + + public void putCache(String cacheName, String key, Object value) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.put(key, value); + } + } + + public void evictCache(String cacheName, String key) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.evict(key); + } + } + +} diff --git a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java index 9e47907..ab0dff5 100644 --- a/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java +++ b/bnyer-common/bnyer-common-redis/src/main/java/com/bnyer/common/redis/service/RedisService.java @@ -1,17 +1,15 @@ package com.bnyer.common.redis.service; -import java.time.Duration; -import java.util.*; -import java.util.concurrent.TimeUnit; - -import org.apache.poi.ss.formula.functions.T; +import cn.hutool.core.util.StrUtil; +import com.bnyer.common.core.exception.ServiceException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.core.*; import org.springframework.data.redis.core.script.DefaultRedisScript; -import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; +import java.util.*; +import java.util.concurrent.TimeUnit; + /** * spring redis 工具类 * @@ -353,4 +351,21 @@ public class RedisService return redisTemplate.opsForHyperLogLog().add(today + page+"UV", ip); } + + public boolean cad(String key, String value) { + + if (key.contains(StrUtil.SPACE) || value.contains(StrUtil.SPACE)) { + throw new ServiceException("系统繁忙,请稍候重试!"); + } + + String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; + + //通过lure脚本原子验证令牌和删除令牌 + Long result = (Long) redisTemplate.execute(new DefaultRedisScript(script, Long.class), + Collections.singletonList(key), + value); + + return !Objects.equals(result, 0L); + } + } diff --git a/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories b/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories index 6dfffb0..d180a1f 100644 --- a/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories +++ b/bnyer-common/bnyer-common-redis/src/main/resources/META-INF/spring.factories @@ -2,6 +2,7 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.bnyer.common.redis.configure.RedisConfig,\ com.bnyer.common.redis.service.RedisService,\ com.bnyer.common.redis.configure.RedissonConfig,\ - com.bnyer.common.redis.service.RedissonService + com.bnyer.common.redis.service.RedissonService,\ + com.bnyer.common.redis.service.RedisCacheService diff --git a/bnyer-common/bnyer-common-rocketmq/pom.xml b/bnyer-common/bnyer-common-rocketmq/pom.xml new file mode 100644 index 0000000..2196bce --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/pom.xml @@ -0,0 +1,32 @@ + + + + com.dimensionalnode + bnyer-common + 1.0.0 + + 4.0.0 + + + bnyer-common-rocketmq消息队列模块 + + + bnyer-common-rocketmq + + + + + com.dimensionalnode + bnyer-common-core + + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + + + diff --git a/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java new file mode 100644 index 0000000..2fd082b --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqAdapter.java @@ -0,0 +1,38 @@ +package com.bnyer.common.rocketmq.config; + +import org.apache.rocketmq.client.producer.DefaultMQProducer; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.apache.rocketmq.spring.support.RocketMQMessageConverter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Configuration; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqAdapter { + + @Autowired + private RocketMQMessageConverter rocketMqMessageConverter; + + @Value("${rocketmq.name-server:}") + private String nameServer; + + public RocketMQTemplate getTemplateByTopicName(String topic){ + RocketMQTemplate mqTemplate = new RocketMQTemplate(); + DefaultMQProducer producer = new DefaultMQProducer(topic); + producer.setNamesrvAddr(nameServer); + producer.setRetryTimesWhenSendFailed(RocketMqConstant.RETRY_FAILED_COUNT); + producer.setRetryTimesWhenSendAsyncFailed(RocketMqConstant.ASYNC_RETRY_FAILED_COUNT); + producer.setSendMsgTimeout((int) RocketMqConstant.TIMEOUT); + mqTemplate.setProducer(producer); + mqTemplate.setMessageConverter(rocketMqMessageConverter.getMessageConverter()); + return mqTemplate; + } + +} diff --git a/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java new file mode 100644 index 0000000..6ca663d --- /dev/null +++ b/bnyer-common/bnyer-common-rocketmq/src/main/java/com/bnyer/common/rocketmq/config/RocketMqConstant.java @@ -0,0 +1,40 @@ +package com.bnyer.common.rocketmq.config; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +public class RocketMqConstant { + /** + * 默认发送消息超时时间 + */ + public static final long TIMEOUT = 3000; + + /** + * 延迟队列取消订单时间,实际上30分钟 + * 按顺序匹配:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h (1-18) + */ + public static final int CANCEL_ORDER_DELAY_LEVEL = 16; + + /** + * 发送消息失败重试次数,默认2 + */ + public static final int RETRY_FAILED_COUNT = 2; + + /** + * 发送异步消息失败重试次数,默认2 + */ + public static final int ASYNC_RETRY_FAILED_COUNT = 2; + + /** + * vip订单取消 + */ + public static final String VIP_ORDER_CANCEL_TOPIC = "vip-order-cancel-topic"; + + /** + * vip订单支付成功 + */ + public static final String VIP_ORDER_PAY_SUCCESS_TOPIC = "vip-order-pay-success-topic"; + +} diff --git a/bnyer-common/pom.xml b/bnyer-common/pom.xml index 197f450..ac9d216 100644 --- a/bnyer-common/pom.xml +++ b/bnyer-common/pom.xml @@ -16,6 +16,7 @@ bnyer-common-security bnyer-common-datascope bnyer-common-datasource + bnyer-common-rocketmq bnyer-common diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java deleted file mode 100644 index fd0fa76..0000000 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/constants/UserVipTypeConstant.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.bnyer.img.constants; - -/** - * 用户会员vip常量 - * @author chengkun - * @date 2022/4/21 18:12 - */ -public class UserVipTypeConstant { - - /** - * 月卡 - */ - public static final String MONTH_CARD = "0"; - - /** - * 季卡 - */ - public static final String SEASON_CARD = "1"; - - /** - * 年卡 - */ - public static final String YEAR_CARD = "2"; -} diff --git a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java index 98a7363..53c580c 100644 --- a/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java +++ b/bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java @@ -5,10 +5,8 @@ import com.bnyer.common.core.constant.RedisKeyConstant; import com.bnyer.common.core.domain.UserVipRecord; import com.bnyer.common.core.dto.PayUserVipDto; import com.bnyer.common.core.exception.ServiceException; -import com.bnyer.common.core.utils.DateUtils; import com.bnyer.common.redis.service.RedissonService; import com.bnyer.img.constants.UserVipOrderStatusConstant; -import com.bnyer.img.constants.UserVipTypeConstant; import com.bnyer.img.mapper.UserVipRecordMapper; import com.bnyer.img.service.UserVipRecordService; import lombok.extern.slf4j.Slf4j; @@ -50,16 +48,16 @@ public class UserVipServiceRecordImpl implements UserVipRecordService { userVipRecord.setIsShow("1"); Date startTime = new Date(); userVipRecord.setStartTime(startTime); - if(userVipRecord.getType().equals(UserVipTypeConstant.MONTH_CARD)){ - //计算月卡(30天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 30)); - }else if(userVipRecord.getType().equals(UserVipTypeConstant.SEASON_CARD)){ - //结算季卡(90天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 90)); - }else{ - //计算年卡(365天)的结束时间 - userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 365)); - } +// if(userVipRecord.getType().equals(UserVipTypeConstant.MONTH_CARD)){ +// //计算月卡(30天)的结束时间 +// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 30)); +// }else if(userVipRecord.getType().equals(UserVipTypeConstant.SEASON_CARD)){ +// //结算季卡(90天)的结束时间 +// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 90)); +// }else{ +// //计算年卡(365天)的结束时间 +// userVipRecord.setEndTime(DateUtils.getDateAfter(startTime, 365)); +// } }else{ userVipRecord.setStatus(UserVipOrderStatusConstant.PAY_EXCEPTION); log.error("用户会员vip支付异常,锁被占用!"); diff --git a/bnyer-services/bnyer-order/pom.xml b/bnyer-services/bnyer-order/pom.xml index 8ff58cb..323dcc1 100644 --- a/bnyer-services/bnyer-order/pom.xml +++ b/bnyer-services/bnyer-order/pom.xml @@ -89,6 +89,11 @@ mybatis-plus-boot-starter + + + com.dimensionalnode + bnyer-common-rocketmq + @@ -128,4 +133,4 @@ - \ No newline at end of file + diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java index 4a7db6a..2674afb 100644 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/BnyerOrderApplication.java @@ -15,7 +15,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableCustomConfig @EnableCustomSwagger2 @EnableRyFeignClients -@SpringBootApplication +@SpringBootApplication(scanBasePackages = { "com.bnyer" }) @EnableAsync public class BnyerOrderApplication { diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java new file mode 100644 index 0000000..9155ba0 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/OrderCacheTtlAdapter.java @@ -0,0 +1,27 @@ +package com.bnyer.order.config; + +import com.bnyer.common.redis.adapter.CacheTtlAdapter; +import com.bnyer.common.redis.bo.CacheNameWithTtlBo; +import com.bnyer.common.redis.constant.OrderCacheNames; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Component +public class OrderCacheTtlAdapter implements CacheTtlAdapter{ + + @Override + public List listCacheNameWithTtl() { + List cacheNameWithTtls = new ArrayList<>(); + // 确认订单缓存30分钟 + cacheNameWithTtls.add(new CacheNameWithTtlBo(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_UID_KEY, 60 * 30)); + cacheNameWithTtls.add(new CacheNameWithTtlBo(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_KEY, 60 * 30)); + return cacheNameWithTtls; + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java new file mode 100644 index 0000000..da710d7 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/config/RocketMqConfig.java @@ -0,0 +1,28 @@ +package com.bnyer.order.config; + +import com.bnyer.common.rocketmq.config.RocketMqAdapter; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderCancelMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.VIP_ORDER_CANCEL_TOPIC); + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java new file mode 100644 index 0000000..bd35334 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java @@ -0,0 +1,78 @@ +package com.bnyer.order.controller; + +import com.bnyer.common.core.context.SecurityContextHolder; +import com.bnyer.common.core.dto.VipOrderDto; +import com.bnyer.common.core.enums.ResponseEnum; +import com.bnyer.common.core.vo.VipOrderVo; +import com.bnyer.common.core.web.controller.BaseController; +import com.bnyer.common.core.web.domain.AjaxResult; +import com.bnyer.common.redis.constant.CacheNames; +import com.bnyer.common.redis.constant.OrderCacheNames; +import com.bnyer.common.redis.service.RedisCacheService; +import com.bnyer.common.redis.service.RedisService; +import com.bnyer.order.service.VipOrderService; +import io.swagger.annotations.Api; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@Api(value = "会员订单相关接口",tags = "会员订单相关接口") +@RestController +@RequestMapping("/order/vip") +@Slf4j +public class VipOrderController extends BaseController { + + @Autowired + private RedisService redisService; + + @Autowired + private RedisCacheService redisCacheService; + + + @Autowired + private VipOrderService vipOrderService; + + /** + * 生成订单 + */ + @PostMapping("/confirmVipOrder") + @Operation(summary = "生成订单,返回订单信息" , description = "传入下单所需要的参数进行下单") + public AjaxResult confirmVipOrder(@Valid @RequestBody VipOrderDto vipOrderDto){ + VipOrderVo vipOrderVo = vipOrderService.confirmVipOrder(vipOrderDto); + return AjaxResult.success(vipOrderVo); + } + + /** + * 提交订单 + */ + @PostMapping("/submitOrder") + @Operation(summary = "提交订单,返回支付流水号" , description = "提交订单,通过支付场景获取订单信息生成订单并返回订单号") + public AjaxResult submitVipOrder() { + // 防止重复提交 + Long userId = SecurityContextHolder.getUserId(); + VipOrderVo vipOrderVo = redisCacheService.getCache(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_KEY, String.valueOf(userId)); + // 看看订单有没有过期 + if (Objects.isNull(vipOrderVo)) { + return AjaxResult.error(ResponseEnum.ORDER_EXPIRED); + } + boolean cad = redisService.cad(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_UID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); + if (!cad) { + return AjaxResult.error(ResponseEnum.REPEAT_ORDER); + } + String orderId = vipOrderService.submitVipOrder(userId,vipOrderVo); + return AjaxResult.success(orderId); + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java deleted file mode 100644 index ade0f8a..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/testController.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.order.controller; - -public class testController { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumSceneCode.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumSceneCode.java new file mode 100644 index 0000000..b410ac8 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumSceneCode.java @@ -0,0 +1,30 @@ +package com.bnyer.order.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author :WXC + * @description : + */ +@Getter +@AllArgsConstructor +public enum EnumSceneCode { + + VIP_RECHARGE("1","会员充值"), + ; + + private String sceneCode; + + private String name; + + public static String getSceneCodeName(String sceneCode) { + for (EnumSceneCode s : EnumSceneCode.values()) { + if (sceneCode.equals(s.getSceneCode())) { + return s.getName(); + } + } + return null; + } + +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java new file mode 100644 index 0000000..903a52a --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/enums/EnumVipType.java @@ -0,0 +1,32 @@ +package com.bnyer.order.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 用户会员vip常量 + * @author chengkun + * @date 2022/4/21 18:12 + */ +@Getter +@AllArgsConstructor +public enum EnumVipType { + + MONTH_CARD("0","月卡"), + SEASON_CARD("1","季卡"), + YEAR_CARD("2","年卡"), + ; + + private String typeCode; + + private String name; + + public static String getTypeCodeName(String typeCode) { + for (EnumSceneCode s : EnumSceneCode.values()) { + if (typeCode.equals(s.getSceneCode())) { + return s.getName(); + } + } + return null; + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java new file mode 100644 index 0000000..aacbf9c --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderCancelConsumer.java @@ -0,0 +1,24 @@ +package com.bnyer.order.listener.vip; + +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description :取消订单mq消费监听 + */ +@Slf4j +@Component +@RocketMQMessageListener(topic = RocketMqConstant.VIP_ORDER_CANCEL_TOPIC,consumerGroup = RocketMqConstant.VIP_ORDER_CANCEL_TOPIC) +public class VipOrderCancelConsumer implements RocketMQListener { + + @Override + public void onMessage(String orderId) { + log.info("收到消息:{}", orderId); +// cancelOrder(); + } +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPaySuccessConsumer.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPaySuccessConsumer.java new file mode 100644 index 0000000..2d901ba --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/listener/vip/VipOrderPaySuccessConsumer.java @@ -0,0 +1,18 @@ +//package com.bnyer.order.listener.vip; +// +//import com.bnyer.common.rocketmq.config.RocketMqConstant; +//import lombok.extern.slf4j.Slf4j; +//import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +//import org.springframework.stereotype.Component; +// +///** +// * @author :WXC +// * @Date :2023/03/24 +// * @description :订单支付成功mq消费监听 +// */ +//@Slf4j +//@Component +//@RocketMQMessageListener(topic = RocketMqConstant.VIP_ORDER_PAY_SUCCESS_TOPIC,consumerGroup = RocketMqConstant.VIP_ORDER_PAY_SUCCESS_TOPIC) +//public class VipOrderPaySuccessConsumer { +// +//} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java deleted file mode 100644 index 20ed969..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/TestService.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.order.service; - -public interface TestService { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java new file mode 100644 index 0000000..b147150 --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/VipOrderService.java @@ -0,0 +1,27 @@ +package com.bnyer.order.service; + +import com.bnyer.common.core.dto.VipOrderDto; +import com.bnyer.common.core.vo.VipOrderVo; + +/** + * @author :WXC + * @Date :2023/03/27 + * @description : + */ +public interface VipOrderService { + /** + * 生成订单,返回订单信息 + * @param vipOrderDto 下单所需要的参数 + * @return 订单信息 + */ + VipOrderVo confirmVipOrder(VipOrderDto vipOrderDto); + + /** + * 提交订单,返回支付流水号 + * + * @param userId + * @param vipOrderVo 缓存的订单信息 + * @return 支付流水号 + */ + String submitVipOrder(Long userId, VipOrderVo vipOrderVo); +} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java deleted file mode 100644 index 4bb1f8f..0000000 --- a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/TestServiceImpl.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.bnyer.order.service.impl; - -import com.bnyer.order.service.TestService; -import org.springframework.stereotype.Service; - -@Service -public class TestServiceImpl implements TestService { -} diff --git a/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java new file mode 100644 index 0000000..cac68ea --- /dev/null +++ b/bnyer-services/bnyer-order/src/main/java/com/bnyer/order/service/impl/VipOrderServiceImpl.java @@ -0,0 +1,97 @@ +package com.bnyer.order.service.impl; + +import com.bnyer.common.core.exception.ServiceException; +import com.bnyer.common.core.utils.DateUtils; +import com.bnyer.common.core.utils.bean.EntityConvertUtil; +import com.bnyer.common.redis.constant.CacheNames; +import com.bnyer.common.redis.constant.OrderCacheNames; +import com.bnyer.common.core.context.SecurityContextHolder; +import com.bnyer.common.core.dto.VipOrderDto; +import com.bnyer.common.core.vo.VipOrderVo; +import com.bnyer.common.redis.service.RedisCacheService; +import com.bnyer.common.redis.service.RedisService; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import com.bnyer.order.enums.EnumVipType; +import com.bnyer.order.service.VipOrderService; +import lombok.extern.slf4j.Slf4j; +import org.apache.rocketmq.client.producer.SendStatus; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.messaging.support.GenericMessage; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Objects; + +/** + * @author :WXC + * @Date :2023/03/28 + * @description : + */ +@Slf4j +@Service +public class VipOrderServiceImpl implements VipOrderService { + + @Autowired + private RedisService redisService; + + @Autowired + private RedisCacheService redisCacheService; + + @Autowired + private RocketMQTemplate orderCancelMqTemplate; + + @Override + public VipOrderVo confirmVipOrder(VipOrderDto vipOrderDto) { + Long userId = SecurityContextHolder.getUserId(); + //将要返回给前端的完整的订单信息 + VipOrderVo vipOrderVo = buildVipOrderVo(vipOrderDto); + // 防止重复提交 + redisService.setCacheObject(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_UID_KEY + CacheNames.UNION + userId, String.valueOf(userId)); + // 保存订单计算结果缓存 + redisCacheService.putCache(OrderCacheNames.VIP_ORDER_PREFIX + OrderCacheNames.ORDER_CONFIRM_KEY ,String.valueOf(userId), vipOrderVo); + return vipOrderVo; + } + + /** + * 构建vip订单 + * @param vipOrderDto + * @return + */ + private VipOrderVo buildVipOrderVo(VipOrderDto vipOrderDto) { + VipOrderVo vipOrderVo = EntityConvertUtil.copy(vipOrderDto, VipOrderVo.class); + Date startTime = new Date(); + vipOrderVo.setStartTime(startTime); + if(EnumVipType.MONTH_CARD.getTypeCode().equals(vipOrderDto.getType())){ + //计算月卡(30天)的结束时间 + vipOrderVo.setEndTime(DateUtils.getDateAfter(startTime, 30)); + }else if(EnumVipType.SEASON_CARD.getTypeCode().equals(vipOrderDto.getType())){ + //结算季卡(90天)的结束时间 + vipOrderVo.setEndTime(DateUtils.getDateAfter(startTime, 90)); + }else if (EnumVipType.YEAR_CARD.getTypeCode().equals(vipOrderDto.getType())){ + //计算年卡(365天)的结束时间 + vipOrderVo.setEndTime(DateUtils.getDateAfter(startTime, 365)); + }else { + throw new ServiceException("参数异常!"); + } + return vipOrderVo; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String submitVipOrder(Long userId,VipOrderVo vipOrderVo) { + String orderId = "123"; + //发送消息,如果三十分钟后没有支付,则取消订单 + SendStatus sendStatus = orderCancelMqTemplate.syncSend(RocketMqConstant.VIP_ORDER_CANCEL_TOPIC, new GenericMessage<>(orderId), RocketMqConstant.TIMEOUT, RocketMqConstant.CANCEL_ORDER_DELAY_LEVEL).getSendStatus(); + if (!Objects.equals(sendStatus,SendStatus.SEND_OK)) { + // 消息发不出去就抛异常,发的出去无所谓 + throw new ServiceException("系统繁忙,请稍候重试!"); + }else { + log.info("消息发送成功,topic:{}",RocketMqConstant.VIP_ORDER_CANCEL_TOPIC); + } + return orderId; + } +} diff --git a/bnyer-services/bnyer-pay/pom.xml b/bnyer-services/bnyer-pay/pom.xml index 92ea1c3..e232156 100644 --- a/bnyer-services/bnyer-pay/pom.xml +++ b/bnyer-services/bnyer-pay/pom.xml @@ -95,6 +95,12 @@ mybatis-plus-boot-starter + + + com.dimensionalnode + bnyer-common-rocketmq + + diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java index a30abb8..b3112d7 100644 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/BnyerPayApplication.java @@ -15,7 +15,7 @@ import org.springframework.scheduling.annotation.EnableAsync; @EnableCustomConfig @EnableCustomSwagger2 @EnableRyFeignClients -@SpringBootApplication +@SpringBootApplication(scanBasePackages = { "com.bnyer" }) @EnableAsync public class BnyerPayApplication { diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java new file mode 100644 index 0000000..88fe05f --- /dev/null +++ b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/RocketMqConfig.java @@ -0,0 +1,28 @@ +package com.bnyer.pay.config; + +import com.bnyer.common.rocketmq.config.RocketMqAdapter; +import com.bnyer.common.rocketmq.config.RocketMqConstant; +import org.apache.rocketmq.spring.core.RocketMQTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.context.config.annotation.RefreshScope; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; + +/** + * @author :WXC + * @Date :2023/03/24 + * @description : + */ +@RefreshScope +@Configuration +public class RocketMqConfig { + @Autowired + private RocketMqAdapter rocketMqAdapter; + + @Lazy + @Bean(destroyMethod = "destroy") + public RocketMQTemplate orderPaySuccessMqTemplate() { + return rocketMqAdapter.getTemplateByTopicName(RocketMqConstant.VIP_ORDER_PAY_SUCCESS_TOPIC); + } +} diff --git a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java b/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java deleted file mode 100644 index 263d3ce..0000000 --- a/bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/config/TestConfig.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.bnyer.pay.config; - -public class TestConfig { -} diff --git a/pom.xml b/pom.xml index ddf9806..8134091 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 4.23.21.ALL 4.2.0 7.2.18 + 2.2.2 @@ -250,6 +251,13 @@ ${bnyer.version} + + + com.dimensionalnode + bnyer-common-rocketmq + ${bnyer.version} + + com.dimensionalnode @@ -282,6 +290,11 @@ qiniu-java-sdk ${qiniu.version} + + org.apache.rocketmq + rocketmq-spring-boot-starter + ${rocketmq-springboot.version} +