39 changed files with 1168 additions and 89 deletions
@ -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; |
||||
|
|
||||
|
} |
||||
@ -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 + '\'' + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
||||
@ -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 <T> 类型 |
||||
|
* @return List<T> |
||||
|
* @description 用于jpa查询自定义vo用的 |
||||
|
*/ |
||||
|
public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz) { |
||||
|
List<T> 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<T> 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> T convertBean(Object object, Class<T> 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> T mapToObject(Map<String, Object> map, Class<T> 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> T copy(Object source, Class<T> 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<A> copy到List<B> |
||||
|
* @param list List<B> |
||||
|
* @param clazz B |
||||
|
* @return List<B> |
||||
|
*/ |
||||
|
public static <T> List<T> copy(List<?> list,Class<T> clazz){ |
||||
|
String oldOb = JSON.toJSONString(list); |
||||
|
return JSON.parseArray(oldOb, clazz); |
||||
|
} |
||||
|
|
||||
|
public static <T, E> List<T> copyList(List<E> list, Class<T> target, String... ignoreProperties) { |
||||
|
List<T> 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<String, String> transBean2Map(Object obj) { |
||||
|
if (obj == null) { |
||||
|
return null; |
||||
|
} |
||||
|
Map<String, String> map = new HashMap<String, String>(); |
||||
|
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 <T> |
||||
|
* @return target |
||||
|
*/ |
||||
|
public static <T> 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<String> 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); |
||||
|
} |
||||
|
} |
||||
@ -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; |
||||
|
|
||||
|
} |
||||
@ -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<CacheNameWithTtlBo> listCacheNameWithTtl(); |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
@ -1,50 +1,149 @@ |
|||||
package com.bnyer.common.redis.configure; |
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.CachingConfigurerSupport; |
||||
import org.springframework.cache.annotation.EnableCaching; |
import org.springframework.cache.annotation.EnableCaching; |
||||
import org.springframework.context.annotation.Bean; |
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
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.connection.RedisConnectionFactory; |
||||
import org.springframework.data.redis.core.RedisTemplate; |
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 org.springframework.data.redis.serializer.StringRedisSerializer; |
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect; |
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo; |
import java.time.Duration; |
||||
import com.fasterxml.jackson.annotation.PropertyAccessor; |
import java.util.Collections; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
import java.util.HashMap; |
||||
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; |
import java.util.Map; |
||||
|
|
||||
/** |
/** |
||||
* redis配置 |
* redis配置 |
||||
* |
* |
||||
* @author ruoyi |
* @author ruoyi |
||||
*/ |
*/ |
||||
@Configuration |
@Configuration |
||||
@EnableCaching |
@EnableCaching |
||||
public class RedisConfig extends CachingConfigurerSupport |
public class RedisConfig extends CachingConfigurerSupport |
||||
{ |
{ |
||||
|
|
||||
@Bean |
@Bean |
||||
@SuppressWarnings(value = { "unchecked", "rawtypes" }) |
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, CacheTtlAdapter cacheTtlAdapter) { |
||||
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) |
|
||||
{ |
|
||||
RedisTemplate<Object, Object> template = new RedisTemplate<>(); |
|
||||
template.setConnectionFactory(connectionFactory); |
|
||||
|
|
||||
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(); |
redisCacheManager.setTransactionAware(true); |
||||
mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); |
return redisCacheManager; |
||||
mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); |
} |
||||
serializer.setObjectMapper(mapper); |
|
||||
|
|
||||
// 使用StringRedisSerializer来序列化和反序列化redis的key值
|
private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(CacheTtlAdapter cacheTtlAdapter) { |
||||
template.setKeySerializer(new StringRedisSerializer()); |
if (cacheTtlAdapter == null) { |
||||
template.setValueSerializer(serializer); |
return Collections.emptyMap(); |
||||
|
} |
||||
|
Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(16); |
||||
|
|
||||
// Hash的key也采用StringRedisSerializer的序列化方式
|
for (CacheNameWithTtlBo cacheNameWithTtlBO : cacheTtlAdapter.listCacheNameWithTtl()) { |
||||
template.setHashKeySerializer(new StringRedisSerializer()); |
redisCacheConfigurationMap.put(cacheNameWithTtlBO.getCacheName(), |
||||
template.setHashValueSerializer(serializer); |
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<Object> 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<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
|
||||
|
// {
|
||||
|
// RedisTemplate<Object, Object> 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<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory, |
||||
|
RedisSerializer<Object> redisSerializer) { |
||||
|
RedisTemplate<Object, Object> 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(); |
template.afterPropertiesSet(); |
||||
return template; |
return template; |
||||
} |
} |
||||
|
|
||||
|
|
||||
|
@Bean |
||||
|
@ConditionalOnMissingBean |
||||
|
public CacheTtlAdapter cacheTtl() { |
||||
|
return Collections::emptyList; |
||||
|
} |
||||
|
|
||||
} |
} |
||||
|
|||||
@ -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 = ":"; |
||||
|
} |
||||
@ -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"; |
||||
|
|
||||
|
} |
||||
@ -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> 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); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
|
<parent> |
||||
|
<groupId>com.dimensionalnode</groupId> |
||||
|
<artifactId>bnyer-common</artifactId> |
||||
|
<version>1.0.0</version> |
||||
|
</parent> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
<description> |
||||
|
bnyer-common-rocketmq消息队列模块 |
||||
|
</description> |
||||
|
|
||||
|
<artifactId>bnyer-common-rocketmq</artifactId> |
||||
|
|
||||
|
<dependencies> |
||||
|
<!-- bnyer Common Core--> |
||||
|
<dependency> |
||||
|
<groupId>com.dimensionalnode</groupId> |
||||
|
<artifactId>bnyer-common-core</artifactId> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- bnyer Common rocketmq--> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.rocketmq</groupId> |
||||
|
<artifactId>rocketmq-spring-boot-starter</artifactId> |
||||
|
</dependency> |
||||
|
</dependencies> |
||||
|
|
||||
|
</project> |
||||
@ -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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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"; |
||||
|
|
||||
|
} |
||||
@ -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"; |
|
||||
} |
|
||||
@ -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<CacheNameWithTtlBo> listCacheNameWithTtl() { |
||||
|
List<CacheNameWithTtlBo> 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; |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -1,4 +0,0 @@ |
|||||
package com.bnyer.order.controller; |
|
||||
|
|
||||
public class testController { |
|
||||
} |
|
||||
@ -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; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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<String> { |
||||
|
|
||||
|
@Override |
||||
|
public void onMessage(String orderId) { |
||||
|
log.info("收到消息:{}", orderId); |
||||
|
// cancelOrder();
|
||||
|
} |
||||
|
} |
||||
@ -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 {
|
||||
|
//
|
||||
|
//}
|
||||
@ -1,4 +0,0 @@ |
|||||
package com.bnyer.order.service; |
|
||||
|
|
||||
public interface TestService { |
|
||||
} |
|
||||
@ -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); |
||||
|
} |
||||
@ -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 { |
|
||||
} |
|
||||
@ -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; |
||||
|
} |
||||
|
} |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -1,4 +0,0 @@ |
|||||
package com.bnyer.pay.config; |
|
||||
|
|
||||
public class TestConfig { |
|
||||
} |
|
||||
Loading…
Reference in new issue