Browse Source

feat: 更新业务逻辑

master
xc-yjs 7 months ago
parent
commit
58604afa8f
  1. 2
      nla-common/src/main/java/cn/nla/common/config/WebConfig.java
  2. 14
      nla-common/src/main/java/cn/nla/common/constant/TimeConstant.java
  3. 2
      nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java
  4. 2
      nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java
  5. 1
      nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java
  6. 5
      nla-order-service/pom.xml
  7. 128
      nla-order-service/src/main/java/cn/nla/order/component/AlipayStrategy.java
  8. 55
      nla-order-service/src/main/java/cn/nla/order/component/PayFactory.java
  9. 18
      nla-order-service/src/main/java/cn/nla/order/component/PayStrategy.java
  10. 22
      nla-order-service/src/main/java/cn/nla/order/component/PayStrategyContext.java
  11. 22
      nla-order-service/src/main/java/cn/nla/order/component/WechatPayStrategy.java
  12. 74
      nla-order-service/src/main/java/cn/nla/order/config/AlipayConfig.java
  13. 23
      nla-order-service/src/main/java/cn/nla/order/config/PayUrlConfig.java
  14. 2
      nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java
  15. 74
      nla-order-service/src/main/java/cn/nla/order/controller/CallbackController.java
  16. 156
      nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java
  17. 5
      nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java
  18. 2
      nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java
  19. 8
      nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java
  20. 42
      nla-order-service/src/main/java/cn/nla/order/model/VO/PayInfoVO.java
  21. 82
      nla-order-service/src/main/java/cn/nla/order/model/VO/ProductOrderVO.java
  22. 23
      nla-order-service/src/main/java/cn/nla/order/model/request/RepayOrderRequest.java
  23. 43
      nla-order-service/src/main/java/cn/nla/order/mq/ProductOrderMQListener.java
  24. 31
      nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java
  25. 191
      nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java
  26. 18
      nla-order-service/src/main/resources/application.yml
  27. 6
      nla-order-service/src/main/resources/mapper/ProductOrderMapper.xml
  28. 2
      nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java
  29. 2
      nla-product-service/src/main/resources/application.yml

2
nla-common/src/main/java/cn/nla/common/config/WebConfig.java

@ -31,6 +31,8 @@ public class WebConfig implements WebMvcConfigurer {
"/user/**/login",
"/user/**/uploadFile",
"/cop/coupon/v1/new_user_coupon",
"/odr/product/v1/test_pay",
"/odr/callback/v1/**",
"/odr/product/v1/query_state");
}

14
nla-common/src/main/java/cn/nla/common/constant/TimeConstant.java

@ -0,0 +1,14 @@
package cn.nla.common.constant;
public class TimeConstant {
/**
*
* 支付订单的有效时长超过未支付则关闭订单
*
* 订单超时毫秒默认30分钟
*/
//public static final long ORDER_PAY_TIMEOUT_MILLS = 30*60*1000;
public static final long ORDER_PAY_TIMEOUT_MILLS = 5*60*1000;
}

2
nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponController.java

@ -40,7 +40,7 @@ public class CouponController {
@ApiOperation("领取优惠券")
@GetMapping("/add/promotion/{coupon_id}")
public JsonData addPromotionCoupon(@ApiParam(value = "优惠券id", required = true) @PathVariable("coupon_id") long couponId) {
return couponService.addCoupon(couponId, CouponCategoryEnum.PROMOTION);
return couponService.addCoupon(couponId, CouponCategoryEnum.NEW_USER);
}
/**
* 新用户注册发放优惠券接口

2
nla-coupon-service/src/main/java/cn/nla/coupon/controller/CouponRecordController.java

@ -23,7 +23,7 @@ import java.util.Map;
*/
@Api(tags = "优惠券领劵记录控制器")
@RestController
@RequestMapping("/cop/couponRecord/v1")
@RequestMapping("/cop/coupon_record/v1")
public class CouponRecordController {
@Resource

1
nla-coupon-service/src/main/java/cn/nla/coupon/service/impl/CouponServiceImpl.java

@ -87,6 +87,7 @@ public class CouponServiceImpl extends ServiceImpl<CouponMapper, CouponEntity> i
CouponEntity coupon = baseMapper.selectOne(
Wrappers.<CouponEntity>lambdaQuery()
.eq(CouponEntity::getId, couponId).eq(CouponEntity::getCategory, category.name()));
log.info("参数:{},{}",couponId,category.name());
//优惠券是否可以领取
this.checkCoupon(coupon, loginUser.getId());
//构建领劵记录

5
nla-order-service/pom.xml

@ -25,5 +25,10 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>4.10.218.ALL</version>
</dependency>
</dependencies>
</project>

128
nla-order-service/src/main/java/cn/nla/order/component/AlipayStrategy.java

@ -0,0 +1,128 @@
package cn.nla.order.component;
import cn.nla.common.enums.BizCodeEnum;
import cn.nla.common.enums.ClientType;
import cn.nla.common.exception.BizException;
import cn.nla.order.config.AlipayConfig;
import cn.nla.order.config.PayUrlConfig;
import cn.nla.order.model.VO.PayInfoVO;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
@Slf4j
@Service
public class AlipayStrategy implements PayStrategy {
@Autowired
private PayUrlConfig payUrlConfig;
@Override
public String unifiedorder(PayInfoVO payInfoVO) {
HashMap<String, String> content = new HashMap<>();
//商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
content.put("out_trade_no", payInfoVO.getOutTradeNo());
content.put("product_code", "FAST_INSTANT_TRADE_PAY");
//订单总金额,单位为元,精确到小数点后两位
content.put("total_amount", payInfoVO.getPayFee().toString());
//商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,&amp; 等。
content.put("subject", payInfoVO.getTitle());
//商品描述,可空
content.put("body", payInfoVO.getDescription());
double timeout = Math.floor(payInfoVO.getOrderPayTimeoutMills() / (1000 * 60));
//前端也需要判断订单是否要关闭了, 如果要快要到期则不给二次支付
if (timeout < 1) {
throw new BizException(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
}
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
// 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
content.put("timeout_express", Double.valueOf(timeout).intValue() + "m");
String clientType = payInfoVO.getClientType();
String form = "";
try {
if (clientType.equalsIgnoreCase(ClientType.H5.name())) {
//H5手机网页支付
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setBizContent(JSON.toJSONString(content));
request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl());
request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl());
AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request);
log.info("响应日志:alipayResponse={}", alipayResponse);
if (alipayResponse.isSuccess()) {
form = alipayResponse.getBody();
} else {
log.error("支付宝构建H5表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO);
}
} else if (clientType.equalsIgnoreCase(ClientType.PC.name())) {
//PC支付
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setBizContent(JSON.toJSONString(content));
request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl());
request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl());
AlipayTradePagePayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request);
log.info("响应日志:alipayResponse={}", alipayResponse);
if (alipayResponse.isSuccess()) {
form = alipayResponse.getBody();
} else {
log.error("支付宝构建PC表单失败:alipayResponse={},payInfo={}", alipayResponse, payInfoVO);
}
}
} catch (AlipayApiException e) {
log.error("支付宝构建表单异常:payInfo={},异常={}", payInfoVO, e);
}
return form;
}
@Override
public String refund(PayInfoVO payInfoVO) {
return null;
}
/**
* 查询订单状态
* 支付成功 返回非空
* 其他返回空
* <p>
* 未支付
* {"alipay_trade_query_response":{"code":"40004","msg":"Business Failed","sub_code":"ACQ.TRADE_NOT_EXIST","sub_msg":"交易不存在","buyer_pay_amount":"0.00","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00"},"sign":"xxxxx"}
* <p>
* 已经支付
* {"alipay_trade_query_response":{"code":"10000","msg":"Success","buyer_logon_id":"mqv***@sandbox.com","buyer_pay_amount":"0.00","buyer_user_id":"2088102176996700","buyer_user_type":"PRIVATE","invoice_amount":"0.00","out_trade_no":"adbe8e8f-3b18-4c9e-b736-02c4c2e15eca","point_amount":"0.00","receipt_amount":"0.00","send_pay_date":"2020-12-04 17:06:47","total_amount":"111.99","trade_no":"2020120422001496700501648498","trade_status":"TRADE_SUCCESS"},"sign":"xxxx"}
*
* @param payInfoVO
* @return
*/
@Override
public String queryPaySuccess(PayInfoVO payInfoVO) {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
HashMap<String, String> content = new HashMap<>();
//订单商户号,64位
content.put("out_trade_no", payInfoVO.getOutTradeNo());
request.setBizContent(JSON.toJSONString(content));
AlipayTradeQueryResponse response = null;
try {
response = AlipayConfig.getInstance().execute(request);
log.info("支付宝订单查询响应:{}", response.getBody());
} catch (AlipayApiException e) {
log.error("支付宝订单查询异常:{}", e);
}
if (response.isSuccess()) {
log.info("支付宝订单状态查询成功:{}", payInfoVO);
return response.getTradeStatus();
} else {
log.info("支付宝订单状态查询失败:{}", payInfoVO);
return "";
}
}
}

55
nla-order-service/src/main/java/cn/nla/order/component/PayFactory.java

@ -0,0 +1,55 @@
package cn.nla.order.component;
import cn.nla.common.enums.ProductOrderPayTypeEnum;
import cn.nla.order.model.VO.PayInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class PayFactory {
@Autowired
private AlipayStrategy alipayStrategy;
@Autowired
private WechatPayStrategy wechatPayStrategy;
/**
* 创建支付简单工程模式
* @param payInfoVO
* @return
*/
public String pay(PayInfoVO payInfoVO){
String payType = payInfoVO.getPayType();
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
//支付宝支付
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);
return payStrategyContext.executeUnifiedorder(payInfoVO);
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
//微信支付 暂未实现
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
return payStrategyContext.executeUnifiedorder(payInfoVO);
}
return "";
}
/**
* 查询订单支付状态
*
* 支付成功返回非空其他返回空
*
* @param payInfoVO
* @return
*/
public String queryPaySuccess(PayInfoVO payInfoVO){
String payType = payInfoVO.getPayType();
if(ProductOrderPayTypeEnum.ALIPAY.name().equalsIgnoreCase(payType)){
//支付宝支付
PayStrategyContext payStrategyContext = new PayStrategyContext(alipayStrategy);
return payStrategyContext.executeQueryPaySuccess(payInfoVO);
} else if(ProductOrderPayTypeEnum.WECHAT.name().equalsIgnoreCase(payType)){
//微信支付 暂未实现
PayStrategyContext payStrategyContext = new PayStrategyContext(wechatPayStrategy);
return payStrategyContext.executeQueryPaySuccess(payInfoVO);
}
return "";
}
}

18
nla-order-service/src/main/java/cn/nla/order/component/PayStrategy.java

@ -0,0 +1,18 @@
package cn.nla.order.component;
import cn.nla.order.model.VO.PayInfoVO;
public interface PayStrategy {
/**
* 下单
*/
String unifiedorder(PayInfoVO payInfoVO);
/**
* 退款
*/
default String refund(PayInfoVO payInfoVO){return "";}
/**
* 查询支付是否成功
*/
default String queryPaySuccess(PayInfoVO payInfoVO){return "";}
}

22
nla-order-service/src/main/java/cn/nla/order/component/PayStrategyContext.java

@ -0,0 +1,22 @@
package cn.nla.order.component;
import cn.nla.order.model.VO.PayInfoVO;
public class PayStrategyContext {
private PayStrategy payStrategy;
public PayStrategyContext(PayStrategy payStrategy){
this.payStrategy = payStrategy;
}
/**
* 根据支付策略调用不同的支付
*/
public String executeUnifiedorder(PayInfoVO payInfoVO){
return this.payStrategy.unifiedorder(payInfoVO);
}
/**
* 根据支付的策略调用不同的查询订单支持状态
*/
public String executeQueryPaySuccess(PayInfoVO payInfoVO){
return this.payStrategy.queryPaySuccess(payInfoVO);
}
}

22
nla-order-service/src/main/java/cn/nla/order/component/WechatPayStrategy.java

@ -0,0 +1,22 @@
package cn.nla.order.component;
import cn.nla.order.model.VO.PayInfoVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j
@Service
public class WechatPayStrategy implements PayStrategy {
@Override
public String unifiedorder(PayInfoVO payInfoVO) {
return null;
}
@Override
public String refund(PayInfoVO payInfoVO) {
return null;
}
@Override
public String queryPaySuccess(PayInfoVO payInfoVO) {
return null;
}
}

74
nla-order-service/src/main/java/cn/nla/order/config/AlipayConfig.java

@ -0,0 +1,74 @@
package cn.nla.order.config;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
public class AlipayConfig {
/**
* 支付宝网关地址
*/
public static final String PAY_GATEWAY = "https://openapi-sandbox.dl.alipaydev.com/gateway.do";
/**
* 支付宝 APPID
*/
public static final String APPID = "9021000140670744";
/**
* 应用私钥
*/
public static final String APP_PRI_KEY = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCZqPyUdTWlvNXo9MVd2mXVNSugokQSsAkdfArNoecoSVzxZvXzrK3hyIFSvw3N8BXgdFy2WPZ/55uuWReuudpDmsbUntdT//31hCm86KMKLinK/9qd43+L+xtOc7W9X7WUn0Zdy29OtUUgzp1SV17vZoxi+GVijQkBLgENHxQUe6N6gupl8YtSMrG8aUdw63VA2JzGIIuehv5icseBtPqAv1Bb3PV5RGvhrPxQhY87FXE3Yr144yVCQjqUJIBP6KGqDLt8qp5+If+mNkuE3JfdFQKxAj5h6fWFXtZ4JRP2ax0bJcgk8GA3mhHd/fofFYE5D4nekQ2dTVat4n0D62MVAgMBAAECggEAFFSObpn/ahlM+BCCmINP2+C4D/3IIezWl+cUitZz/hmOyYXE3uOm8euUaL1Md0Xfrx3WMm4c1xluuKejAYQng7BTxEfr13pUQIqm9w6Um8E12Fz3sNIryvqDR2L51b93B9328mv7Ix/KsKeFmyJiNiyMQH/68goU+en7S6AkCP2KRUf9zIkCn+7s5ARlB82nH8nOlL8W4J57GBM5w8T5TXIdeZ+kVehaLbNYs45kRDZHgSCDLVZcvWOlQEDLQupU8Z97LMgNugwu26lChX3XJ1Nd8DrixTV61DKzg5qkKFhHb3f+oK89SYbw6DCNmMdI/Eo+MKK4x71fXeCJF7jigQKBgQDNKWgml+DQ1Yv92NI9jeotmtTKfPmHvYjY99+7WZe9hofwLh0F/53efc/x2woSaFMwF/fYDM2qi4sPf/ee2r9jrz2zr7igCfs65YPePWEfw61Enb6XXY5tVz3+0ukkLh5qsJe3+fYcJryvyFhLRHoR01VadxextqwogFQyP5pwtQKBgQC/vIiXNWdnw1+sgoH1ERJCFcOgQZqkHNOj985so+owiRlDjGY20Vhe+i7AJpASIuZIG5vHSG7Q13m15vkj1eSLa/Xjvo985paEv/wkZH5QWv3VSvN+3KuZX1uxDIhwiSKw8o7T8vIPIwPehemI5/wh2eVz5xuMx0zqpl6kRm2E4QKBgQCza3zcE4LW34qYK2Bh2JRt4bQqiXUoEsd1CAGvtMCRTO/hSlKzhkKn61tJ5gc+5eGqc6uaVEgg1/x7R6xkTmWGZiEN3aUWwOPnNAVI/GDOCLClC1c8xOLkGKHtEsI1k+eh4mIs0Ps8z1vAVvNwj6g5oppIjVI5ZsnUebYoY5tK6QKBgGW8TeWdqo5+Nke8v+Qe+Mcj8TcZWEp4GTxJgrhWiS7TsBoNZzbzAabzrNa8H3ngFrxwGco1/sbFbok8UznWhnwL/t6nAE/PslIh5FITGIrenQ0NxgGsaUjqNibNdZ/ww/2L1olTwWiStbze5TmFjl0P1xYWrKGYJpHjURhtXjNBAoGARNV7eG+H6pAFjECQ7fgNWAbPYQaFuWZQijMdRec6OOnLRvnzMlBWkrCmlSQS8Wl5PKNSkj9x1BCbFitrfv6SUONFi8Mx13lRQKHrWEvyrvPwv7pA2x1QkIn3po1p6obh8xmy7NsUPoBnZsWUWgyE94bYYmHteEmxclezrazsac4=";
/**
* 支付宝公钥
*/
public static final String ALIPAY_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhoKAJ8j4r772JuPQDgVsMckqkeDQtJN4xZRa208dv9TClOsPUyptpyjDTnb01cXrZCBxWWbnYx5YlYjq+4Fee22HOWlh+lozSmFWeWigKefj7zSFji1WMmHTnSlYF0nvRPKWMfXKBqlnvkG1H82jxmeqDsZGw/a53jFL5EKiaS7bvLf3BlS8KCEuMvnwGQXMiTaIe98GkV8YcbEUXAEi7KLrinslEVwkCOA7y8exjOlCWJEi2lcbXyiuqejDs2K594O8wOd5RcoWpOmWYRMV0dhjue7ovETyErfeK0A0nTCmXmbZdcLYVOODzLHO2zupVSyF6Rj845BLb+z2tnZ3QwIDAQAB";
/**
* 签名类型
*/
public static final String SIGN_TYPE = "RSA2";
/**
* 字符编码
*/
public static final String CHARSET = "UTF-8";
/**
* 返回参数格式
*/
public static final String FORMAT = "json";
/**
* 构造函数私有化
*/
private AlipayConfig() {
}
private volatile static AlipayClient instance = null;
/**
* 单例模式获取, 双重锁校验
*
* @return
*/
public static AlipayClient getInstance() {
if (instance == null) {
synchronized (AlipayConfig.class) {
if (instance == null) {
instance = new DefaultAlipayClient(PAY_GATEWAY, APPID, APP_PRI_KEY, FORMAT, CHARSET, ALIPAY_PUB_KEY, SIGN_TYPE);
}
}
}
return instance;
}
}

23
nla-order-service/src/main/java/cn/nla/order/config/PayUrlConfig.java

@ -0,0 +1,23 @@
package cn.nla.order.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Configuration
@Data
public class PayUrlConfig {
/**
* 支付成功页面跳转
*/
@Value("${alipay.success_return_url}")
private String alipaySuccessReturnUrl;
/**
* 支付成功回调通知
*/
@Value("${alipay.callback_url}")
private String alipayCallbackUrl;
}

2
nla-order-service/src/main/java/cn/nla/order/config/RabbitMQConfig.java

@ -47,7 +47,6 @@ public class RabbitMQConfig {
*/
@Value("${mq.config.ttl}")
private Integer ttl;
/**
* 消息转换器
* @return
@ -99,5 +98,4 @@ public class RabbitMQConfig {
public Binding orderCloseBinding(){
return new Binding(orderCloseQueue,Binding.DestinationType.QUEUE,eventExchange,orderCloseRoutingKey,null);
}
}

74
nla-order-service/src/main/java/cn/nla/order/controller/CallbackController.java

@ -0,0 +1,74 @@
package cn.nla.order.controller;
import cn.nla.common.enums.ProductOrderPayTypeEnum;
import cn.nla.common.util.JsonData;
import cn.nla.order.config.AlipayConfig;
import cn.nla.order.service.ProductOrderService;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Api(tags ="订单回调通知模块")
@Controller
@RequestMapping("/odr/callback/v1")
@Slf4j
public class CallbackController {
@Autowired
private ProductOrderService productOrderService;
/**
* 支付回调通知 post方式
*/
@PostMapping("alipay")
public String alipayCallback(HttpServletRequest request, HttpServletResponse response) {
//将异步通知中收到的所有参数存储到map中
log.info("into alipay ");
Map<String, String> paramsMap = convertRequestParamsToMap(request);
log.info("支付宝回调通知结果:{}", paramsMap);
//调用SDK验证签名
try {
boolean signVerified = AlipaySignature.rsaCheckV1(paramsMap, AlipayConfig.ALIPAY_PUB_KEY, AlipayConfig.CHARSET, AlipayConfig.SIGN_TYPE);
if (signVerified) {
JsonData jsonData = productOrderService.handlerOrderCallbackMsg(ProductOrderPayTypeEnum.ALIPAY, paramsMap);
if (jsonData.getCode() == 0) {
//通知结果确认成功,不然会一直通知,八次都没返回success就认为交易失败
return "success";
}
}
} catch (AlipayApiException e) {
log.info("支付宝回调验证签名失败:异常:{},参数:{}", e, paramsMap);
}
return "failure";
}
/**
* 将request中的参数转换成Map
*/
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(16);
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int size = values.length;
if (size == 1) {
paramsMap.put(name, values[0]);
} else {
paramsMap.put(name, "");
}
}
return paramsMap;
}
}

156
nla-order-service/src/main/java/cn/nla/order/controller/ProductOrderController.java

@ -1,22 +1,41 @@
package cn.nla.order.controller;
import cn.nla.common.constant.CacheKey;
import cn.nla.common.enums.BizCodeEnum;
import cn.nla.common.enums.ClientType;
import cn.nla.common.enums.ProductOrderPayTypeEnum;
import cn.nla.common.interceptor.LoginInterceptor;
import cn.nla.common.model.LoginUser;
import cn.nla.common.util.CommonUtil;
import cn.nla.common.util.JsonData;
import cn.nla.order.config.AlipayConfig;
import cn.nla.order.config.PayUrlConfig;
import cn.nla.order.model.request.ConfirmOrderRequest;
import cn.nla.order.model.request.RepayOrderRequest;
import cn.nla.order.service.ProductOrderService;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeWapPayResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* <p>
@ -34,6 +53,33 @@ public class ProductOrderController {
@Resource
private ProductOrderService orderService;
@Resource
private PayUrlConfig payUrlConfig;
@Autowired
private StringRedisTemplate redisTemplate;
@ApiOperation("获取提交订单令牌")
@GetMapping("get_token")
public JsonData getOrderToken() {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
String key = String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId());
String token = CommonUtil.getStringNumRandom(32);
redisTemplate.opsForValue().set(key, token, 30, TimeUnit.MINUTES);
return JsonData.buildSuccess(token);
}
@ApiOperation("分页查询我的订单列表")
@GetMapping("page")
public JsonData pagePOrderList(
@ApiParam(value = "当前页") @RequestParam(value = "page", defaultValue = "1") int page,
@ApiParam(value = "每页显示多少条") @RequestParam(value = "size", defaultValue = "10") int size,
@ApiParam(value = "订单状态") @RequestParam(value = "state", required = false) String state) {
return JsonData.buildSuccess(orderService.page(page, size, state));
}
/**
* 查询订单状态
* <p>
@ -52,21 +98,46 @@ public class ProductOrderController {
public void confirmOrder(@ApiParam("订单对象") @RequestBody ConfirmOrderRequest orderRequest, HttpServletResponse response) {
JsonData jsonData = orderService.confirmOrder(orderRequest);
if (jsonData.getCode() == 0) {
// String client = orderRequest.getClientType();
// String payType = orderRequest.getPayType();
// //如果是支付宝网页支付,都是跳转网页,APP除外
// if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
// log.info("创建支付宝订单成功:{}", orderRequest.toString());
// if (client.equalsIgnoreCase(ClientType.H5.name())) {
// writeData(response, jsonData);
// } else if (client.equalsIgnoreCase(ClientType.APP.name())) {
// //APP SDK支付 TODO
// }
// } else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
// //微信支付 TODO
// }
String client = orderRequest.getClientType();
String payType = orderRequest.getPayType();
//如果是支付宝网页支付,都是跳转网页,APP除外
if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
log.info("创建支付宝订单成功:{}", orderRequest.toString());
if (client.equalsIgnoreCase(ClientType.H5.name())) {
writeData(response, jsonData);
} else if (client.equalsIgnoreCase(ClientType.APP.name())) {
//APP SDK支付 TODO
}
} else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
//微信支付 TODO
}
} else {
log.error("创建订单失败{}", jsonData);
CommonUtil.sendJsonMessage(response, jsonData);
}
}
@ApiOperation("重新支付订单")
@PostMapping("repay")
public void repay(@ApiParam("订单对象") @RequestBody RepayOrderRequest repayOrderRequest, HttpServletResponse response) {
JsonData jsonData = orderService.repay(repayOrderRequest);
if (jsonData.getCode() == 0) {
String client = repayOrderRequest.getClientType();
String payType = repayOrderRequest.getPayType();
//如果是支付宝网页支付,都是跳转网页,APP除外
if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
log.info("重新支付订单成功:{}", repayOrderRequest.toString());
if (client.equalsIgnoreCase(ClientType.H5.name())) {
writeData(response, jsonData);
} else if (client.equalsIgnoreCase(ClientType.APP.name())) {
//APP SDK支付 TODO
}
} else if (payType.equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
//微信支付 TODO
}
} else {
log.error("重新支付订单失败{}", jsonData.toString());
CommonUtil.sendJsonMessage(response, jsonData);
}
}
@ -81,5 +152,64 @@ public class ProductOrderController {
log.error("写出Html异常", e);
}
}
/**
* 将request中的参数转换成Map
*/
private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
Map<String, String> paramsMap = new HashMap<>(16);
Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
for (Map.Entry<String, String[]> entry : entrySet) {
String name = entry.getKey();
String[] values = entry.getValue();
int size = values.length;
if (size == 1) {
paramsMap.put(name, values[0]);
} else {
paramsMap.put(name, "");
}
}
return paramsMap;
}
/**
* 测试支付方法
*/
@GetMapping("test_pay")
public void testAlipay(HttpServletResponse response) throws AlipayApiException, IOException {
HashMap<String, String> content = new HashMap<>();
//商户订单号,64个字符以内、可包含字母、数字、下划线;需保证在商户端不重复
String no = UUID.randomUUID().toString();
log.info("订单号:{}", no);
content.put("out_trade_no", no);
content.put("product_code", "FAST_INSTANT_TRADE_PAY");
//订单总金额,单位为元,精确到小数点后两位
content.put("total_amount", String.valueOf("111.99"));
//商品标题/交易标题/订单标题/订单关键字等。 注意:不可使用特殊字符,如 /,=,&amp; 等。
content.put("subject", "杯子");
//商品描述,可空
content.put("body", "好的杯子");
// 该笔订单允许的最晚付款时间,逾期将关闭交易。取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(1c-当天的情况下,无论交易何时创建,都在0点关闭)。
// 该参数数值不接受小数点, 如 1.5h,可转换为 90m。
content.put("timeout_express", "5m");
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
request.setBizContent(JSON.toJSONString(content));
request.setNotifyUrl(payUrlConfig.getAlipayCallbackUrl());
request.setReturnUrl(payUrlConfig.getAlipaySuccessReturnUrl());
AlipayTradeWapPayResponse alipayResponse = AlipayConfig.getInstance().pageExecute(request);
if (alipayResponse.isSuccess()) {
System.out.println("调用成功");
String form = alipayResponse.getBody();
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(form);
response.getWriter().flush();
response.getWriter().close();
} else {
System.out.println("调用失败");
}
}
}

5
nla-order-service/src/main/java/cn/nla/order/feign/CouponFeignService.java

@ -13,9 +13,10 @@ public interface CouponFeignService {
/**
* 领取优惠券
*/
@GetMapping("/cop/coupon/v1/promotion/{coupon_id}")
@GetMapping("/cop/coupon_record/v1/detail/{record_id}")
JsonData findUserCouponRecordById(@PathVariable("record_id") long recordId);
@PostMapping("/cop/coupon/v1/lock_records")
@PostMapping("/cop/coupon_record/v1/lock_records")
JsonData lockCouponRecords(@RequestBody LockCouponRecordRequest recordRequest);
}

2
nla-order-service/src/main/java/cn/nla/order/feign/ProductFeignService.java

@ -18,6 +18,6 @@ public interface ProductFeignService {
/**
* 商品库存锁定
*/
@PostMapping("/cop/couponRecord/v1/lock_products")
@PostMapping("/pdt/product/v1/lock_products")
JsonData lockProducts(@RequestBody LockProductRequest lockProductRequest);
}

8
nla-order-service/src/main/java/cn/nla/order/mapper/ProductOrderMapper.java

@ -2,6 +2,7 @@ package cn.nla.order.mapper;
import cn.nla.order.model.entity.ProductOrderEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* <p>
@ -13,4 +14,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface ProductOrderMapper extends BaseMapper<ProductOrderEntity> {
/**
* 更新订单状态
* @param outTradeNo
* @param newState
* @param oldState
*/
void updateOrderPayState(@Param("outTradeNo") String outTradeNo, @Param("newState") String newState, @Param("oldState") String oldState);
}

42
nla-order-service/src/main/java/cn/nla/order/model/VO/PayInfoVO.java

@ -0,0 +1,42 @@
package cn.nla.order.model.VO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayInfoVO {
/**
* 订单号
*/
private String outTradeNo;
/**
* 订单总金额
*/
private BigDecimal payFee;
/**
* 支付类型 微信-支付宝-银行-其他
*/
private String payType;
/**
* 端类型 APP/H5/PC
*/
private String clientType;
/**
* 标题
*/
private String title;
/**
* 描述
*/
private String description;
/**
* 订单支付超时时间毫秒
*/
private long orderPayTimeoutMills;
}

82
nla-order-service/src/main/java/cn/nla/order/model/VO/ProductOrderVO.java

@ -0,0 +1,82 @@
package cn.nla.order.model.VO;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@Data
public class ProductOrderVO {
private Long id;
/**
* 订单唯一标识
*/
private String outTradeNo;
/**
* NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单
*/
private String state;
/**
* 订单生成时间
*/
private Date createTime;
/**
* 订单总金额
*/
private BigDecimal totalAmount;
/**
* 订单实际支付价格
*/
private BigDecimal payAmount;
/**
* 支付类型微信-银行-支付宝
*/
private String payType;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String headImg;
/**
* 用户id
*/
private Long userId;
/**
* 0表示未删除1表示已经删除
*/
private Integer del;
/**
* 更新时间
*/
private Date updateTime;
/**
* 订单类型 DAILY普通单PROMOTION促销订单
*/
private String orderType;
/**
* 收货地址 json存储
*/
private String receiverAddress;
/**
* 订单项
*/
private List<OrderItemVO> orderItemList;
}

23
nla-order-service/src/main/java/cn/nla/order/model/request/RepayOrderRequest.java

@ -0,0 +1,23 @@
package cn.nla.order.model.request;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
@Data
public class RepayOrderRequest {
/**
* 订单号
*/
@JsonProperty("out_trade_no")
private String outTradeNo;
/**
* 支付类型- 微信-银行卡-支付宝
*/
@JsonProperty("pay_type")
private String payType;
/**
* 订单号
*/
@JsonProperty("client_type")
private String clientType;
}

43
nla-order-service/src/main/java/cn/nla/order/mq/ProductOrderMQListener.java

@ -0,0 +1,43 @@
package cn.nla.order.mq;
import cn.nla.common.model.OrderMessage;
import cn.nla.order.service.ProductOrderService;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Slf4j
@Component
@RabbitListener(queues = "${mq.config.order_close_queue}")
public class ProductOrderMQListener {
@Autowired
private ProductOrderService productOrderService;
/**
*
* 消费重复消息幂等性保证
* 并发情况下如何保证安全
*/
@RabbitHandler
public void closeProductOrder(OrderMessage orderMessage, Message message, Channel channel) throws IOException {
log.info("监听到消息:closeProductOrder:{}",orderMessage);
long msgTag = message.getMessageProperties().getDeliveryTag();
try{
boolean flag = productOrderService.closeProductOrder(orderMessage);
if(flag){
channel.basicAck(msgTag,false);
}else {
channel.basicReject(msgTag,true);
}
}catch (IOException e){
log.error("定时关单失败:",orderMessage);
channel.basicReject(msgTag,true);
}
}
}

31
nla-order-service/src/main/java/cn/nla/order/service/ProductOrderService.java

@ -1,10 +1,17 @@
package cn.nla.order.service;
import cn.nla.common.enums.ProductOrderPayTypeEnum;
import cn.nla.common.model.OrderMessage;
import cn.nla.common.model.PageResult;
import cn.nla.common.util.JsonData;
import cn.nla.order.model.VO.ProductOrderVO;
import cn.nla.order.model.entity.ProductOrderEntity;
import cn.nla.order.model.request.ConfirmOrderRequest;
import cn.nla.order.model.request.RepayOrderRequest;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.Map;
/**
* <p>
* 订单表 服务类
@ -31,5 +38,29 @@ public interface ProductOrderService extends IService<ProductOrderEntity> {
*/
JsonData confirmOrder(ConfirmOrderRequest request);
/**
* 查询订单状态
*/
String queryProductOrderState(String outTradeNo);
/**
* 队列监听定时关单
*/
boolean closeProductOrder(OrderMessage orderMessage);
/**
* 支付结果回调通知
*/
JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum alipay, Map<String, String> paramsMap);
/**
* 分页查询我的订单列表
*/
PageResult<ProductOrderVO> page(int page, int size, String state);
/**
* 订单二次支付
*/
JsonData repay(RepayOrderRequest repayOrderRequest);
}

191
nla-order-service/src/main/java/cn/nla/order/service/impl/ProductOrderServiceImpl.java

@ -1,36 +1,37 @@
package cn.nla.order.service.impl;
import cn.nla.common.constant.CacheKey;
import cn.nla.common.constant.TimeConstant;
import cn.nla.common.enums.*;
import cn.nla.common.exception.BizException;
import cn.nla.common.interceptor.LoginInterceptor;
import cn.nla.common.model.LoginUser;
import cn.nla.common.model.OrderMessage;
import cn.nla.common.model.PageResult;
import cn.nla.common.util.CommonUtil;
import cn.nla.common.util.JsonData;
import cn.nla.order.component.PayFactory;
import cn.nla.order.config.RabbitMQConfig;
import cn.nla.order.feign.CouponFeignService;
import cn.nla.order.feign.ProductFeignService;
import cn.nla.order.feign.UserFeignService;
import cn.nla.order.mapper.ProductOrderItemMapper;
import cn.nla.order.model.VO.CouponRecordVO;
import cn.nla.order.model.VO.OrderItemVO;
import cn.nla.order.model.VO.ProductOrderAddressVO;
import cn.nla.order.model.VO.*;
import cn.nla.order.model.entity.ProductOrderEntity;
import cn.nla.order.mapper.ProductOrderMapper;
import cn.nla.order.model.entity.ProductOrderItemEntity;
import cn.nla.order.model.request.ConfirmOrderRequest;
import cn.nla.order.model.request.LockCouponRecordRequest;
import cn.nla.order.model.request.LockProductRequest;
import cn.nla.order.model.request.OrderItemRequest;
import cn.nla.order.model.request.*;
import cn.nla.order.service.ProductOrderService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
@ -38,9 +39,7 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -76,6 +75,9 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
@Resource
private RabbitMQConfig rabbitMQConfig;
@Resource
private PayFactory payFactory;
@Override
public JsonData confirmOrder(ConfirmOrderRequest request) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
@ -84,12 +86,13 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_NOT_EXIST);
}
//原子操作 校验令牌,删除令牌
// String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
// Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
// List.of(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken);
// if (result == 0L) {
// throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
// }
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
Long result = redisTemplate.execute(new DefaultRedisScript<>(script, Long.class),
List.of(String.format(CacheKey.SUBMIT_ORDER_TOKEN_KEY, loginUser.getId())), orderToken);
if (result == 0L) {
throw new BizException(BizCodeEnum.ORDER_CONFIRM_TOKEN_EQUAL_FAIL);
}
String orderOutTradeNo = CommonUtil.getStringNumRandom(32);
//获取收货地址详情
ProductOrderAddressVO addressVO = this.getUserAddress(request.getAddressId());
@ -113,17 +116,26 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
//锁定库存
this.lockProductStocks(orderItemList, orderOutTradeNo);
//创建订单
ProductOrderEntity productOrder = this.saveProductOrder(request,loginUser,orderOutTradeNo,addressVO);
ProductOrderEntity productOrder = this.saveProductOrder(request, loginUser, orderOutTradeNo, addressVO);
//创建订单项
this.saveProductOrderItems(orderOutTradeNo,productOrder.getId(),orderItemList);
this.saveProductOrderItems(orderOutTradeNo, productOrder.getId(), orderItemList);
//发送延迟消息,用于自动关单
OrderMessage orderMessage = new OrderMessage();
orderMessage.setOutTradeNo(orderOutTradeNo);
rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(),rabbitMQConfig.getOrderCloseDelayRoutingKey(),orderMessage);
rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getOrderCloseDelayRoutingKey(), orderMessage);
return null;
//创建支付
PayInfoVO payInfoVO = new PayInfoVO(orderOutTradeNo,
productOrder.getPayAmount(), request.getPayType(),
request.getClientType(), orderItemList.get(0).getProductTitle(), "", TimeConstant.ORDER_PAY_TIMEOUT_MILLS);
String payResult = payFactory.pay(payInfoVO);
if (StringUtils.isNotBlank(payResult)) {
log.info("创建支付订单成功:payInfoVO={},payResult={}", payInfoVO, payResult);
return JsonData.buildSuccess(payResult);
} else {
log.error("创建支付订单失败:payInfoVO={},payResult={}", payInfoVO, payResult);
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
}
}
/**
@ -131,7 +143,7 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
*/
private void saveProductOrderItems(String orderOutTradeNo, Long orderId, List<OrderItemVO> orderItemList) {
List<ProductOrderItemEntity> list = orderItemList.stream().map(
obj->{
obj -> {
ProductOrderItemEntity itemDO = new ProductOrderItemEntity();
itemDO.setBuyNum(obj.getBuyNum());
itemDO.setProductId(obj.getProductId());
@ -158,6 +170,7 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
productOrder.setUserId(loginUser.getId());
productOrder.setHeadImg(loginUser.getHeadImg());
productOrder.setNickname(loginUser.getName());
productOrder.setOutTradeNo(orderOutTradeNo);
productOrder.setCreateTime(new Date());
productOrder.setDel(0);
@ -237,6 +250,136 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
}
}
@Override
public boolean closeProductOrder(OrderMessage orderMessage) {
ProductOrderEntity productOrderDO = baseMapper.selectOne(new QueryWrapper<ProductOrderEntity>()
.eq("out_trade_no", orderMessage.getOutTradeNo()));
if (productOrderDO == null) {
//订单不存在
log.warn("直接确认消息,订单不存在:{}", orderMessage);
return true;
}
if (productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.PAY.name())) {
//已经支付
log.info("直接确认消息,订单已经支付:{}", orderMessage);
return true;
}
//向第三方支付查询订单是否真的未支付 todo
PayInfoVO payInfoVO = new PayInfoVO();
payInfoVO.setPayType(productOrderDO.getPayType());
payInfoVO.setOutTradeNo(orderMessage.getOutTradeNo());
String payResult = payFactory.queryPaySuccess(payInfoVO);
//结果为空,则未支付成功,本地取消订单
if (StringUtils.isBlank(payResult)) {
baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.CANCEL.name(), ProductOrderStateEnum.NEW.name());
log.info("结果为空,则未支付成功,本地取消订单:{}", orderMessage);
return true;
} else {
//支付成功,主动的把订单状态改成UI就支付,造成该原因的情况可能是支付通道回调有问题
log.warn("支付成功,主动的把订单状态改成UI就支付,造成该原因的情况可能是支付通道回调有问题:{}", orderMessage);
baseMapper.updateOrderPayState(productOrderDO.getOutTradeNo(), ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
return true;
}
}
/***
* 支付通知结果更新订单状态
*/
@Override
public JsonData handlerOrderCallbackMsg(ProductOrderPayTypeEnum payType, Map<String, String> paramsMap) {
//MQ投递 --》在慢慢消费
if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.ALIPAY.name())) {
//支付宝支付
//获取商户订单号
String outTradeNo = paramsMap.get("out_trade_no");
//交易的状态
String tradeStatus = paramsMap.get("trade_status");
if ("TRADE_SUCCESS".equalsIgnoreCase(tradeStatus) || "TRADE_FINISHED".equalsIgnoreCase(tradeStatus)) {
//更新订单状态
baseMapper.updateOrderPayState(outTradeNo, ProductOrderStateEnum.PAY.name(), ProductOrderStateEnum.NEW.name());
return JsonData.buildSuccess();
}
} else if (payType.name().equalsIgnoreCase(ProductOrderPayTypeEnum.WECHAT.name())) {
//微信支付 TODO
}
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_CALLBACK_NOT_SUCCESS);
}
@Override
public PageResult<ProductOrderVO> page(int page, int size, String state) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
Page<ProductOrderEntity> pageInfo = new Page<>(page, size);
IPage<ProductOrderEntity> orderDOPage = null;
if (StringUtils.isBlank(state)) {
orderDOPage = baseMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderEntity>().eq("user_id", loginUser.getId()));
} else {
orderDOPage = baseMapper.selectPage(pageInfo, new QueryWrapper<ProductOrderEntity>().eq("user_id", loginUser.getId())
.eq("state", state));
}
//获取订单列表
List<ProductOrderEntity> productOrderDOList = orderDOPage.getRecords();
List<ProductOrderVO> productOrderVOList = productOrderDOList.stream().map(orderDO -> {
List<ProductOrderItemEntity> itemDOList = productOrderItemMapper.selectList(
new QueryWrapper<ProductOrderItemEntity>().eq("product_order_id", orderDO.getId()));
List<OrderItemVO> itemVOList = itemDOList.stream().map(item -> {
OrderItemVO itemVO = new OrderItemVO();
BeanUtils.copyProperties(item, itemVO);
return itemVO;
}).collect(Collectors.toList());
ProductOrderVO productOrderVO = new ProductOrderVO();
BeanUtils.copyProperties(orderDO, productOrderVO);
productOrderVO.setOrderItemList(itemVOList);
return productOrderVO;
}).collect(Collectors.toList());
return new PageResult<>(orderDOPage.getTotal(), orderDOPage.getPages(), productOrderVOList);
}
@Override
public JsonData repay(RepayOrderRequest repayOrderRequest) {
LoginUser loginUser = LoginInterceptor.threadLocal.get();
ProductOrderEntity productOrderDO = baseMapper.selectOne(new QueryWrapper<ProductOrderEntity>().eq("out_trade_no",
repayOrderRequest.getOutTradeNo()).eq("user_id", loginUser.getId()));
log.info("订单状态:{}", productOrderDO);
if (productOrderDO == null) {
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_NOT_EXIST);
}
//订单状态不对,不是NEW状态
if (!productOrderDO.getState().equalsIgnoreCase(ProductOrderStateEnum.NEW.name())) {
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_STATE_ERROR);
} else {
//订单创建到现在的存活时间
long orderLiveTime = CommonUtil.getCurrentTimestamp() - productOrderDO.getCreateTime().getTime();
//创建订单是临界点,所以再增加1分钟多几秒,假如29分,则也不能支付了
orderLiveTime = orderLiveTime + 70 * 1000;
//大于订单超时时间,则失效
if (orderLiveTime > TimeConstant.ORDER_PAY_TIMEOUT_MILLS) {
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_PAY_TIMEOUT);
} else {
//记得更新DB订单支付参数 payType,还可以增加订单支付信息日志 TODO
//总时间-存活的时间 = 剩下的有效时间
long timeout = TimeConstant.ORDER_PAY_TIMEOUT_MILLS - orderLiveTime;
//创建支付
PayInfoVO payInfoVO = new PayInfoVO(productOrderDO.getOutTradeNo(),
productOrderDO.getPayAmount(), repayOrderRequest.getPayType(),
repayOrderRequest.getClientType(), productOrderDO.getOutTradeNo(), "", timeout);
log.info("payInfoVO={}", payInfoVO);
String payResult = payFactory.pay(payInfoVO);
if (StringUtils.isNotBlank(payResult)) {
log.info("创建二次支付订单成功:payInfoVO={},payResult={}", payInfoVO, payResult);
return JsonData.buildSuccess(payResult);
} else {
log.error("创建二次支付订单失败:payInfoVO={},payResult={}", payInfoVO, payResult);
return JsonData.buildResult(BizCodeEnum.PAY_ORDER_FAIL);
}
}
}
}
/**
* 验证价格
@ -267,7 +410,7 @@ public class ProductOrderServiceImpl extends ServiceImpl<ProductOrderMapper, Pro
}
}
if (realPayAmount.compareTo(orderRequest.getRealPayAmount()) != 0) {
log.error("订单验价失败:{}", orderRequest);
log.error("订单验价失败:{} , 真实价格:{}", orderRequest, realPayAmount);
throw new BizException(BizCodeEnum.ORDER_CONFIRM_PRICE_FAIL);
}
}

18
nla-order-service/src/main/resources/application.yml

@ -37,17 +37,17 @@ spring:
mq:
config:
#延迟队列,不能被监听消费
order_close_delay_queue: stock.release.delay.queue
order_close_delay_queue: order.release.delay.queue
#延迟队列的消息过期后转发的队列
order_close_queue: stock.release.queue
order_close_queue: order.release.queue
#交换机
order_event_exchange: stock.event.exchange
order_event_exchange: order.event.exchange
#进入延迟队列的路由key
order_close_delay_routing_key: stock.release.delay.routing.key
order_close_delay_routing_key: order.release.delay.routing.key
#消息过期,进入释放队列的key
order_close_routing_key: stock.release.routing.key
order_close_routing_key: order.release.routing.key
#消息过期时间,毫秒,临时改为6分钟
ttl: 60000
ttl: 360000
#配置plus打印sql⽇志
mybatis-plus:
@ -67,3 +67,9 @@ feign:
connectTimeout: 5000
# 设置Feign的读取超时时间(秒)
readTimeout: 10000
##支付宝配置
alipay:
#支付成功的跳转页面
success_return_url: https://blog.virtualagent.cn/
#支付宝通知回调接口, 蚂蚁金服开放平台应用后台也需要配置对应的地址
callback_url: https://d3ca-171-223-196-63.ngrok-free.app/odr/callback/v1/alipay

6
nla-order-service/src/main/resources/mapper/ProductOrderMapper.xml

@ -25,4 +25,10 @@
id, out_trade_no, state, create_time, total_amount, pay_amount, pay_type, nickname, head_img, user_id, del, update_time, order_type, receiver_address
</sql>
<!--更新订单状态-->
<update id="updateOrderPayState">
update product_order set `state`=#{newState} where out_trade_no = #{outTradeNo} and `state` = #{oldState}
</update>
</mapper>

2
nla-product-service/src/main/java/cn/nla/product/controller/ProductController.java

@ -24,7 +24,7 @@ import java.util.Map;
*/
@Api(tags = "商品控制器")
@RestController
@RequestMapping("//pdt/product/v1")
@RequestMapping("/pdt/product/v1")
public class ProductController {
@Resource

2
nla-product-service/src/main/resources/application.yml

@ -60,7 +60,7 @@ mq:
#消息过期,进入释放队列的key
stock_release_routing_key: stock.release.routing.key
#消息过期时间,毫秒,临时改为6分钟
ttl: 60000
ttl: 360000
feign:
client:

Loading…
Cancel
Save