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; |
|||
} |
|||
@ -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