微服务秒杀项目整合网关+feign+redis分离热点商品分别下单示例


思路:对于普通商品进行正常下单(直接减去数据库中的库存然后生成订单,对于热点数据加一层redis缓存层,商品放在redis中,用分布式锁加锁,然后可以放入消息队列中排队下单,下单时先检查Redis中库存量是否足够,成功了才减去数据库库存)
在这里还使用了feign用来远程调用服务和网关拦截请求。

下面是代码:/
在这里插入图片描述

配置文件和通用文件

@Configuration
public class RedisConfig {

    /***
     * 模板操作对象序列化设置
     * @param redissonConnectionFactory
     * @return
     */
    @Bean("redisTemplate")
    public RedisTemplate getRedisTemplate(RedisConnectionFactory redissonConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redissonConnectionFactory);
        redisTemplate.setValueSerializer(valueSerializer());
        redisTemplate.setKeySerializer(keySerializer());
        redisTemplate.setHashKeySerializer(keySerializer());
        redisTemplate.setHashValueSerializer(valueSerializer());
        return redisTemplate;
    }

    /****
     * 序列化设置
     * @return
     */
    @Bean
    public StringRedisSerializer keySerializer() {
        return new StringRedisSerializer();
    }

    /****
     * 序列化设置
     * @return
     */
    @Bean
    public RedisSerializer valueSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }
}

通用结果类型:

@ApiModel(description = "Result",value = "Result")
public class Result<T> {

    @ApiModelProperty(value="执行是否成功,true:成功,false:失败",required = true)
    private boolean flag;//是否成功
    @ApiModelProperty(value="返回状态码,20000:成功,20001:失败,20002:用户名或密码错误,20003:权限不足,20004:远程调用失败,20005:重复操作,20006:没有对应的数据",required = true)
    private Integer code;//返回码

    @ApiModelProperty(value="提示信息",required = true)
    private String message;//返回消息
    @ApiModelProperty(value="逻辑数据",required = true)
    private T data;//返回数据

    public Result(boolean flag, Integer code, String message, Object data) {
        this.flag = flag;
        this.code = code;
        this.message = message;
        this.data = (T) data;
    }

    public Result(boolean flag, Integer code, String message) {
        this.flag = flag;
        this.code = code;
        this.message = message;
    }

    public Result() {
        this.flag = true;
        this.code = StatusCode.OK;
        this.message = "操作成功!";
    }

返回码:


/**
 * 返回码
 */
public class StatusCode {
    public static final int OK = 20000;//成功
    public static final int ERROR = 20001;//失败
    public static final int LOGINERROR = 20002;//用户名或密码错误
    public static final int ACCESSERROR = 20003;//权限不足
    public static final int REMOTEERROR = 20004;//远程调用失败
    public static final int REPERROR = 20005;//重复操作
    public static final int NOTFOUNDERROR = 20006;//没有对应的抢购数据


    //库存递减状态码
    public static final int DECOUNT_1=1;    //递减成功
    public static final int DECOUNT_NUM=405;//库存不足
    public static final int DECOUNT_HOT=205;//商品是热卖商品
    public static final int DECOUNT_OK=200;//库存递减成功
    public static final int ORDER_QUEUE=202;//抢购商品正在排队
    public static final int ORDER_OK=200;//抢单成功

    //令牌无效
    public static final int TOKEN_ERROR=401;
}

统一异常处理:

@ControllerAdvice   //所有请求路径,都将被该类处理->过滤器/(拦截器)
public class BaseExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(BaseExceptionHandler.class);

    /***
     * 异常处理
     * 当前请求发生了指定异常,则执行该方法处理异常
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public Result error(Exception ex){
        StringWriter stringWriter = new StringWriter();
        PrintWriter writer = new PrintWriter(stringWriter);
        ex.printStackTrace(writer);
        ex.printStackTrace();
        logger.error(stringWriter.toString());
        return new Result(false, StatusCode.ERROR,ex.getMessage(),stringWriter.toString());
    }
}

网关

server:
  port: 8001
spring:
  application:
    name: gateway-web
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: nacos-server:8848
      discovery:
        #Nacos的注册地址
        server-addr: nacos-server:8848
    gateway:
      routes:
            #商品
            - id: goods_route
              uri: lb://seckill-goods
              predicates:
              - Path=/api/skuAct/**,/api/activity/**,/api/brand/**,/api/category/**,/api/seckillTime/**,/api/sku/**
              filters:
              - StripPrefix=1
            #订单
            - id: order_route
              uri: lb://seckill-order
              predicates:
              - Path=/api/order/**
              filters:
              - StripPrefix=1
            #搜索
            - id: search_route
              uri: lb://seckill-search
              predicates:
              - Path=/api/search/**
              filters:
              - StripPrefix=1
            #用户
            - id: user_route
              uri: lb://seckill-user
              predicates:
              - Path=/api/user/**
              filters:
              - StripPrefix=1
            #管理员
            - id: manager_route
              uri: lb://seckill-manager
              predicates:
              - Path=/api/admin/**
              filters:
              - StripPrefix=1
            #静态页
            - id: page_route
              uri: lb://seckill-page
              predicates:
              - Path=/api/page/**
              filters:
              - StripPrefix=1

网关拦截设置:

@Component
public class AuthorizeFilter implements GlobalFilter,Ordered {

    //令牌头名字
    private static final String AUTHORIZE_TOKEN = "Authorization";
    private static final String ADMINAUTHORIZE_TOKEN = "Admin-Token-Itheima";

    /***
     * 过滤拦截
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取request和response
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        //获取用户请求的地址
        String path = request.getURI().getPath();

        // /api/user/login放行
        ///api/order/add 测试放行
        if(path.equals("/api/user/login") || path.equals("/api/admin/login") || path.equals("/api/search") || path.equals("/api/activity/times")){
            //放行
            return chain.filter(exchange);
        }
        HttpMethod method = request.getMethod();
        System.out.println(method.name());
        // /sku/xxx   GET方式允许通过
        if(path.startsWith("/api/sku/") && request.getMethod().name().equalsIgnoreCase("GET")){
            //放行
            return chain.filter(exchange);
        }

        //获取用户请求头中的令牌
        String token = request.getHeaders().getFirst(AUTHORIZE_TOKEN); //获取请求头中第1个Authorization参数

        //如果请求头中没有令牌,则有可能用的是参数传入的
        if(StringUtils.isEmpty(token)){
            token = request.getQueryParams().getFirst(AUTHORIZE_TOKEN);//获取请求参数中第1个Authorization
        }

        //如果请求头和参数中都没有令牌,则直接拒绝用户访问各大微服务
        if(StringUtils.isEmpty(token)){
            //从Cookie中获取令牌数据
            HttpCookie cookie = request.getCookies().getFirst(AUTHORIZE_TOKEN);
            HttpCookie adminCookie = request.getCookies().getFirst(ADMINAUTHORIZE_TOKEN);
            if(cookie==null && adminCookie==null){
                //状态吗  401
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                //结束当前请求
                return response.setComplete();
            }
            //获取令牌
            if(cookie!=null){
                token = cookie.getValue();
            }else{
                token = adminCookie.getValue();
            }
            //将令牌封装到请求头中
            request.mutate().header(AUTHORIZE_TOKEN,"bearer "+token);
        }
        return chain.filter(exchange);
    }

    /***
     * 排序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
 @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        // cookie跨域
        config.setAllowCredentials(Boolean.TRUE);   //允许Cookie跨域
        config.addAllowedMethod("*");   //所有提交方法都允许跨域
        config.addAllowedOrigin("*");   //所有的域名都允许跨域
        config.addAllowedHeader("*");

        //跨域解析器
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config); //所有请求路径都支持跨域
        return new CorsWebFilter(source);
    }
}

feign服务

下面是库存的接口:

@FeignClient(value = "seckill-goods")
public interface SkuFeign {

    /****
     * 库存递减
     */
    @PutMapping(value = "/sku/dcount/{id}/{count}")
    Result<Sku> dcount(@PathVariable(value = "id")String id,@PathVariable(value = "count")Integer count);

    /***
     * 热点商品隔离
     */
    @PostMapping(value = "/sku/hot/isolation")
    Result hotIsolation(@RequestParam List<String> ids);

    /****
     * 分页查询-查询总数量
     */
    @GetMapping(value = "/sku/count")
    Integer count();


    /****
     * 分页查询集合列表
     */
    @GetMapping(value = "/sku/list/{page}/{size}")
    List<Sku> list(@PathVariable(value = "page")Integer page,@PathVariable(value = "size")Integer size);
    
    /***
     * 根据ID查询Sku数据
     * @param id
     * @return
     */
    @GetMapping("/sku/{id}")
    Result<Sku> findById(@PathVariable String id);

    /***
     * 商品数据归0
     */
    @GetMapping(value = "/sku/zero/{id}")
    Result zero(@PathVariable(value = "id") String id);
}

消息通知的接口:

@FeignClient(value = "seckill-message")
public interface MessageFeign {

    /****
     * 发送消息
     */
    @GetMapping(value = "/message/{userid}")
    String send(@PathVariable(value = "userid")String userid,@RequestParam(value = "msg") String msg) throws IOException;
}

redission配置

多个用户实现加锁操作,只允许有一个用户可以获取到对应锁

@Component
public class RedissonDistributedLocker implements DistributedLocker {

    @Autowired
    private RedissonClient redissonClient;

    /***
     * 加锁,会一直循环加锁,直到拿到锁
     * @param lockkey
     * @return
     */
    @Override
    public RLock lock(String lockkey) {
        RLock lock = redissonClient.getLock(lockkey);
        lock.lock();
        return lock;
    }

    /***
     * 加锁,在指定时间内拿不到锁就会放弃
     * @param lockkey
     * @return
     */
    @Override
    public RLock lock(String lockkey, long timeout) {
        RLock lock = redissonClient.getLock(lockkey);
        lock.lock(timeout,TimeUnit.SECONDS);
        return lock;
    }

    /***
     * 加锁,在指定时间内拿不到锁就会放弃
     * @param lockkey
     * @return
     */
    @Override
    public RLock lock(String lockkey, long timeout, TimeUnit unit) {
        return null;
    }

    /***
     * 加锁,在指定时间内拿不到锁就会放弃,如果拿到锁,锁最终有效时间为leasetime
     * @param lockkey
     * @return
     */
    @Override
    public boolean tryLock(String lockkey, long timeout, long leasetime, TimeUnit unit) {
        return false;
    }

    /****
     * 解锁
     * @param lockkey
     */
    @Override
    public void unLock(String lockkey) {
        RLock lock = redissonClient.getLock(lockkey);
        lock.unlock();
    }

    /***
     * 解锁
     * @param lock
     */
    @Override
    public void unLocke(RLock lock) {
        lock.unlock();
    }
}

controller

商品SkuController

@RestController
@RequestMapping("/sku")

public class SkuController {
   @Autowired
    private SkuService skuService;
/****
     * 库存递减
     */
    @PutMapping(value = "/dcount/{id}/{count}")
    public Result<Sku> dcount(@PathVariable(value = "id")String id,@PathVariable(value = "count")Integer count){
        //1.调用业务层实现递减
        int code = skuService.dcount(id, count);
        String message="";
        Sku sku = null;
        switch (code){
            case StatusCode.DECOUNT_OK:
                sku = skuService.findById(id);
                message="库存递减成功!";
                break;
            case StatusCode.DECOUNT_NUM:
                message="库存不足!";
                break;
            case StatusCode.DECOUNT_HOT:
                message="商品是热点商品!";
                break;
                default:
        }
        //3.根据状态码,响应不同的提示信息
        return  new Result<Sku>(true,code,message,sku);
    }

    /***
     * 热点商品隔离
     */
    @PostMapping(value = "/hot/isolation")
    public Result hotIsolation(@RequestParam List<String> ids){
        for (String id : ids) {
            skuService.hotIsolation(id);
        }
        return new Result(true,StatusCode.OK,"热点数据隔离成功!");
    }

订单OrderController:


@RestController
@RequestMapping("/order")
//@CrossOrigin
public class OrderController {

    @Autowired
    private OrderService orderService;

    @Autowired
    private IdWorker idWorker;


    /****
     * 添加订单
     */
    @PostMapping(value = "/add/{id}")
    public Result add(@PathVariable(value = "id") String id, @RequestHeader(value = "Authorization") String authorization) {
        String username = null;
        try {
            //解析令牌
            Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);
            username =tokenMap.get("username").toString();
        } catch (Exception e) {
            return new Result(false, StatusCode.TOKEN_ERROR, "令牌无效!");
        }
        //封装Order
        Order order = new Order();
        order.setId("No"+idWorker.nextId());
        order.setSkuId(id);
        order.setCreateTime(new Date());
        order.setUpdateTime(order.getCreateTime());
        order.setUsername(username);
        order.setTotalNum(1);
        //添加订单
        int code = orderService.add(order);
        switch (code) {
            case StatusCode.ORDER_OK:
                return new Result(true, StatusCode.ORDER_OK, order.getId());
            case StatusCode.DECOUNT_NUM:
                return new Result(false, StatusCode.DECOUNT_NUM, "库存不足!");
            case StatusCode.ORDER_QUEUE:
                return new Result(true, StatusCode.ORDER_QUEUE, "排队抢购中!");
            default:
                return new Result(false, StatusCode.ERROR, "抢单发生异常!");
        }
    }

    /***
     * Order分页条件搜索实现
     * @param page
     * @param size
     * @return
     */
    @PostMapping(value = "/search/{page}/{size}")
    public Result<PageInfo> findPage(@RequestBody(required = false) OrderVo orderVo, @PathVariable int page, @PathVariable int size) {
        //调用OrderService实现分页条件查询Order
        Order order = new Order();
        BeanUtils.copyProperties(orderVo,order);
        PageInfo<Order> pageInfo = orderService.findPage(order, page, size);
        return new Result(true, StatusCode.OK, "查询成功", pageInfo);
    }


    /***
     * 用户订单
     * @param page
     * @param size
     * @return
     */
    @GetMapping(value = "/user/{page}/{size}")
    public Result<PageInfo> userOrders(@PathVariable int page,
                                       @PathVariable int size,
                                       @RequestParam(value = "type",defaultValue = "0")Integer type,
                                       @RequestHeader("Authorization")String authorization) {
        Map<String, Object> userMap = JwtTokenUtil.parseToken(authorization);
        //调用OrderService实现分页条件查询Order
        Order order = new Order();
        order.setUsername(userMap.get("username").toString());
        switch (type){
            case 1:
                order.setPayStatus("0");
                break;
            case 3:
                order.setPayStatus("1");
                break;
        }
        PageInfo<Order> pageInfo = orderService.findPage(order, page, size);
        return new Result(true, StatusCode.OK, "查询成功", pageInfo);
    }

}

service:热点商品和普通商品分开下单

这里用了druid工具进行热点数据监控:

@Component
public class MonitorItemsAccess {

    @Value("${druidurl}")
    private String druidurl;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private DruidDataSource dataSource;

    /******
     * 定义热点数据标准:
     *      1.某一件商品访问量>N
     *      2.最近N小时
     */
    public List<String> loadData() throws Exception{
        //获取连接对象
        //Connection connection = (AvaticaConnection) DriverManager.getConnection(druidurl);
        Connection connection =dataSource.getConnection();
        //Statement
        Statement statement = connection.createStatement();

        //执行查询
        ResultSet resultSet = statement.executeQuery(druidSQL());

        //解析结果集
        List<String> ids = new ArrayList<String>();
        while (resultSet.next()){
            String uri = resultSet.getString("uri");
            uri=uri.replace("/web/items/","").replace(".html","");
            ids.add(uri);
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
        return ids;
    }


    /***
     * SQL组装
     * @return
     */
    public String druidSQL(){
        //SQL语句
        String prefix="SELECT COUNT(*) AS \"viewCount\",uri FROM logsitems WHERE __time>=CURRENT_TIMESTAMP - INTERVAL '1' HOUR";
        //后部分
        String suffix=" GROUP BY uri HAVING viewCount>2";

        //SQL中间部分  AND uri NOT IN ('/web/items/S1235433012716498944.html')
        //SKU_S1235433012716498944
        String sql = "";

        //基于Redis中存的热点商品的key来过滤排除要查询的数据
        Set<String> keys = redisTemplate.keys("SKU_*");//所有以SKU_开始的key全部查询出来
        if(keys!=null && keys.size()>0){
            sql=" AND uri NOT IN (";
            for (String key : keys) {
                sql+="'/web/items/"+key.substring(4)+".html',";
            }
            sql=sql.substring(0,sql.length()-1);
            sql+=")";
        }
        return prefix+sql+suffix;
    }
}

然后用定时任务组件来执行上面的监控方法,设置每隔多久执行一次,这里用的是elasticjob第三方工具,实际当中可以用其他的定时任务框架



import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.dangdang.elasticjob.lite.annotation.ElasticSimpleJob;
import com.seckill.goods.feign.SkuFeign;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
@Component
@ElasticSimpleJob(//这是elasticjob工具的定时任务注解
        cron = "1/5 * * * * ?",
        jobName = "monitortask",
        shardingTotalCount = 1
)
public class MonitorTask implements SimpleJob{

    @Autowired
    private MonitorItemsAccess monitorItemsAccess;

    @Autowired
    private SkuFeign skuFeign;

    /***
     * 执行业务逻辑
     * @param shardingContext
     */
    @SneakyThrows
    @Override
    public void execute(ShardingContext shardingContext) {
        List<String> ids = monitorItemsAccess.loadData();
        for (String id : ids) {
            System.out.println("热点商品ID:"+id);
        }

        //热点数据隔离
        skuFeign.hotIsolation(ids);
    }
}

订单Service

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private SkuFeign skuFeign;

    @Autowired
    private IdWorker idWorker;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private RedissonDistributedLocker redissonDistributedLocker;

    @Autowired
    private MessageFeign messageFeign;

    /****
     * 热点商品下单
     * @param orderMap
     * @return
     */
    @Override
    public void hotAdd(Map<String, String> orderMap) throws IOException {
        //消息封装对象
        Map<String,Object> messageMap = new HashMap<String,Object>();

        String username = orderMap.get("username");
        String id = orderMap.get("id");

        //Redis中对应的key
        String key="SKU_"+id;
        String lockkey="LOCKSKU_"+id;
        String userKey="USER"+username+"ID"+id;

        //如果key在redis缓存,则表示商品信息在Redis中进行操作
        boolean bo =true;
        // redissonDistributedLocker.tryLock(lockkey, 10, 10, TimeUnit.MINUTES);
        if(bo){
            if(redisTemplate.hasKey(key)){
                //获取商品数量
                Integer num = Integer.parseInt(redisTemplate.boundHashOps(key).get("num").toString());

                if(num<=0){
                    //商品售罄通知
                    messageMap.put("code",20001);
                    messageMap.put("message","商品已售罄");
                    messageFeign.send(username,JSON.toJSONString(messageMap));
                    return;
                }
                Result<Sku> skuResult =skuFeign.findById(id);
                Sku sku = skuResult.getData();

                //1.创建Order
                Order order = new Order();
                order.setTotalNum(1);
                order.setCreateTime(new Date());
                order.setUpdateTime(order.getCreateTime());
                order.setId("No"+idWorker.nextId());
                order.setOrderStatus("0");
                order.setPayStatus("0");
                order.setConsignStatus("0");
                order.setSkuId(id);
                order.setName(sku.getName());
                order.setPrice(sku.getSeckillPrice()*order.getTotalNum());
                orderMapper.insertSelective(order);

                //2.Redis中对应的num递减
                num--;
                if(num==0){
                    skuFeign.zero(id);
                }

                //2.清理用户排队信息
                Map<String,Object> allMap = new HashMap<String,Object>();
                allMap.put(userKey,0);
                allMap.put("num",num);
                redisTemplate.boundHashOps(key).putAll(allMap);
                //3.记录用户购买过该商品,24小时后过期
                redisTemplate.boundValueOps(userKey).set("");
                redisTemplate.expire(userKey,1,TimeUnit.MINUTES);

                //抢单成功通知
                messageMap.put("code",200);
                messageMap.put("message","抢单成功!");
                messageFeign.send(username,JSON.toJSONString(messageMap));
            }

            //释放锁
            //redissonDistributedLocker.unLock(lockkey);
            return;
        }

        //抢单失败通知
        messageMap.put("code",20001);
        messageMap.put("message","抢单失败!");
        messageFeign.send(username,JSON.toJSONString(messageMap));
    }

    /***
     * 添加订单
     * @param order
     * @return
     */
    @GlobalTransactional
    @Override
    public int add(Order order) {
        String userKey="USER"+order.getUsername()+"ID"+order.getSkuId();
        //1.递减库存
        Result<Sku> dcount = skuFeign.dcount(order.getSkuId(), order.getTotalNum());
        //2.递减成功->下单->记录当前用户抢单的时间点,24小时内不能抢购该商品
        if(dcount.getCode()== StatusCode.DECOUNT_OK){
            //int q=10/0;
            Sku sku = dcount.getData();
            //下单
            //order.setId("No"+idWorker.nextId());
            order.setOrderStatus("0");
            order.setPayStatus("0");
            order.setConsignStatus("0");
            order.setSkuId(sku.getId());
            order.setName(sku.getName());
            order.setPrice(sku.getSeckillPrice()*order.getTotalNum());
            orderMapper.insertSelective(order);
            //记录当前用户抢单的时间点,24小时内不能抢购该商品
            redisTemplate.boundValueOps(userKey).set("");
            redisTemplate.boundValueOps(userKey).expire(1, TimeUnit.MINUTES);
            return StatusCode.ORDER_OK;
        }else{
            //3.递减失败
            //405库存不足
            if(dcount.getCode()==StatusCode.DECOUNT_NUM){
                return StatusCode.DECOUNT_NUM;
            }else if(dcount.getCode()==StatusCode.DECOUNT_HOT){
                //205商品热点
                String key = "SKU_"+order.getSkuId();
                Long increment = redisTemplate.boundHashOps(key).increment(userKey, 1);
                if(increment==1){
                    //执行排队
                    Map<String,String> queueMap = new HashMap<String,String>();
                    queueMap.put("username",order.getUsername());
                    queueMap.put("id",order.getSkuId());
                    kafkaTemplate.send("neworder", JSON.toJSONString(queueMap));
                }
                return StatusCode.ORDER_QUEUE;
            }

            //0
            return dcount.getCode();
        }
}

消息队列监听订单

@Component
public class RabbitOrderListener {

    @Autowired
    private OrderService orderService;

    /***
     * 订单消费
     * @param message
     */
    @RabbitListener(queues = PayOrderMchNotifyMQ.MQ_NAME)
    public void getMessage(String message) throws IOException {
        //下单信息
        Map<String,String> orderMap = JSON.parseObject(message,Map.class);
        //热点商品下单
        orderService.hotAdd(orderMap);
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值