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,"创建订单失败") ;
}