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 765433e..c887c83 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 @@ -5,7 +5,6 @@ import cn.hutool.crypto.SecureUtil; import com.mall.auth.feign.UserServiceFeign; import com.mall.auth.service.AuthService; import com.mall.common.constant.JwtConstants; -import com.mall.common.constant.RabbitConstants; import com.mall.common.constant.TokenConstants; import com.mall.common.domain.UserInfo; import com.mall.common.domain.request.LoginRequest; @@ -60,7 +59,7 @@ public class AuthServiceImpl implements AuthService { // loginVo.getPassword()+ "|" + data.getSalt() // ); if (!loginVo.getPassword().equals(data.getPassword())){ - throw new BizException("密码错误"); + throw new BizException("密码错误") ; } HashMap map = new HashMap<>(); map.put(JwtConstants.USER_KEY,data.getId()); diff --git a/mall_common/pom.xml b/mall_common/pom.xml index 2002848..fd90e33 100644 --- a/mall_common/pom.xml +++ b/mall_common/pom.xml @@ -65,13 +65,13 @@ org.springframework.boot spring-boot-starter-data-redis - - - - - - - + + + org.redisson + redisson + 3.16.0 + + diff --git a/mall_common/src/main/java/com/mall/common/config/RedisConfig.java b/mall_common/src/main/java/com/mall/common/config/RedisConfig.java index 7bd2bae..65510b9 100644 --- a/mall_common/src/main/java/com/mall/common/config/RedisConfig.java +++ b/mall_common/src/main/java/com/mall/common/config/RedisConfig.java @@ -1,4 +1,7 @@ package com.mall.common.config; +import org.redisson.Redisson; +import org.redisson.api.RedissonClient; +import org.redisson.config.Config; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; @@ -15,6 +18,14 @@ import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { + + @Bean + public RedissonClient redissonClient(){ + Config config = new Config(); + config.useSingleServer().setAddress("redis://124.221.183.9:6379"); + return Redisson.create(config); + } + @Bean @SuppressWarnings(value = {"unchecked", "rawtypes"}) public RedisTemplate redisTemplate (RedisConnectionFactory connectionFactory) { diff --git a/mall_common/src/main/java/com/mall/common/domain/OrderItemEntity.java b/mall_common/src/main/java/com/mall/common/domain/OrderItemEntity.java new file mode 100644 index 0000000..387ba41 --- /dev/null +++ b/mall_common/src/main/java/com/mall/common/domain/OrderItemEntity.java @@ -0,0 +1,112 @@ +package com.mall.common.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; + +/** + *订单明细表 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("oms_order_item") +public class OrderItemEntity { + + /** + * id + */ + @TableId(type = IdType.AUTO) + private Long id; + /** + * 用户id + */ + private Long userId; + /** + * order_id + */ + private Long orderId; + /** + * order_sn + */ + private String orderSn; + /** + * spu_id + */ + private Long spuId; + /** + * spu_name + */ + private String spuName; + /** + * spu_pic + */ + private String spuPic; + /** + * 品牌 + */ + private String spuBrand; + /** + * 商品分类id + */ + private Long categoryId; + /** + * 商品sku编号 + */ + private Long skuId; + /** + * 商品sku名字 + */ + private String skuName; + /** + * 商品sku图片 + */ + private String skuPic; + /** + * 商品sku价格 + */ + private BigDecimal skuPrice; + /** + * 商品购买的数量 + */ + private Integer skuQuantity; + /** + * 商品销售属性组合(JSON) + */ + private String skuAttrsVals; + /** + * 商品促销分解金额 + */ + private BigDecimal promotionAmount; + /** + * 优惠券优惠分解金额 + */ + private BigDecimal couponAmount; + /** + * 积分优惠分解金额 + */ + private BigDecimal integrationAmount; + /** + * 应付金额 + */ + private BigDecimal realAmount; + /** + * 赠送积分 + */ + private Integer giftIntegration; + /** + * 赠送成长值 + */ + private Integer giftGrowth; + /** + *订单状态【0->待付款;1->待发货;2->已发货;3->已完成;4->已关闭;5->无效订单】 + */ + private Integer status; +} diff --git a/mall_common/src/main/java/com/mall/common/domain/vo/ActivitySkuVo.java b/mall_common/src/main/java/com/mall/common/domain/vo/ActivitySkuVo.java index de65718..8b55d52 100644 --- a/mall_common/src/main/java/com/mall/common/domain/vo/ActivitySkuVo.java +++ b/mall_common/src/main/java/com/mall/common/domain/vo/ActivitySkuVo.java @@ -89,4 +89,5 @@ public class ActivitySkuVo { * 秒杀结束时间 */ private Date endTime; + } 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 46710c2..c58d53a 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 @@ -1,11 +1,15 @@ 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.EnableScheduling; /** *server启动类 */ @SpringBootApplication +@EnableScheduling +@EnableFeignClients 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/config/DelayConfig.java b/mall_modules/mall_server/src/main/java/com/mall/server/config/DelayConfig.java new file mode 100644 index 0000000..a6b7570 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/config/DelayConfig.java @@ -0,0 +1,98 @@ +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.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 static final String CZKQUEUE = "czk_queue"; + /** + * 交换机 + */ + public static final String EXCHANGE = "direct"; + /** + * 路由key + */ + public static final String ROUKEYCZK = "roukey_czk_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 = "dead_exchange"; + /** + * 死信队列 + */ + public static final String DEADQUEUE = "dead_queue"; + /** + * 死信路由key + */ + public static final String DEADROUKEY = "dead_dead_queue"; + /** + * 5秒 + */ + public static final Integer MESSAGE_FIVE = 5000; + /** + * 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/MqConfig.java b/mall_modules/mall_server/src/main/java/com/mall/server/config/MqConfig.java new file mode 100644 index 0000000..f9e0383 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/config/MqConfig.java @@ -0,0 +1,58 @@ +package com.mall.server.config; + +import lombok.extern.log4j.Log4j2; +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.connection.ConnectionFactory; +import org.springframework.amqp.rabbit.connection.CorrelationData; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; +import org.springframework.amqp.support.converter.MessageConverter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +/** + * @CLassName MqConfig + * @Description 描述 + * @Author Meng.Wang + * @Date 2023/11/24 21:15 + */ +@Configuration +@Log4j2 +public class MqConfig implements RabbitTemplate.ReturnsCallback,RabbitTemplate.ConfirmCallback{ + public static final String DXQUEUE = "DxQueue"; + public static final String DXEXCHANGE = "DxExchange"; + public static final String ROUTINGKEY = "RoutingKey"; + + + //创建队列 + @Bean + public Queue queue(){ + return new Queue(DXQUEUE,true); + } + + @Bean("DxExchange") + public DirectExchange directExchange(){ + return new DirectExchange(DXEXCHANGE); + } + + @Bean + public Binding binding(){ + return BindingBuilder.bind(queue()).to(directExchange()).with(ROUTINGKEY); + } + + @Override + public void confirm(CorrelationData correlationData, boolean b, String s) { + if(b){ + log.info("{}消息到达交换机",correlationData.getId()); + }else { + log.error("{}消息丢失",correlationData.getId()); + } + } + + @Override + public void returnedMessage(ReturnedMessage returnedMessage) { + log.error("{}消息未到达队列",returnedMessage.getMessage().getMessageProperties().getMessageId()); + } +} + \ No newline at end of file diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/constant/SpikesConstant.java b/mall_modules/mall_server/src/main/java/com/mall/server/constant/SpikesConstant.java new file mode 100644 index 0000000..eb89fea --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/constant/SpikesConstant.java @@ -0,0 +1,11 @@ +package com.mall.server.constant; + +/** + *秒杀常池 + */ +public class SpikesConstant { + + public static final String SPIKES_INVENTORY = "秒杀商品库存数量"; + public static final String SPIKES_SKUID = "秒杀商品Id"; + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/controller/SpikesController.java b/mall_modules/mall_server/src/main/java/com/mall/server/controller/SpikesController.java index d36d1f4..529dbcc 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/controller/SpikesController.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/controller/SpikesController.java @@ -2,11 +2,10 @@ package com.mall.server.controller; import com.mall.common.domain.request.SpikesRequest; import com.mall.common.result.Result; import com.mall.server.service.SpikesService; +import org.redisson.client.RedisClient; 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 org.springframework.web.bind.annotation.*; + /** *秒杀controller */ @@ -17,6 +16,7 @@ public class SpikesController { @Autowired private SpikesService spikesService; + /** * 添加秒杀场次 * @param spikesRequest @@ -27,4 +27,14 @@ public class SpikesController { return spikesService.add(spikesRequest); } + /** + * 秒杀 + * @param skuId + * @return + */ + @GetMapping("spike") + public Result spike(@RequestParam Long skuId){ + return spikesService.spike(skuId); + } + } diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/enumerate/ActivityEnum.java b/mall_modules/mall_server/src/main/java/com/mall/server/enumerate/ActivityEnum.java index 27d87b8..d3dd029 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/enumerate/ActivityEnum.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/enumerate/ActivityEnum.java @@ -1,11 +1,7 @@ package com.mall.server.enumerate; - -import lombok.Data; - /** - * + *活动未开启状态 */ - public enum ActivityEnum { SPIKES(0,"秒杀未开启"), BARGAIN(0,"砍价未开启"), diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/job/SpikesJob.java b/mall_modules/mall_server/src/main/java/com/mall/server/job/SpikesJob.java new file mode 100644 index 0000000..0b264bd --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/job/SpikesJob.java @@ -0,0 +1,65 @@ +package com.mall.server.job; + +import com.alibaba.nacos.client.naming.utils.CollectionUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.mall.common.utils.StringUtils; +import com.mall.server.constant.SpikesConstant; +import com.mall.server.domain.SkuEntity; +import com.mall.server.domain.SpikesEntity; +import com.mall.server.service.SkuService; +import com.mall.server.service.SpikesService; +import org.redisson.api.RSemaphore; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + *同步秒杀商品信息 + */ +@Component +public class SpikesJob { + + @Autowired + private SkuService skuService; + + @Autowired + private SpikesService spikesService; + + @Autowired + private RedissonClient redissonClient; + +// @Scheduled(cron = "0/5 * * * * ? ") + public void spikesJob(){ + Date date = Date.from( + LocalDateTime.now().withHour(0).withMinute(0) + .withSecond(0).withNano(0) + .plusDays(1).atZone(ZoneId.systemDefault()).toInstant() + ); + List list = spikesService.list( + new LambdaQueryWrapper() + .ge(SpikesEntity::getCreateTime, date) + ); + if(CollectionUtils.isEmpty(list)){ + return; + } + list.forEach(c->{ + skuService.list( + new LambdaQueryWrapper() + .eq(c.getSpikesId()!=null,SkuEntity::getSpikesId,c.getSpikesId()) + ).forEach(s -> { + RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + s.getId()); + semaphore.trySetPermitsAsync(s.getInventoryRestrict()); + long expire = (c.getEndTime().getTime() - System.currentTimeMillis()) / 60000; + semaphore.expire(expire, TimeUnit.MINUTES); + }); + }); + } + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/mapper/OrderItemMapper.java b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/OrderItemMapper.java new file mode 100644 index 0000000..fe83b08 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/mapper/OrderItemMapper.java @@ -0,0 +1,12 @@ +package com.mall.server.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.mall.common.domain.OrderItemEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + *订单详细Mapper + */ +@Mapper +public interface OrderItemMapper extends BaseMapper { +} 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 new file mode 100644 index 0000000..2c1c8ed --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/monitor/SpikesMonitor.java @@ -0,0 +1,55 @@ +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.constant.SpikesConstant; +import com.mall.server.service.OrderItemService; +import com.rabbitmq.client.Channel; +import lombok.extern.slf4j.Slf4j; +import org.redisson.api.RSemaphore; +import org.redisson.api.RedissonClient; +import org.springframework.amqp.core.Message; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + *监听秒杀信息下单 + */ +@Component +@Slf4j +public class SpikesMonitor { + + @Autowired + private RedissonClient redissonClient; + + @Autowired + private OrderItemService orderItemService; + + @RabbitListener(queues = DelayConfig.DEADQUEUE) + public void consumer(String meg, Message message, Channel channel){ + log.info("延迟队列接受到消息"+meg); + long deliveryTag = message.getMessageProperties().getDeliveryTag(); + 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 { + channel.basicAck(deliveryTag,false); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/OrderItemService.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/OrderItemService.java new file mode 100644 index 0000000..0ddfd20 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/OrderItemService.java @@ -0,0 +1,10 @@ +package com.mall.server.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.mall.common.domain.OrderItemEntity; + +/** + *订单详细Service + */ +public interface OrderItemService extends IService { +} diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/SpikesService.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/SpikesService.java index d7785ad..93b3ef0 100644 --- a/mall_modules/mall_server/src/main/java/com/mall/server/service/SpikesService.java +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/SpikesService.java @@ -11,4 +11,6 @@ import com.mall.server.domain.SpikesEntity; public interface SpikesService extends IService { Result add(SpikesRequest spikesRequest); + + Result spike(Long skuId); } diff --git a/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/OrderItemServiceImpl.java b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/OrderItemServiceImpl.java new file mode 100644 index 0000000..ee52c54 --- /dev/null +++ b/mall_modules/mall_server/src/main/java/com/mall/server/service/impl/OrderItemServiceImpl.java @@ -0,0 +1,19 @@ +package com.mall.server.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.mall.common.domain.OrderItemEntity; +import com.mall.server.mapper.OrderItemMapper; +import com.mall.server.service.OrderItemService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + *订单详细表ServiceImpl + */ +@Service +public class OrderItemServiceImpl extends ServiceImpl + implements OrderItemService { + + @Autowired + private OrderItemMapper orderItemMapper; +} 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 075ab41..c901865 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 @@ -1,25 +1,39 @@ package com.mall.server.service.impl; -import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson.JSON; 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.domain.request.SpikesRequest; import com.mall.common.domain.vo.ActivitySkuVo; 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.StringUtils; +import com.mall.server.config.DelayConfig; +import com.mall.server.constant.SpikesConstant; import com.mall.server.domain.SkuEntity; import com.mall.server.domain.SpikesEntity; -import com.mall.server.enumerate.ActivityEnum; import com.mall.server.mapper.SpikesMapper; +import com.mall.server.service.OrderItemService; import com.mall.server.service.SkuService; import com.mall.server.service.SpikesService; import com.mall.server.service.SpuService; +import org.redisson.api.RSemaphore; +import org.redisson.api.RedissonClient; +import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.BoundZSetOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.servlet.http.HttpServletRequest; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -34,9 +48,6 @@ public class SpikesServiceImpl extends ServiceImpl @Autowired private SpikesMapper spikesMapper; - @Autowired - private SpuService spuService; - @Autowired private SkuService skuService; @@ -44,11 +55,38 @@ public class SpikesServiceImpl extends ServiceImpl private RedisCache redisCache; @Autowired - private RedisTemplate redisTemplate; + private RedissonClient redissonClient; + @Autowired + private HttpServletRequest request; + + @Autowired + private OrderItemService orderItemService; + + @Autowired + private RabbitTemplate rabbitTemplate; + + + /** + * 添加秒杀信息 + * @param spikesRequest + * @return + */ @Transactional @Override public Result add(SpikesRequest spikesRequest) { + if(spikesRequest.getCreateTime().getMinutes()%60!=0){ + throw new BizException(500,"不是整点"); + } + //一天后的0点 + Date date = Date.from( + LocalDateTime.now().withHour(0).withMinute(0) + .withSecond(0).withNano(0) + .plusDays(1).atZone(ZoneId.systemDefault()).toInstant() + ); + if(spikesRequest.getCreateTime().compareTo(date)<0){ + throw new BizException(500,"时间必须是第二天0点"); + } //秒杀表信息 int spikesId = spikesMapper.insert( SpikesEntity.builder() @@ -96,12 +134,84 @@ public class SpikesServiceImpl extends ServiceImpl .bargainId(c.getBargainId()) .groupId(c.getGroupId()) .startTime(spikesRequest.getCreateTime()) - .endTime(spikesRequest.getEndTime()).build()).collect(Collectors.toList()); + .endTime(spikesRequest.getEndTime()).build() + ).collect(Collectors.toList()); + //后期存入redis放入查询列表中 activitySkuVoList.forEach(c->{ //redis秒杀信息存在时间 - long expire = c.getEndTime().getTime() - System.currentTimeMillis()/1000/1000; - redisCache.setCacheObject("spikes_"+c.getId(),c,expire, TimeUnit.MINUTES); + long expire = (c.getEndTime().getTime() - System.currentTimeMillis()) / 60000; + redisCache.setCacheObject(SpikesConstant.SPIKES_SKUID +c.getId(),c,expire, TimeUnit.MINUTES); }); return Result.success(true,"添加秒杀成功"); } + + /** + * 秒杀 + * @param skuId + * @return + */ + @Override + public Result spike(Long skuId) { + SkuEntity skuEntity = skuService.getOne( + new LambdaQueryWrapper() + .eq(skuId != null, SkuEntity::getId, skuId) + ); + SpikesEntity spikesEntity = spikesMapper.selectOne( + new LambdaQueryWrapper() + .eq(skuEntity.getSpikesId() != null, SpikesEntity::getSpikesId, skuEntity.getSpikesId()) + ); + if(spikesEntity.getCreateTime().compareTo(new Date())>0 || spikesEntity.getEndTime().compareTo(new Date())<0){ + throw new BizException(500,skuEntity.getName()+"该商品不在抢购时间内"); + } + UserInfo login = getLogin(); + OrderItemEntity orderItemEntity = orderItemService.getOne( + new LambdaQueryWrapper() + .eq(OrderItemEntity::getUserId, login.getId()) + .eq(OrderItemEntity::getSkuId,skuId) + ); + if(orderItemEntity!=null){ + throw new BizException(500,"禁止重复抢购欧"); + } + RSemaphore semaphore = redissonClient.getSemaphore(SpikesConstant.SPIKES_INVENTORY + skuId); +// if(semaphore.availablePermits()==0){ +// //同步数据库库存量 +// throw new BizException(500,"商品已抢购完"); +// } + if(!semaphore.tryAcquire(1)){ + throw new BizException(500,"系统繁忙,商品已抢购完"); + } + String orderSn = IdUtils.genId(); + orderItemService.save( + 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() + ); + rabbitTemplate.convertAndSend( + DelayConfig.EXCHANGE, DelayConfig.ROUKEYCZK, skuId + "," +orderSn , + message -> { + message.getMessageProperties().setMessageId(IdUtils.genId()); + return message; + } + ); + return Result.success(skuId,"下单成功"); + } + + public UserInfo getLogin(){ + String token = request.getHeader(TokenConstants.LOGIN_TOKEN_KEY); + if (StringUtils.isBlank(token)){ + throw new BizException(401,"未登录"); + } + UserInfo userInfo = redisCache.getCacheObject(TokenConstants.LOGIN_TOKEN_KEY + token); + return userInfo; + } + } diff --git a/mall_modules/mall_server/src/main/resources/bootstrap.yml b/mall_modules/mall_server/src/main/resources/bootstrap.yml index 5e8a069..7046b82 100644 --- a/mall_modules/mall_server/src/main/resources/bootstrap.yml +++ b/mall_modules/mall_server/src/main/resources/bootstrap.yml @@ -12,7 +12,7 @@ spring: listener: simple: # 消息确认模式,这里设置为手动确认(manual),即消费者需要手动确认消息的消费 - acknowledge-mode: auto + acknowledge-mode: manual main: allow-circular-references: true jackson: diff --git a/mall_modules/mall_system/src/main/java/com/mall/system/controller/UserController.java b/mall_modules/mall_system/src/main/java/com/mall/system/controller/UserController.java index 8e396c7..afb3cb1 100644 --- a/mall_modules/mall_system/src/main/java/com/mall/system/controller/UserController.java +++ b/mall_modules/mall_system/src/main/java/com/mall/system/controller/UserController.java @@ -27,8 +27,7 @@ public class UserController { @PostMapping("findLogin/{username}/{password}") public Result findLogin(@PathVariable String username, @PathVariable String password){ LambdaQueryWrapper userInfoLambdaQueryWrapper = new LambdaQueryWrapper<>(); - userInfoLambdaQueryWrapper.eq(UserInfo::getUsername,username) - .eq(UserInfo::getPassword,password); + userInfoLambdaQueryWrapper.eq(UserInfo::getUsername,username); UserInfo userInfo = userService.getOne(userInfoLambdaQueryWrapper); return Result.success(userInfo); } diff --git a/mall_modules/mall_system/src/main/java/com/mall/system/mapper/UserMapper.java b/mall_modules/mall_system/src/main/java/com/mall/system/mapper/UserMapper.java index 8f02902..7e1b5c5 100644 --- a/mall_modules/mall_system/src/main/java/com/mall/system/mapper/UserMapper.java +++ b/mall_modules/mall_system/src/main/java/com/mall/system/mapper/UserMapper.java @@ -2,7 +2,7 @@ package com.mall.system.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.mall.common.domain.UserInfo; -import org.mapstruct.Mapper; +import org.apache.ibatis.annotations.Mapper; /** * @Author: lzh