SpringCloud+RabbitMQ微服务订单系统

1.下单的时候,调用订单微服务系统的下单方法,首先,先给订单实体赋值,然后在向MQ发送消息,接收到MQ返回来的消息后,保存订单,在生成微信或支付宝的签名字符串

2.下单(购买)接口

@PostMapping("/payGoods")
    public Result payGoods(@RequestBody OrderSGoods orderSGoods, HttpServletRequest request) throws ParserConfigurationException, IOException, SAXException {
        //得到用户id
        Result result = jwtUtil.getUserId() ;
        if(!result.isFlag()){
            return result ;
        }
        //用户id
        String userId = (String)result.getData() ;
        System.out.println(orderSGoods);
        //判断参数是否为空
        if(orderSGoods==null){
            return ResultUtil.error(StatusCode.ERROR,"对象不能为空") ;
        }
        if(StringUtils.isEmpty(orderSGoods.getArea())|| StringUtils.isEmpty(orderSGoods.getCity())
                || StringUtils.isEmpty(orderSGoods.getDetail())|| StringUtils.isEmpty(orderSGoods.getMobile())
                || StringUtils.isEmpty(orderSGoods.getProvince())|| StringUtils.isEmpty(orderSGoods.getStreet())
                || StringUtils.isEmpty(orderSGoods.getUserName())||orderSGoods.getOrderShopModels().size()<1
                ||orderSGoods.getTotalNum()<1||orderSGoods.getTotalPrice()<1|| StringUtils.isEmpty(orderSGoods.getChannel())){
            return ResultUtil.error(StatusCode.ERROR,"参数不能为空") ;
        }
        //TODO 判断库存是否充足,从缓存中得库存

        /****************赋值****************/
        Date date = new Date() ;
        //订单商家表
        List orderShops = orderSGoods.getOrderShopModels() ;
        //订单个数,前期只有一个订单
        int len = orderShops.size() ;
        List orderSaves = new ArrayList<>();
        //TODO 订单总金额
        Double money = orderSGoods.getTotalPrice(); ;
        //订单编号
        String orderNo = Long.toString(idWorker.nextId())+"0";
        for(int i =0;i goodsModels = orderShopModel.getGoodsModels() ;
            if(goodsModels.size()<1){
                return ResultUtil.error(StatusCode.ERROR,"商品信息不能为空");
            }

            //商品详情表
            List orderDetails = new ArrayList<>() ;
            for(GoodsModel o : goodsModels){
                OrderDetail orderDetail = new OrderDetail() ;
                orderDetail.setId(Long.toString(idWorker.nextId()));
                orderDetail.setGenTime(date);
                orderDetail.setImages(o.getImages());
                orderDetail.setNum(o.getNum());
                orderDetail.setOrderId(id);
                orderDetail.setPrice(o.getPrice());
                orderDetail.setSkuId(o.getSkuId());
                orderDetail.setTitle(o.getTitle());
                orderDetail.setSpuId(o.getSpuId());
                orderDetail.setDes(o.getDes());
                orderDetails.add(orderDetail) ;
            }

            //订单地址
            OrderAddress orderAddress = new OrderAddress() ;
            orderAddress.setId(Long.toString(idWorker.nextId()));
            orderAddress.setArea(orderSGoods.getArea());
            orderAddress.setCity(orderSGoods.getCity());
            orderAddress.setUserName(orderSGoods.getUserName());
            orderAddress.setDetail(orderSGoods.getDetail());
            orderAddress.setGenTime(date);
            orderAddress.setMobile(orderSGoods.getMobile());
            orderAddress.setOrderId(id);
            orderAddress.setProvince(orderSGoods.getProvince());
            orderAddress.setStreet(orderSGoods.getStreet());
            orderAddress.setUpdateTime(date);
            OrderSave orderSave = new OrderSave() ;
            orderSave.setOrder(order);
            orderSave.setOrderAddress(orderAddress);
            orderSave.setOrderDetails(orderDetails);
            orderSave.setOrderShop(orderShop);
            orderSaves.add(orderSave) ;

            //向RabbitMQ发送消息,然后在保存到数据库中;这样可以更好的应对大量订单的情况,做到缓冲的作用,提高系统响应
            orderSender.send(JSON.toJSONString(orderSaves));
            //TODO 计算金额,根据商品类型id去查询商品,得到商品金额

        }

        //TODO 修改库存,在下订单的时候就修改库存,这样在支付成功后回调那,就不用再向商品系统发送消息去修改库存了
        // 支付渠道渠道
        String channel = orderSGoods.getChannel();

        //生成支付宝、微信签名
        Result rs = getSign(channel,orderNo,money,request);
        return rs;
    }

3.向MQ发送消息

package com.laxun.order.mq.order;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.laxun.order.model.OrderSave;
import com.laxun.order.mq.configure.CommonOrderMq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;

/** 生成订单发送消息 */
@Component
@Slf4j
public class OrderSender implements RabbitTemplate.ConfirmCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 发送消息
     * @param  msg 订单内容,是json字符串
     *  */
    @Transactional
    public void send(String msg) {
        System.out.println(">>>>>>订单微服务:向MQ发送消息>>>>>>" + msg);
        ArrayList orderSaves  = JSON.parseObject(msg, new TypeReference>(){});
        // 封装消息
        Message message = MessageBuilder.withBody(msg.getBytes())
                .setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8").setMessageId(orderSaves.get(0).getOrder().getId())
                .build();
        // 构建回调返回的数据(消息id)
        this.rabbitTemplate.setMandatory(true);
        this.rabbitTemplate.setConfirmCallback(this);
        CorrelationData correlationData = new CorrelationData(msg);
        rabbitTemplate.convertAndSend(CommonOrderMq.ORDER_EXCHANGE, CommonOrderMq.ORDER_ROUTING_KEY, message, correlationData);
    }

    // 生产消息确认机制 生产者往服务器端发送消息的时候,采用应答机制
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        String jsonString = correlationData.getId();
        System.out.println("消息id:" + correlationData.getId());
        if (ack) {
            log.info(">>>>>>使用MQ消息确认机制确保消息一定要投递到MQ中成功>>>>>>");
            return;
        }
        JSONObject jsonObject = JSONObject.parseObject(jsonString);
        // 生产者消息投递失败的话,采用递归重试机制
        send(jsonObject.toJSONString());
        log.info(">>>>>>使用MQ消息确认机制投递到MQ中失败>>>>>>");
    }

}

4,接收MQ返回的消息,同时保存订单

package com.laxun.order.mq.order;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.laxun.lxcommon.entity.order.Order;
import com.laxun.order.model.OrderSave;
import com.laxun.order.mq.configure.CommonOrderMq;
import com.laxun.order.service.OrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
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;
import java.util.ArrayList;
import java.util.List;

/** 接收订单消息队列中的消息 */
@Component
@Slf4j
public class OrderReceiver {

    @Autowired
    OrderService orderService;

    @RabbitListener(queues = CommonOrderMq.ORDER_QUEUE)
    public void process(Message message, Channel channel) throws IOException {
        try {
            //内容字符串
            String msg = new String(message.getBody(), "UTF-8");
            //将消息转为list集合,得到订单信息
            ArrayList orderSaves  = JSON.parseObject(msg, new TypeReference>(){});
            //判断订单信息是否为空
            if (orderSaves.size()==0) {
                log.error(">>>>>>订单信息不能为空>>>>>>", orderSaves);
                basicNack(message, channel);
                return;
            }
            //查看订单信息是否保存过
            if (isProcess(orderSaves)) {
                log.error(">>>>>>订单已存在>>>>>>", orderSaves);
                basicNack(message, channel);
                return;
            }
            //保存订单
            orderService.addGoods(orderSaves);
            basicNack(message,channel);
            System.out.println(">>>>>>订单保存成功>>>>>>");
        } catch (Exception e) {
            e.printStackTrace();
            basicNack(message, channel);
        }
    }

    //告诉服务器收到这条消息 已经被我消费了 可以在队列删掉 这样以后就不会再发了
    // 否则消息服务器以为这条消息没处理掉 后续还会在发
    private void basicNack(Message message, Channel channel) throws IOException {
        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }

    /**
     * 判断订单是否保存过
     * @param orderSave
     * @return
     */
    public boolean isProcess(List orderSave){
        boolean flag = true ;
        for(OrderSave o:orderSave){
            //订单
            Order order = o.getOrder() ;
            //订单编号
            String orderNo = order.getOrderNo() ;
            //根据订单编号查询订单是否存在
            Order od = orderService.getOrderByOrderNo(orderNo) ;
            if(od==null){
                flag = false ;
                break;
            }
        }
        return flag ;
    }

}

5.生成支付宝、微信的签名字符串

/**
     * 返回支付需要的签名
     * @param channel 支付渠道
     * @param orderNo 订单编号
     * @param money 金额
     * @param request
     * @return
     * @throws ParserConfigurationException
     * @throws IOException
     * @throws SAXException
     */
    public Result getSign(String channel,String orderNo,Double money,HttpServletRequest request) throws ParserConfigurationException, IOException, SAXException {
        if("0".equals(channel)){
            // 余额支付
        }else if("1".equals(channel)){
            //如果是微信支付
            Map m = new HashMap();
            m.put("appid", WxConfig.APPID);// 公众账号ID
            m.put("mch_id", WxConfig.MUCHID);// 商户号
            m.put("nonce_str", RandomStringGenerator.getRandomStringByLength(32));//设备号
            m.put("body", "辣讯商品支付");// 商品描述/名称
            m.put("out_trade_no", orderNo);// 商户订单号编号
            m.put("total_fee",String.valueOf((int)(money*100)));//总金额
            m.put("spbill_create_ip", RandomStringGenerator.getIpAddress(request));// 终端IP
            m.put("notify_url", WxConfig.NOTIFY_URL);// 回调地址
            m.put("trade_type", "APP");// 交易类型
            //参数签名,待传key
            m.put("sign", PaymentKit.createSign(m, WxConfig.SECRETKEY));
            String xmlStr = PaymentApi.pushOrder(m);
            System.out.println("调用支付接口xml:"+xmlStr);
            Map map = XMLParser.getMapFromXML(xmlStr);
            if("SUCCESS".equals(map.get("result_code"))){
                Map paySignReqHandler = new HashMap();
                paySignReqHandler.put("appid", WxConfig.APPID);
                paySignReqHandler.put("partnerid", WxConfig.MUCHID);
                paySignReqHandler.put("prepayid", map.get("prepay_id").toString());
                paySignReqHandler.put("package", "Sign=WXPay");
                paySignReqHandler.put("noncestr", map.get("nonce_str").toString());
                paySignReqHandler.put("timestamp", String.valueOf(System.currentTimeMillis()/1000));
                paySignReqHandler.put("sign", PaymentKit.createSign(paySignReqHandler, WxConfig.SECRETKEY));
                return ResultUtil.success(StatusCode.OK,"下单成功",paySignReqHandler);
            }
        }else if("2".equals(channel)){
            //支付宝支付
            //初始化请求客户端,其中封装了签名、请求、验签的功能
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.URL, AlipayConfig.APPID, AlipayConfig.RSA_PRIVATE_KEY, "json", AlipayConfig.CHARSET, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.SIGNTYPE);
            //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称:alipay.trade.app.pay
            AlipayTradeAppPayRequest alipayrequest = new AlipayTradeAppPayRequest();
            //SDK已经封装掉了公共参数,这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
            AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
            model.setBody("辣讯商品支付");//对一笔交易的具体描述信息
            model.setSubject("辣讯商品支付");//商品的标题/交易标题/订单标题/订单关键字等。
            model.setOutTradeNo(orderNo);//商户网站唯一订单号
            model.setTimeoutExpress("30m");//该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15dm-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
            model.setTotalAmount(money.toString());//金额
            model.setProductCode("QUICK_MSECURITY_PAY");//销售产品码,商家和支付宝签约的产品码
            alipayrequest.setBizModel(model);
            alipayrequest.setNotifyUrl(AlipayConfig.notify_url);//回调地址
            try {
                //这里和普通的接口调用不同,使用的是sdkExecute
                AlipayTradeAppPayResponse response = alipayClient.sdkExecute(alipayrequest);
                System.out.println(response.getBody());//就是orderString 可以直接给客户端请求,无需再做处理。
                return ResultUtil.success(StatusCode.OK,"下单成功",response.getBody());
            } catch (AlipayApiException e) {
                e.printStackTrace();
            }
        }
        return ResultUtil.error(StatusCode.ERROR,"创建订单失败") ;
    }

 

你可能感兴趣的:(spring-cloud,RabbitMQ)