commit
e51c3ed307
|
@ -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;
|
||||
|
|
|
@ -66,11 +66,11 @@
|
|||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<!-- Redis分布式锁 -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.redisson</groupId>-->
|
||||
<!-- <artifactId>redisson</artifactId>-->
|
||||
<!-- <version>3.16.0</version>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.redisson</groupId>
|
||||
<artifactId>redisson</artifactId>
|
||||
<version>3.16.0</version>
|
||||
</dependency>
|
||||
<!-- Redis 事务-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>redis.clients</groupId>-->
|
||||
|
|
|
@ -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<Object, Object> redisTemplate (RedisConnectionFactory connectionFactory) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -89,4 +89,5 @@ public class ActivitySkuVo {
|
|||
* 秒杀结束时间
|
||||
*/
|
||||
private Date endTime;
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<String, Object> 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);
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
package com.mall.server.enumerate;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
*
|
||||
*活动未开启状态
|
||||
*/
|
||||
|
||||
public enum ActivityEnum {
|
||||
SPIKES(0,"秒杀未开启"),
|
||||
BARGAIN(0,"砍价未开启"),
|
||||
|
|
|
@ -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<SpikesEntity> list = spikesService.list(
|
||||
new LambdaQueryWrapper<SpikesEntity>()
|
||||
.ge(SpikesEntity::getCreateTime, date)
|
||||
);
|
||||
if(CollectionUtils.isEmpty(list)){
|
||||
return;
|
||||
}
|
||||
list.forEach(c->{
|
||||
skuService.list(
|
||||
new LambdaQueryWrapper<SkuEntity>()
|
||||
.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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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<OrderItemEntity> {
|
||||
}
|
|
@ -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<OrderItemEntity>()
|
||||
.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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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<OrderItemEntity> {
|
||||
}
|
|
@ -11,4 +11,6 @@ import com.mall.server.domain.SpikesEntity;
|
|||
public interface SpikesService extends IService<SpikesEntity> {
|
||||
|
||||
Result add(SpikesRequest spikesRequest);
|
||||
|
||||
Result spike(Long skuId);
|
||||
}
|
||||
|
|
|
@ -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<OrderItemMapper, OrderItemEntity>
|
||||
implements OrderItemService {
|
||||
|
||||
@Autowired
|
||||
private OrderItemMapper orderItemMapper;
|
||||
}
|
|
@ -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<SpikesMapper, SpikesEntity>
|
|||
@Autowired
|
||||
private SpikesMapper spikesMapper;
|
||||
|
||||
@Autowired
|
||||
private SpuService spuService;
|
||||
|
||||
@Autowired
|
||||
private SkuService skuService;
|
||||
|
||||
|
@ -44,11 +55,38 @@ public class SpikesServiceImpl extends ServiceImpl<SpikesMapper, SpikesEntity>
|
|||
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<SpikesMapper, SpikesEntity>
|
|||
.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<SkuEntity>()
|
||||
.eq(skuId != null, SkuEntity::getId, skuId)
|
||||
);
|
||||
SpikesEntity spikesEntity = spikesMapper.selectOne(
|
||||
new LambdaQueryWrapper<SpikesEntity>()
|
||||
.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<OrderItemEntity>()
|
||||
.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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ spring:
|
|||
listener:
|
||||
simple:
|
||||
# 消息确认模式,这里设置为手动确认(manual),即消费者需要手动确认消息的消费
|
||||
acknowledge-mode: auto
|
||||
acknowledge-mode: manual
|
||||
main:
|
||||
allow-circular-references: true
|
||||
jackson:
|
||||
|
|
|
@ -27,8 +27,7 @@ public class UserController {
|
|||
@PostMapping("findLogin/{username}/{password}")
|
||||
public Result<UserInfo> findLogin(@PathVariable String username, @PathVariable String password){
|
||||
LambdaQueryWrapper<UserInfo> 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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue