From 3191f721ce118bf0b04d46b2d06ee0046a9dba3f Mon Sep 17 00:00:00 2001 From: czk <13938338+chen-zhaozhaokai@user.noreply.gitee.com> Date: Mon, 20 May 2024 16:04:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=BC=E5=9B=A2=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mall_auth/pom.xml | 8 +- .../mall/auth/controller/AuthController.java | 6 +- .../com/mall/auth/service/AuthService.java | 3 + .../auth/service/impl/AuthServiceImpl.java | 3 +- mall_auth/src/main/resources/bootstrap.yml | 12 ++ .../com/mall/common/utils/SecurityUtils.java | 44 ++++ .../com/mall/server/ServerApplication.java | 2 + .../mall/server/aop/LogAnnotationAspect.java | 24 ++- .../mall/server/config/GroupDelayConfig.java | 94 +++++++++ ...elayConfig.java => SpikesDelayConfig.java} | 12 +- .../server/controller/TeamworkController.java | 55 +++++ .../mall/server/domain/TeamworkEntity.java | 109 ++++++++++ .../server/domain/TeamworkRecordEntity.java | 65 ++++++ .../mall/server/domain/vo/GroupBookingVo.java | 18 ++ .../mall/server/mapper/TeamworkMapper.java | 12 ++ .../server/mapper/TeamworkRecordMapper.java | 12 ++ .../server/monitor/GroupBookingMonitor.java | 68 +++++++ .../mall/server/monitor/SpikesMonitor.java | 50 +++-- .../mall/server/service/TeamworkService.java | 17 ++ .../service/impl/SpikesServiceImpl.java | 4 +- .../service/impl/TeamworkServiceImpl.java | 192 ++++++++++++++++++ 21 files changed, 772 insertions(+), 38 deletions(-) create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/config/GroupDelayConfig.java rename mall_modules/mall_server/src/main/java/com/mall/server/config/{DelayConfig.java => SpikesDelayConfig.java} (87%) create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/controller/TeamworkController.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkEntity.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkRecordEntity.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/domain/vo/GroupBookingVo.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkMapper.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkRecordMapper.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/monitor/GroupBookingMonitor.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/service/TeamworkService.java create mode 100644 mall_modules/mall_server/src/main/java/com/mall/server/service/impl/TeamworkServiceImpl.java diff --git a/mall_auth/pom.xml b/mall_auth/pom.xml index ad03314..0b4e0b5 100644 --- a/mall_auth/pom.xml +++ b/mall_auth/pom.xml @@ -36,12 +36,18 @@ aliyun-sdk-oss 3.10.2 - junit junit 4.12 + + + org.springframework.cloud + spring-cloud-starter-zipkin + 2.2.8.RELEASE + + diff --git a/mall_auth/src/main/java/com/mall/auth/controller/AuthController.java b/mall_auth/src/main/java/com/mall/auth/controller/AuthController.java index 253eb54..ff79378 100644 --- a/mall_auth/src/main/java/com/mall/auth/controller/AuthController.java +++ b/mall_auth/src/main/java/com/mall/auth/controller/AuthController.java @@ -16,6 +16,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.util.Base64; +import java.util.HashMap; import java.util.concurrent.CompletableFuture; /** @@ -112,7 +115,4 @@ public class AuthController { return authService.authentication(idCardRequest); } - @Autowired - private UserServiceFeign userServiceFeign; - } diff --git a/mall_auth/src/main/java/com/mall/auth/service/AuthService.java b/mall_auth/src/main/java/com/mall/auth/service/AuthService.java index 70be632..83e92bb 100644 --- a/mall_auth/src/main/java/com/mall/auth/service/AuthService.java +++ b/mall_auth/src/main/java/com/mall/auth/service/AuthService.java @@ -8,6 +8,8 @@ import com.mall.common.domain.vo.UserInfoVo; import com.mall.common.result.Result; import org.springframework.web.multipart.MultipartFile; +import java.util.HashMap; + public interface AuthService { Result login(LoginVo loginVo); @@ -25,4 +27,5 @@ public interface AuthService { Result authentication(IdCardRequest idCardRequest); + } diff --git a/mall_auth/src/main/java/com/mall/auth/service/impl/AuthServiceImpl.java b/mall_auth/src/main/java/com/mall/auth/service/impl/AuthServiceImpl.java index 2c0a477..3eddc76 100644 --- a/mall_auth/src/main/java/com/mall/auth/service/impl/AuthServiceImpl.java +++ b/mall_auth/src/main/java/com/mall/auth/service/impl/AuthServiceImpl.java @@ -216,10 +216,9 @@ public class AuthServiceImpl implements AuthService { return idCardCheckLifeVo; }, threadPoolConfig.getThreadPoolExecutor()); -// CompletableFuture.allOf(f1,f2).join(); IdCardValidateVo idCardValidateVo = null; IdCardCheckLifeVo idCardCheckLifeVo = null; - +// CompletableFuture.allOf(f1,f2).join(); try { idCardValidateVo = f1.get(); idCardCheckLifeVo = f2.get(); diff --git a/mall_auth/src/main/resources/bootstrap.yml b/mall_auth/src/main/resources/bootstrap.yml index 19925c4..c22cc25 100644 --- a/mall_auth/src/main/resources/bootstrap.yml +++ b/mall_auth/src/main/resources/bootstrap.yml @@ -3,6 +3,18 @@ server: port: 9000 # Spring spring: + #服务追踪 + zipkin: + base-url: http://124.221.183.9:9411/ # zipkin服务器的地址 + discovery-client-enabled: false #关闭服务发现,否则spring cloud会把zipkin的url当作服务名称 + sender: + type: web #设苦使用http的方式传输数据 + sleuth: + traceId128: true + sampler: + probability: 1.0 #抽样采集率为100%,默认为0.1 ,即10% + propagation-keys: user_name + servlet: multipart: #这将限制单个文件的最大大小为10MB diff --git a/mall_common/src/main/java/com/mall/common/utils/SecurityUtils.java b/mall_common/src/main/java/com/mall/common/utils/SecurityUtils.java index a38aa1b..6d5bea1 100644 --- a/mall_common/src/main/java/com/mall/common/utils/SecurityUtils.java +++ b/mall_common/src/main/java/com/mall/common/utils/SecurityUtils.java @@ -1,6 +1,9 @@ package com.mall.common.utils; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; @@ -12,6 +15,7 @@ import java.util.Base64; */ public class SecurityUtils { + private static final String key = "01020304"; /** * 生成BCryptPasswordEncoder密码 @@ -51,4 +55,44 @@ public class SecurityUtils { throw new RuntimeException(e); } } + + // 加密方法 + public static String encrypt(String plaintext) throws Exception { + // 创建 Cipher 对象 + Cipher cipher = Cipher.getInstance("DES"); + + // 创建 SecretKey 对象 + SecretKey secretKey = new SecretKeySpec(key.getBytes(), "DES"); + + // 初始化 Cipher 对象为加密模式,并传入密钥 + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + + // 执行加密操作 + byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes()); + + // 对加密后的数据进行 Base64 编码 + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + // 解密方法 + public static String decrypt(String ciphertext) throws Exception { + // 创建 Cipher 对象 + Cipher cipher = Cipher.getInstance("DES"); + + // 创建 SecretKey 对象 + SecretKey secretKey = new SecretKeySpec(key.getBytes(), "DES"); + + // 初始化 Cipher 对象为解密模式,并传入密钥 + cipher.init(Cipher.DECRYPT_MODE, secretKey); + + // 对 Base64 编码后的密文进行解码 + byte[] encryptedBytes = Base64.getDecoder().decode(ciphertext); + + // 执行解密操作 + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + + // 将解密后的字节数组转换为字符串并返回 + return new String(decryptedBytes); + } + } diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/ServerApplication.java b/mall_modules/mall_server/src/main/java/com/mall/server/ServerApplication.java index c58d53a..dd9197f 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/ServerApplication.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/ServerApplication.java @@ -2,6 +2,7 @@ package com.mall.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; /** @@ -10,6 +11,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication @EnableScheduling @EnableFeignClients +@EnableAsync public class ServerApplication { public static void main(String[] args) { SpringApplication.run(ServerApplication.class,args); diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/aop/LogAnnotationAspect.java b/mall_modules/mall_server/src/main/java/com/mall/server/aop/LogAnnotationAspect.java index aaeac6a..907a399 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/aop/LogAnnotationAspect.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/aop/LogAnnotationAspect.java @@ -21,19 +21,27 @@ public class LogAnnotationAspect { @Pointcut("execution(* com.mall.server.service.impl.*.*(..))") private void allMethod(){} - //应用周围通知 +// 应用周围通知 @Around("allMethod()") - public void doAround(ProceedingJoinPoint call) throws Throwable{ - long start = new Date().getTime(); + public Object doAround(ProceedingJoinPoint call) throws Throwable { + long start = System.currentTimeMillis(); // 更推荐使用 System.currentTimeMillis() String methodName = call.getSignature().getName(); // 获取方法名 Object[] args = call.getArgs(); // 获取方法的参数 - call.proceed(); - long end = new Date().getTime(); - log.info("耗时:"+(end-start)/1000+"秒"); - log.info(methodName+"()方法执行"); + + // 调用原始方法并获取返回值 + Object proceed = call.proceed(); + + long end = System.currentTimeMillis(); // 更推荐使用 System.currentTimeMillis() + log.info("方法 {} 执行耗时:{} 秒", methodName, (end - start) / 1000.0); + + // 打印方法名和参数 + log.info("{}() 方法执行", methodName); for (Object arg : args) { - log.info("参数:"+String.valueOf(arg)); + log.info("参数:{}", String.valueOf(arg)); } + + // 将方法的返回值传回 + return proceed; // 确保返回原方法的返回值 } } diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/config/GroupDelayConfig.java b/mall_modules/mall_server/src/main/java/com/mall/server/config/GroupDelayConfig.java new file mode 100644 index 0000000..d159a7f --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/config/GroupDelayConfig.java @@ -0,0 +1,94 @@ +package com.mall.server.config; + +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.core.Binding; +import org.springframework.amqp.core.BindingBuilder; +import org.springframework.amqp.core.DirectExchange; +import org.springframework.amqp.core.Queue; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; + +/** + * 拼团延迟队列 + */ +@Log4j2 +@Configuration +public class GroupDelayConfig { + + /** + * 队列 + */ + public static final String CZKQUEUE = "group_queue"; + /** + * 交换机 + */ + public static final String EXCHANGE = "group_exchange"; + /** + * 路由key + */ + public static final String ROUKEYCZK = "group_queue"; + + + + @Bean + public DirectExchange directExchange() { + return new DirectExchange(EXCHANGE); + } + + @Bean + public Queue autoDeleteQueue1(){ + HashMap map = new HashMap<>(); + map.put("x-dead-letter-exchange",DEADEXCHANGE); + map.put("x-dead-letter-routing-key",DEADROUKEY); + map.put("x-message-ttl",MESSAGE_FIVE); + return new Queue(CZKQUEUE,true,false,false,map); + } + + @Bean + public Binding binding1a() { + return BindingBuilder.bind(autoDeleteQueue1()) + .to(directExchange()) + .with(ROUKEYCZK); + } + + /** + * 死信交换机 + */ + public static final String DEADEXCHANGE = "group_dead_exchange"; + /** + * 死信队列 + */ + public static final String DEADQUEUE = "group_dead_queue"; + /** + * 死信路由key + */ + public static final String DEADROUKEY = "group_dead_queue"; + /** + * 5秒 + */ + public static final Integer MESSAGE_FIVE = 20000; + /** + * 10分钟 + */ + public static final Integer MESSAGE_FEN = 600000; + + + @Bean + public DirectExchange deadExchange() { + return new DirectExchange(DEADEXCHANGE); + } + + @Bean + public Queue autoDeleteQueue2(){ + return new Queue(DEADQUEUE,true); + } + + @Bean + public Binding binding2() { + return BindingBuilder.bind(autoDeleteQueue2()) + .to(deadExchange()) + .with(DEADROUKEY); + } +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/config/DelayConfig.java b/mall_modules/mall_server/src/main/java/com/mall/server/config/SpikesDelayConfig.java similarity index 87% rename from mall_modules/mall_server/src/main/java/com/mall/server/config/DelayConfig.java rename to mall_modules/mall_server/src/main/java/com/mall/server/config/SpikesDelayConfig.java index a6b7570..90d58e7 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/config/DelayConfig.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/config/SpikesDelayConfig.java @@ -4,22 +4,16 @@ import org.springframework.amqp.core.Binding; import org.springframework.amqp.core.BindingBuilder; import org.springframework.amqp.core.DirectExchange; import org.springframework.amqp.core.Queue; -import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; /** - * @Author:Chen - * @Package:com.bw.common.config - * @Project:holiday2 - * @name:RabbitConfig - * @Date:2024/2/5 10:55 - * @Description: TODO + * 秒杀延迟队列 */ @Log4j2 @Configuration -public class DelayConfig { +public class SpikesDelayConfig { /** * 队列 @@ -72,7 +66,7 @@ public class DelayConfig { /** * 5秒 */ - public static final Integer MESSAGE_FIVE = 5000; + public static final Integer MESSAGE_FIVE = 20000; /** * 10分钟 */ diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/controller/TeamworkController.java b/mall_modules/mall_server/src/main/java/com/mall/server/controller/TeamworkController.java new file mode 100644 index 0000000..f9bdaae --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/controller/TeamworkController.java @@ -0,0 +1,55 @@ +package com.mall.server.controller; + +import cn.hutool.crypto.SecureUtil; +import com.mall.common.result.BizException; +import com.mall.common.result.Result; +import com.mall.common.utils.SecurityUtils; +import com.mall.server.service.TeamworkService; +import io.swagger.models.auth.In; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.web.bind.annotation.*; + +import javax.validation.constraints.NotNull; + +/** + *拼团Controller + */ +@RestController +@RequestMapping("/teamwork") +public class TeamworkController { + + @Autowired + private TeamworkService teamworkService; + + /** + * 用户开启拼团 + * @param id + * @return + */ + @GetMapping("groupBooking") + public Result groupBooking(@NotNull String id, @RequestParam Integer state){ + //state 0未加密 1加密 + if(state==null || state==0){ + return teamworkService.groupBooking(Long.parseLong(id)); + } + String skuId = null; + try { + skuId = SecurityUtils.decrypt(id); + } catch (Exception e) { + throw new RuntimeException(e); + } + return teamworkService.groupBooking(Long.parseLong(skuId)); + } + + /** + * 分享加密路径 + * @param skuId + * @return + */ + @GetMapping("encryption") + public Result encryption(@NotNull Long skuId){ + return teamworkService.encryption(skuId); + } + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkEntity.java b/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkEntity.java new file mode 100644 index 0000000..90992c7 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkEntity.java @@ -0,0 +1,109 @@ +package com.mall.server.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.Date; + +/** + *拼团表 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("pms_teamwork") +public class TeamworkEntity { + /** + * 主键id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 开团时间 + */ + private Date startGroupTime; + /** + * 几人团 + */ + private Integer groupOfSeveral; + /** + * 几人参与 + */ + private Integer severalPeopleInvolved; + /** + * 拼团号 + */ + private Integer packageNumber; + /** + * 结束时间 + */ + private Date finishTime; + /** + * 拼团名称 + */ + private String groupName; + /** + * 拼团简介 + */ + private String groupIntroduce; + /** + * 原价 + */ + private BigDecimal originalPrice; + /** + * 拼团价 + */ + private BigDecimal groupPrice; + /** + * 拼团时效 + */ + private Date groupAging; + /** + * 拼团人数 + */ + private Integer groupNumber; + /** + * 购买数量限制 + */ + private Integer buyNumRestrict; + /** + * 运费类型0-包邮 1-不包邮 + */ + private Integer freightType; + /** + * 拼团状态0正在平团1拼团成功2拼团失败 + */ + private Integer groupStatus; + /** + * 活动状态 + */ + private Integer activeState; + /** + * 商品属性 + */ + private Long skuId; + /** + * 后台人员设置默认拼团持续时间(时间到未成团,解散) + */ + private Integer continueDate; + /** + * 拼团活动开始日期 + */ + private Date createDate; + /** + * 拼团活动结束日期 + */ + private Date endDate; +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkRecordEntity.java b/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkRecordEntity.java new file mode 100644 index 0000000..5877096 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/domain/TeamworkRecordEntity.java @@ -0,0 +1,65 @@ +package com.mall.server.domain; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.Date; + +/** + *拼团记录表 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("pms_teamwork_record") +public class TeamworkRecordEntity { + /** + * 拼团记录主键ID + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 拼团订单号 + */ + private String teamworkOrderSn; + /** + * 拼团id + */ + private Long teamworkId; + /** + * 购买价格 + */ + private BigDecimal price; + /** + * 购买个数 + */ + private Integer num; + /** + * 是否成团 0未 1成 + */ + private Integer severalPeopleInvolved; + /** + * 创建时间 + */ + private Date createTime; + /** + * 用户id + */ + private Long userId; + /** + * 拼团商品id + */ + private Long skuId; + /** + * 判断是否为商品拼团发起人 0是 1 不是 + */ + private Integer state; +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/domain/vo/GroupBookingVo.java b/mall_modules/mall_server/src/main/java/com/mall/server/domain/vo/GroupBookingVo.java new file mode 100644 index 0000000..2ca6c7b --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/domain/vo/GroupBookingVo.java @@ -0,0 +1,18 @@ +package com.mall.server.domain.vo; + +import com.mall.common.domain.OrderItemEntity; +import com.mall.server.domain.TeamworkRecordEntity; +import lombok.Builder; +import lombok.Data; + +/** + *拼团下单添加记录 + */ +@Data +@Builder +public class GroupBookingVo { + + private TeamworkRecordEntity teamworkRecordEntity; + private OrderItemEntity orderItemEntity; + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkMapper.java b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkMapper.java new file mode 100644 index 0000000..6713b98 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkMapper.java @@ -0,0 +1,12 @@ +package com.mall.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mall.server.domain.TeamworkEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + *拼团Mapper + */ +@Mapper +public interface TeamworkMapper extends BaseMapper { +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkRecordMapper.java b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkRecordMapper.java new file mode 100644 index 0000000..7b3bdee --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/TeamworkRecordMapper.java @@ -0,0 +1,12 @@ +package com.mall.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mall.server.domain.TeamworkRecordEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + *拼团记录表 + */ +@Mapper +public interface TeamworkRecordMapper extends BaseMapper { +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/monitor/GroupBookingMonitor.java b/mall_modules/mall_server/src/main/java/com/mall/server/monitor/GroupBookingMonitor.java new file mode 100644 index 0000000..fa61d56 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/monitor/GroupBookingMonitor.java @@ -0,0 +1,68 @@ +package com.mall.server.monitor; + +import com.alibaba.fastjson.JSON; +import com.mall.common.domain.OrderItemEntity; +import com.mall.server.domain.vo.GroupBookingVo; +import com.mall.server.mapper.OrderItemMapper; +import com.mall.server.mapper.TeamworkRecordMapper; +import com.rabbitmq.client.Channel; +import org.springframework.amqp.core.Message; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + *监听添加拼团记录表和下单 + */ +@Component +public class GroupBookingMonitor { + + @Autowired + private TeamworkRecordMapper teamworkRecordMapper; + + @Autowired + private OrderItemMapper orderItemMapper; + + /** + * 添加拼团记录表和下单 + * @param groupBookingVo + */ + @Async + @EventListener(GroupBookingVo.class) + public void customerEventListener(GroupBookingVo groupBookingVo) { + orderItemMapper.insert(groupBookingVo.getOrderItemEntity()); + teamworkRecordMapper.insert(groupBookingVo.getTeamworkRecordEntity()); + } + + /** + * 拼团超时,进行业务处理 + * @param msg + * @param message + * @param channel + */ + public void GroupConsumer(String msg, Message message, Channel channel){ + long deliveryTag = message.getMessageProperties().getDeliveryTag(); + + try { + + channel.basicAck(deliveryTag,false); + } catch (IOException e) { + try { + if(deliveryTag<3){ + Thread.sleep(3000); + channel.basicNack(deliveryTag,false,true); + }else { + channel.basicNack(deliveryTag,false,false); + } + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + } + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/monitor/SpikesMonitor.java b/mall_modules/mall_server/src/main/java/com/mall/server/monitor/SpikesMonitor.java index 49cec22..a55079e 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/monitor/SpikesMonitor.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/monitor/SpikesMonitor.java @@ -3,7 +3,7 @@ package com.mall.server.monitor; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.mall.common.domain.OrderItemEntity; -import com.mall.server.config.DelayConfig; +import com.mall.server.config.SpikesDelayConfig; import com.mall.server.config.MqConfig; import com.mall.server.constant.SpikesConstant; import com.mall.server.service.OrderItemService; @@ -31,22 +31,35 @@ public class SpikesMonitor { @Autowired private OrderItemService orderItemService; - @RabbitListener(queues = DelayConfig.DEADQUEUE) + /** + * 取消订单 + * @param meg + * @param message + * @param channel + */ + @RabbitListener(queues = SpikesDelayConfig.DEADQUEUE) public void consumer(String meg, Message message, Channel channel){ log.info("延迟队列接受到消息"+meg); long deliveryTag = message.getMessageProperties().getDeliveryTag(); + if(meg==null){ + try { + channel.basicAck(deliveryTag,false); + } catch (IOException e) { + throw new RuntimeException(e); + } + } String[] split = meg.split(","); String skuId = split[0]; String orderSn = split[1]; - OrderItemEntity orderItemEntity = orderItemService.getOne( - new LambdaQueryWrapper() - .eq(OrderItemEntity::getOrderSn, orderSn) - ); - if (orderItemEntity.getStatus().equals(0)) { - RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + skuId); - semaphore.release(1); - } try { + OrderItemEntity orderItemEntity = orderItemService.getOne( + new LambdaQueryWrapper() + .eq(OrderItemEntity::getOrderSn, orderSn) + ); + if (orderItemEntity.getStatus().equals(0)) { + RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + skuId); + semaphore.release(1); + } channel.basicAck(deliveryTag,false); } catch (IOException e) { try { @@ -62,14 +75,25 @@ public class SpikesMonitor { } } + /** + * 下订单 + * @param meg + * @param message + * @param channel + */ @RabbitListener(queues = MqConfig.DXQUEUE) public void orderConsumer(String meg, Message message, Channel channel){ log.info("下订单"+meg); long deliveryTag = message.getMessageProperties().getDeliveryTag(); - - orderItemService.save(JSON.parseObject(meg,OrderItemEntity.class)); - + if(meg ==null){ + try { + channel.basicAck(deliveryTag,false); + } catch (IOException e) { + throw new RuntimeException(e); + } + } try { + orderItemService.save(JSON.parseObject(meg,OrderItemEntity.class)); channel.basicAck(deliveryTag,false); } catch (IOException e) { try { diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/TeamworkService.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/TeamworkService.java new file mode 100644 index 0000000..abb2ea1 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/TeamworkService.java @@ -0,0 +1,17 @@ +package com.mall.server.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.mall.common.result.Result; +import com.mall.server.domain.TeamworkEntity; + +import java.util.HashMap; +import java.util.Map; + +/** + *拼团Service + */ +public interface TeamworkService extends IService { + Result groupBooking(Long skuId); + + Result encryption(Long skuId); +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/SpikesServiceImpl.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/SpikesServiceImpl.java index 84d8da0..1697c4a 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/SpikesServiceImpl.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/SpikesServiceImpl.java @@ -12,7 +12,7 @@ import com.mall.common.result.BizException; import com.mall.common.result.Result; import com.mall.common.utils.IdUtils; import com.mall.common.utils.StringUtils; -import com.mall.server.config.DelayConfig; +import com.mall.server.config.SpikesDelayConfig; import com.mall.server.config.MqConfig; import com.mall.server.constant.SpikesConstant; import com.mall.server.domain.SkuEntity; @@ -193,7 +193,7 @@ public class SpikesServiceImpl extends ServiceImpl }); rabbitTemplate.convertAndSend( - DelayConfig.EXCHANGE, DelayConfig.ROUKEYCZK, skuId + "," + orderSn, + SpikesDelayConfig.EXCHANGE, SpikesDelayConfig.ROUKEYCZK, skuId + "," + orderSn, message -> { message.getMessageProperties().setMessageId(IdUtils.genId()); return message; diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/TeamworkServiceImpl.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/TeamworkServiceImpl.java new file mode 100644 index 0000000..8d8e7fc --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/TeamworkServiceImpl.java @@ -0,0 +1,192 @@ +package com.mall.server.service.impl; + +import cn.hutool.crypto.Mode; +import cn.hutool.crypto.Padding; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.symmetric.DES; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.mall.common.constant.TokenConstants; +import com.mall.common.domain.OrderItemEntity; +import com.mall.common.domain.UserInfo; +import com.mall.common.redis.RedisCache; +import com.mall.common.result.BizException; +import com.mall.common.result.Result; +import com.mall.common.utils.IdUtils; +import com.mall.common.utils.SecurityUtils; +import com.mall.common.utils.StringUtils; +import com.mall.server.config.GroupDelayConfig; +import com.mall.server.domain.SkuEntity; +import com.mall.server.domain.TeamworkEntity; +import com.mall.server.domain.TeamworkRecordEntity; +import com.mall.server.domain.vo.GroupBookingVo; +import com.mall.server.mapper.OrderItemMapper; +import com.mall.server.mapper.SkuMapper; +import com.mall.server.mapper.TeamworkMapper; +import com.mall.server.mapper.TeamworkRecordMapper; +import com.mall.server.service.TeamworkService; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.servlet.http.HttpServletRequest; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +/** + * 拼团ServiceImpl + */ +@Service +public class TeamworkServiceImpl extends ServiceImpl + implements TeamworkService { + + @Autowired + private TeamworkMapper teamworkMapper; + + @Autowired + private TeamworkRecordMapper teamworkRecordMapper; + + @Autowired + private RedisCache redisCache; + + @Autowired + private HttpServletRequest request; + + @Autowired + private SkuMapper skuMapper; + + @Autowired + private ApplicationContext applicationContext; + + @Autowired + private OrderItemMapper orderItemMapper; + + @Autowired + private RabbitTemplate rabbitTemplate; + + public UserInfo getLogin() { + String token = request.getHeader(TokenConstants.TOKEN); + if (StringUtils.isBlank(token)) { + throw new BizException(401, "未登录"); + } + UserInfo userInfo = redisCache.getCacheObject(TokenConstants.TOKEN + token); + return userInfo; + } + + @Transactional + @Override + public Result groupBooking(Long skuId) { + UserInfo login = getLogin(); + if (redisCache.hasKey("groupBooking_" + login.getId() + skuId)) { + throw new BizException(500, "禁止重复抢购欧"); + } + redisCache.setCacheObject("groupBooking_" + login.getId() + skuId, IdUtils.genId().toString(), 1L, TimeUnit.MINUTES); + //商品id查询商品表 + SkuEntity skuEntity = skuMapper.selectOne( + new LambdaQueryWrapper() + .eq(SkuEntity::getId, skuId) + ); + //商品的拼图id查询拼团表 + TeamworkEntity teamworkEntity = teamworkMapper.selectOne( + new LambdaQueryWrapper() + .eq(TeamworkEntity::getId, skuEntity.getBargainId()) + ); + //校验拼团时间 + if (teamworkEntity.getCreateDate().compareTo(new Date()) > 0 || teamworkEntity.getEndDate().compareTo(new Date()) < 0) { + throw new BizException(500, skuEntity.getName() + "该商品不在拼团时间内"); + } + //查询拼团记录防止用户拼同一活动同一商品 + TeamworkRecordEntity teamworkRecordEntity = teamworkRecordMapper.selectOne( + new LambdaQueryWrapper() + .eq(TeamworkRecordEntity::getUserId, login.getId()) + .eq(TeamworkRecordEntity::getTeamworkId, teamworkEntity.getId()) + .eq(TeamworkRecordEntity::getSkuId, skuEntity.getId()) + ); + if (teamworkRecordEntity != null) { + throw new BizException(500, "禁止重复拼团"); + } + //根据拼团id和商品id查询拼团记录表几人拼团 + List teamworkRecordEntityList = teamworkRecordMapper.selectList( + new LambdaQueryWrapper() + .eq(TeamworkRecordEntity::getTeamworkId, teamworkEntity.getId()) + .eq(TeamworkRecordEntity::getSkuId, skuEntity.getId()) + ); + //订单信息 + String orderSn = IdUtils.genId(); + OrderItemEntity orderItem = OrderItemEntity.builder() + .userId(login.getId()) + .orderSn(orderSn) + .skuId(skuId) + .skuName(skuEntity.getName()) + .skuPic(skuEntity.getDefaultImage()) + .skuPrice(skuEntity.getPrice()) +// .promotionAmount() +// .couponAmount() +// .integrationAmount() + .realAmount(skuEntity.getActivityPrice()) + .status(0).build(); + //拼团记录表 + TeamworkRecordEntity teamworkRecord = TeamworkRecordEntity.builder() + .teamworkOrderSn(orderSn) + .teamworkId(teamworkEntity.getId()) + .price(skuEntity.getPrice()) + .num(1) + .severalPeopleInvolved(0) + .createTime(new Date()) + .userId(login.getId()) + .skuId(skuEntity.getId()) + .state(0).build();//发起人 + //参与拼团 + if (teamworkRecordEntityList.size() != 0) { + //团满 + if (teamworkRecordEntityList.size() == teamworkEntity.getGroupOfSeveral()) { + throw new BizException(500, "拼团人数已满"); + } + teamworkRecord.setState(1); + applicationContext.publishEvent( + GroupBookingVo.builder() + .orderItemEntity(orderItem) + .teamworkRecordEntity(teamworkRecord).build() + ); + return Result.success(orderSn, "拼团成功"); + } + //拼团发起人 + applicationContext.publishEvent( + GroupBookingVo.builder() + .orderItemEntity(orderItem) + .teamworkRecordEntity(teamworkRecord).build() + ); + //发起拼团之后扣减库存,把一整个拼团的库存全部扣减 + skuMapper.updateById( + SkuEntity.builder() + .id(skuId) + .inventoryRestrict(teamworkEntity.getGroupOfSeveral()) + .build() + ); + //延迟队列48小时后没有成团,取消拼团,并归还库存,支付的退款 + rabbitTemplate.convertAndSend(GroupDelayConfig.EXCHANGE, GroupDelayConfig.ROUKEYCZK, orderSn, + message -> { + message.getMessageProperties().setMessageId(orderSn); + return message; + }); + return Result.success(orderSn, "拼团成功"); + } + + @Override + public Result encryption(Long skuId) { + String encrypt = null; + try { + encrypt = SecurityUtils.encrypt(skuId.toString()); + } catch (Exception e) { + throw new RuntimeException(e); + } + return Result.success("http://localhost:9002/teamwork/groupBooking?skuId=" + encrypt + "&state=1", "加密路径成功"); + } + +}