Authored by tanling

Merge branch 'hotfix_1224_paycancel' into test6.8.4

package com.yohoufo.dal.order;
import com.yohoufo.dal.order.model.OrdersPrePay;
import org.apache.ibatis.annotations.Param;
public interface OrdersPrePayMapper {
int insertOnDuplicateUpdate(OrdersPrePay record);
OrdersPrePay selectByUidAndOrderCode(@Param("uid") Integer uid, @Param("orderCode") Long orderCode);
}
\ No newline at end of file
... ...
package com.yohoufo.dal.order.model;
public class OrdersPrePay {
private Long orderCode;
private Integer uid;
private Integer payment;
private Integer createTime;
private Integer updateTime;
//更新次数
private Integer updateNum = 1;
public Long getOrderCode() {
return orderCode;
}
public void setOrderCode(Long orderCode) {
this.orderCode = orderCode;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public Integer getPayment() {
return payment;
}
public void setPayment(Integer payment) {
this.payment = payment;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getUpdateNum() {
return updateNum;
}
public void setUpdateNum(Integer updateNum) {
this.updateNum = updateNum;
}
}
\ No newline at end of file
... ...
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.yohoufo.dal.order.OrdersPrePayMapper" >
<resultMap id="BaseResultMap" type="com.yohoufo.dal.order.model.OrdersPrePay" >
<id column="order_code" property="orderCode" jdbcType="BIGINT" />
<result column="uid" property="uid" jdbcType="INTEGER" />
<result column="payment" property="payment" jdbcType="INTEGER" />
<result column="create_time" property="createTime" jdbcType="INTEGER" />
<result column="update_time" property="updateTime" jdbcType="INTEGER" />
<result column="update_num" property="updateNum" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
order_code, uid, payment, create_time, update_time, update_num
</sql>
<insert id="insertOnDuplicateUpdate" parameterType="com.yohoufo.dal.order.model.OrdersPrePay" >
insert into orders_pre_pay (order_code, uid, payment, create_time, update_time, update_num)
values(#{orderCode,jdbcType=BIGINT}, #{uid,jdbcType=INTEGER}, #{payment,jdbcType=INTEGER},
#{createTime,jdbcType=INTEGER}, #{updateTime,jdbcType=INTEGER}, #{updateNum,jdbcType=INTEGER})
on duplicate key update payment = #{payment,jdbcType=INTEGER}, update_time = #{updateTime,jdbcType=INTEGER},update_num = update_num + #{updateNum,jdbcType=INTEGER}
</insert>
<select id="selectByUidAndOrderCode" resultMap="BaseResultMap" >
select
<include refid="Base_Column_List" />
from orders_pre_pay
where order_code = #{orderCode,jdbcType=BIGINT} and uid = #{uid,jdbcType=INTEGER}
</select>
</mapper>
\ No newline at end of file
... ...
package com.yohoufo.order.common;
public enum Payment {
WALLET(11, "wallet", "钱包"),
MINIAPP(3, "miniapp", "微信小程序"),
ALIPAY(2, "alipay", "支付宝"),
WECHAT(1, "wechat", "微信");
WALLET(11, "wallet", "钱包", false),
MINIAPP(3, "miniapp", "微信小程序", true),
ALIPAY(2, "alipay", "支付宝", true),
WECHAT(1, "wechat", "微信", true);
int code;
... ... @@ -15,10 +15,13 @@ public enum Payment {
String desc;
Payment(int code, String name, String desc) {
boolean isNeedPrePay;
Payment(int code, String name, String desc, boolean isNeedPrePay) {
this.code = code;
this.name = name;
this.desc = desc;
this.isNeedPrePay = isNeedPrePay;
}
public int getCode() {
... ... @@ -33,12 +36,21 @@ public enum Payment {
return desc;
}
public boolean isNeedPrePay() {
return isNeedPrePay;
}
/**
* 检查是否存在该支付方式
* @param code
* @return
*/
public static Payment getPayment(int code){
public static Payment getPayment(Integer code){
if (code == null){
return null;
}
for(Payment v : values()){
if(v.code == code){
return v;
... ...
package com.yohoufo.order.event;
import com.yohoufo.common.alarm.Event;
public class PayConfirmEvent extends Event {
//用户uid
private int uid;
//订单code
private long orderCode;
//支付方式
private int payment;
public PayConfirmEvent(int uid, long orderCode, int payment) {
this.uid = uid;
this.orderCode = orderCode;
this.payment = payment;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public long getOrderCode() {
return orderCode;
}
public void setOrderCode(long orderCode) {
this.orderCode = orderCode;
}
public int getPayment() {
return payment;
}
public void setPayment(int payment) {
this.payment = payment;
}
}
... ...
package com.yohoufo.order.model.request;
import com.yohobuy.ufo.model.order.common.TabType;
import com.yohoufo.order.event.OrderCancelEvent;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
... ... @@ -21,4 +22,6 @@ public class OrderRequest {
String tabType;
TabType actor;
OrderCancelEvent orderCancelEvent;
}
... ...
... ... @@ -35,6 +35,9 @@ public abstract class AbsOrderAutoCancelDelayMsgConsumer implements YhConsumer {
OrderCancelEvent orderCancelEvent = null;
try {
orderCancelEvent = JSONObject.parseObject(message.toString(), OrderCancelEvent.class);
// 如果1分钟之内有预支付记录,则延迟触发自动取消
cancel(orderCancelEvent);
mqConsumerLog.info( "handle {} message success, message is {}.", topic, message );
... ...
... ... @@ -25,7 +25,7 @@ public class BuyerOrderAutoCancelDelayMsgConsumer extends AbsOrderAutoCancelDela
public void cancel(OrderCancelEvent orderCancelEvent) {
int uid = orderCancelEvent.getUid();
long orderCode = orderCancelEvent.getOrderCode();
OrderRequest req = OrderRequest.builder().uid(uid).orderCode(orderCode).build();
OrderRequest req = OrderRequest.builder().uid(uid).orderCode(orderCode).orderCancelEvent(orderCancelEvent).build();
buyerOrderService.cancelByAuto(req);
}
... ...
package com.yohoufo.order.mq.consumer;
import com.yohoufo.order.event.OrderCancelEvent;
import com.yohoufo.order.model.request.OrderRequest;
import com.yohoufo.order.mq.TopicConstants;
import com.yohoufo.order.service.impl.SellerOrderCancelService;
import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -22,9 +23,11 @@ public class SellerOrderAutoCancelDelayMsgConsumer extends AbsOrderAutoCancelDel
@Override
public void cancel(OrderCancelEvent orderCancelEvent) {
int uid = orderCancelEvent.getUid();
long orderCode = orderCancelEvent.getOrderCode();
sellerOrderCancelService.cancelByTimeout(uid, orderCode);
OrderRequest orderRequest = OrderRequest.builder()
.uid(orderCancelEvent.getUid())
.orderCode(orderCancelEvent.getOrderCode())
.orderCancelEvent(orderCancelEvent).build();
sellerOrderCancelService.cancelByTimeout(orderRequest);
}
@Override
... ...
... ... @@ -15,6 +15,12 @@ public abstract class AbstractOrderPaymentService {
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* 超时未支付取消
* @param orderInfo
* @return
*/
public abstract boolean isTimeoutCancelStatus(OrderInfo orderInfo);
... ...
... ... @@ -130,6 +130,19 @@ public class BuyerOrderPaymentService extends AbstractOrderPaymentService {
return false;
}
/**
* 超时未支付取消
* @param orderInfo
* @return
*/
public boolean isTimeoutCancelStatus(OrderInfo orderInfo){
if (orderInfo.getStatus() != null && orderInfo.getStatus().intValue() == OrderStatus.BUYER_CANCEL_TIMEOUT.getCode()){
return true;
}
return false;
}
/**
* 订单是 已支付的订单
... ...
... ... @@ -8,16 +8,30 @@ import com.yohoufo.order.model.request.PaymentRequest;
import com.yohoufo.order.model.request.TransferMoneyRequest;
import com.yohoufo.order.model.response.PaymentConfirmRsp;
import com.yohoufo.order.model.response.PrepayResponse;
import com.yohoufo.order.service.pay.AbstractPayService;
public interface IPaymentService {
/**
* 近1分钟内是否有预支付记录
* @param uid
* @param orderCode
*/
public void checkPrePay(int uid, long orderCode);
/**
* 支付
* @param request
* @return
*/
PrepayResponse payment(PaymentRequest request);
/**
* 获取支付的主场service
* @param paymentCode
* @return
*/
public AbstractPayService getPayService(int paymentCode);
/**
* 订单支付结果确认
... ...
... ... @@ -262,7 +262,12 @@ public class MerchantOrderPaymentService extends AbstractOrderPaymentService {
}
/**
@Override
public boolean isTimeoutCancelStatus(OrderInfo orderInfo) {
return false;
}
/**
* 获取订单情报
* @param orderCode
* @param uid
... ...
... ... @@ -220,6 +220,15 @@ public class SellerOrderPaymentService extends AbstractOrderPaymentService {
}
@Override
public boolean isTimeoutCancelStatus(OrderInfo orderInfo) {
if (orderInfo.getStatus() != null && orderInfo.getStatus().intValue() == SellerOrderStatus.TIMEOUT_CANCEL.getCode()){
return true;
}
return false;
}
/**
* 获取订单情报
* @param orderCode
... ...
package com.yohoufo.order.service.handler;
import com.google.common.eventbus.Subscribe;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
import com.yohoufo.common.alarm.IEventHandler;
import com.yohoufo.order.event.PayConfirmEvent;
import com.yohoufo.order.model.request.PaymentRequest;
import com.yohoufo.order.service.IPaymentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class PayConfirmEventHandler implements IEventHandler<PayConfirmEvent> {
final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
IPaymentService paymentService;
@Subscribe
@Override
public void handle(PayConfirmEvent event) {
logger.info("begin handle payConfirmEvent, uid: {}, orderCode: {}, payment: {}", event.getUid(), event.getOrderCode(), event.getPayment());
if (event.getOrderCode() <= 0 || event.getUid() <= 0 || event.getPayment() <= 0) {
throw new ServiceException(ServiceError.ORDER_REQUEST_PARM_IS_EMPTY);
}
PaymentRequest request = PaymentRequest.builder().uid(event.getUid()).orderCode(event.getOrderCode()).payment(event.getPayment()).build();
logger.info("ufo.order.confirm req {}", request);
paymentService.payConfirm(request);
}
}
\ No newline at end of file
... ...
... ... @@ -13,6 +13,7 @@ import com.yohobuy.ufo.model.order.resp.OrderCntResp;
import com.yohobuy.ufo.model.order.resp.OrderListInfo;
import com.yohobuy.ufo.model.order.resp.PageResp;
import com.yohobuy.ufo.model.order.vo.OrderListVo;
import com.yohoufo.common.alarm.EventBusPublisher;
import com.yohoufo.common.exception.UfoServiceException;
import com.yohoufo.common.utils.DateUtil;
import com.yohoufo.common.utils.UserInfoHiddenHelper;
... ... @@ -24,18 +25,23 @@ import com.yohoufo.dal.order.model.BuyerOrderGoods;
import com.yohoufo.dal.order.model.SellerOrderGoods;
import com.yohoufo.order.common.ActionStatusHold;
import com.yohoufo.order.common.DelStatus;
import com.yohoufo.order.common.Payment;
import com.yohoufo.order.event.BeforeDepotReceiveEvent;
import com.yohoufo.order.event.BeforeSellerDeliverEvent;
import com.yohobuy.ufo.model.order.vo.AddressInfo;
import com.yohoufo.order.event.PayConfirmEvent;
import com.yohoufo.order.model.PayQueryBo;
import com.yohoufo.order.model.request.OrderListRequest;
import com.yohoufo.order.model.request.OrderRequest;
import com.yohoufo.order.model.response.OrderDetailInfo;
import com.yohoufo.order.model.response.OrderSummaryResp;
import com.yohoufo.order.service.IBuyerOrderMetaService;
import com.yohoufo.order.service.IBuyerOrderService;
import com.yohoufo.order.service.IPaymentService;
import com.yohoufo.order.service.cache.CacheCleaner;
import com.yohoufo.order.service.cache.CacheKeyBuilder;
import com.yohoufo.order.service.cache.OrderCacheService;
import com.yohoufo.order.service.pay.AbstractPayService;
import com.yohoufo.order.service.proxy.InBoxFacade;
import com.yohoufo.order.service.proxy.ProductProxyService;
import org.apache.commons.lang3.StringUtils;
... ... @@ -74,7 +80,6 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
@Autowired
private InBoxFacade inBoxFacade;
@Autowired
private OrderCacheService orderCacheService;
... ... @@ -87,6 +92,9 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
@Autowired
private IBuyerOrderMetaService buyerOrderMetaService;
@Autowired
IPaymentService paymentService;
/**
* 提交订单
* @param orderRequest
... ... @@ -346,10 +354,17 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
}
public void cancelBeforePaid(OrderRequest orderRequest, DataNode dataNode, OrderStatus exceptStatus, OrderStatus targetStatus){
// 状态 ![待付款] 不合法
checkStatus(dataNode, orderRequest, exceptStatus);
// 近一分钟内有预支付记录
if (orderRequest.getOrderCancelEvent()!=null && !orderRequest.getOrderCancelEvent().isFinalRetry()){
paymentService.checkPrePay(orderRequest.getUid(), orderRequest.getOrderCode());
}
// 更新订单状态
final OrderStatus orderStatus = targetStatus;
final long orderCode;
... ... @@ -419,7 +434,6 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
*/
private void checkStatus(DataNode node, OrderRequest orderRequest, OrderStatus orderStatusEx ) {
BuyerOrder buyerOrder = node.buyerOrderInDB;
// 检查 订单状态
int status = buyerOrder.getStatus() == null ? -1 : buyerOrder.getStatus().intValue();
... ... @@ -429,6 +443,23 @@ public class BuyerOrderServiceImpl implements IBuyerOrderService {
orderRequest.getUid(), orderRequest.getOrderCode(), buyerOrder.getStatus());
throw new ServiceException(ServiceError.ORDER_STATUS_INVALIDATE);
}
// 状态是待支付的场合,到第三方检查是否支付过了
Payment payment = Payment.getPayment(buyerOrder.getPayment());
if (payment != null){
AbstractPayService payService = paymentService.getPayService(payment.getCode());
PayQueryBo payQueryBo = payService.payQuery(String.valueOf(buyerOrder.getOrderCode()));
if (payQueryBo != null && payQueryBo.isPayStatus()){
logger.warn("check status failed ,confirm paid, uid is {}, orderCode is {}, status is {}",
orderRequest.getUid(), orderRequest.getOrderCode(), buyerOrder.getStatus());
// 主动触发支付结果确认
PayConfirmEvent event = new PayConfirmEvent(buyerOrder.getUid(), buyerOrder.getOrderCode(), buyerOrder.getPayment());
EventBusPublisher.publishEvent(event);
throw new ServiceException(ServiceError.ORDER_STATUS_INVALIDATE);
}
}
}
... ...
... ... @@ -133,6 +133,10 @@ public class PaymentServiceImpl implements IPaymentService {
@Autowired
private BuyerCancelCompensateComputeHandler buyerCancelCompensateComputeHandler;
@Autowired
OrdersPrePayMapper ordersPrePayMapper;
... ... @@ -170,7 +174,7 @@ public class PaymentServiceImpl implements IPaymentService {
* @param paymentCode
* @return
*/
private AbstractPayService getPayService(int paymentCode){
public AbstractPayService getPayService(int paymentCode){
Payment payment = Payment.getPayment(paymentCode);
if (payment == null){
... ... @@ -273,6 +277,14 @@ public class PaymentServiceImpl implements IPaymentService {
}
logger.info("paySuccess status check {} {}", orderCode, orderInfo.getStatus());
if (!abstractOrderService.isOrderWaitingPay(orderInfo)){
// 如果状态是已取消,则需要需要把钱退给用户
if (abstractOrderService.isTimeoutCancelStatus(orderInfo)){
logger.info("paySuccess but need refund order is {}, amount is {} waring", orderCode, orderInfo.getAmount());
PaymentRequest request = PaymentRequest.builder().orderCode(orderCode).refundAmount(orderInfo.getAmount().doubleValue()).build();
refund(request);
return;
}
logger.warn("paySuccess finished. status has paid, orderCode is {}", orderCode);
return;
}
... ... @@ -1206,7 +1218,12 @@ public class PaymentServiceImpl implements IPaymentService {
//将openID 附属在 orderinfo上,用于拼接支付URL
orderInfo.setOpenid(request.getOpenid());
// 卖家or买家订单,支付方式更新,返回实付金额
BigDecimal amount = paymentService.checkUpdOrderCodePayment(orderInfo, request);
paymentService.checkUpdOrderCodePayment(orderInfo, request);
// 增加预支付记录
if (payment.isNeedPrePay()){
savePrePayOrder(payment, orderInfo);
}
AbstractPayService abstractPayService = getPayService(payment.getCode());
// 微信支付
... ... @@ -1226,7 +1243,40 @@ public class PaymentServiceImpl implements IPaymentService {
return response;
}
/**
* 取消订单与支付的时间间隔判定标识,默认1分钟,单位秒;
*/
public static final int PRE_PAY_TIME = 60;
private void savePrePayOrder(Payment payment, OrderInfo orderInfo) {
int now = (int) (System.currentTimeMillis() / 1000);
OrdersPrePay ordersPrePay = new OrdersPrePay();
ordersPrePay.setOrderCode(orderInfo.getOrderCode());
ordersPrePay.setUid(orderInfo.getUid());
ordersPrePay.setPayment(payment.getCode());
ordersPrePay.setCreateTime( now );
ordersPrePay.setUpdateTime( now );
ordersPrePay.setUpdateNum(1);
ordersPrePayMapper.insertOnDuplicateUpdate(ordersPrePay);
}
/**
* 近1分钟内是否有预支付记录
* @param uid
* @param orderCode
*/
public void checkPrePay(int uid, long orderCode) {
OrdersPrePay ordersPrePay = ordersPrePayMapper.selectByUidAndOrderCode(uid, orderCode);
if (Objects.isNull(ordersPrePay)) {
return;
}
int payTime = (int) (System.currentTimeMillis() / 1000) - ordersPrePay.getUpdateTime();
logger.info("end ordersPrePayDAO.checkPrePayTime,orderCode:{} ,payTime:{}", ordersPrePay.getOrderCode(), payTime);
if (payTime < PRE_PAY_TIME) {
logger.warn("CloseOrderByCode fail, prePayTime is less than setTime. order: {} ", ordersPrePay.getOrderCode());
throw new ServiceException(ServiceError.ORDER_CANCEL_FAILED_NOT_FIT_TIME);
}
}
/**
* 入口数据检查
... ...
... ... @@ -18,17 +18,22 @@ import com.yohoufo.order.constants.AlarmConfig;
import com.yohoufo.order.event.BillLogEvent;
import com.yohoufo.order.event.ErpCancelSellerOrderEvent;
import com.yohoufo.order.event.OrderCancelEvent;
import com.yohoufo.order.event.PayConfirmEvent;
import com.yohoufo.order.model.PayQueryBo;
import com.yohoufo.order.model.PayRefundBo;
import com.yohoufo.order.model.request.OrderRequest;
import com.yohoufo.order.model.request.PaymentRequest;
import com.yohoufo.order.model.request.TransferMoneyRequest;
import com.yohoufo.order.mq.DelayTime;
import com.yohoufo.order.mq.TopicConstants;
import com.yohoufo.order.mq.producer.TradeMqSender;
import com.yohoufo.order.service.IPaymentService;
import com.yohoufo.order.service.cache.CacheCleaner;
import com.yohoufo.order.service.cache.CacheKeyBuilder;
import com.yohoufo.order.service.impl.visitor.AutoCancelCase;
import com.yohoufo.order.service.impl.visitor.OffShelveCancelCase;
import com.yohoufo.order.service.impl.visitor.UserCancelCase;
import com.yohoufo.order.service.pay.AbstractPayService;
import com.yohoufo.order.service.proxy.InBoxFacade;
import com.yohoufo.order.service.proxy.ProductProxyService;
import com.yohoufo.order.service.support.codegenerator.OrderCodeGenerator;
... ... @@ -89,6 +94,9 @@ public class SellerOrderCancelService {
@Autowired
private SellerService sellerService;
@Autowired
IPaymentService paymentService;
/**
* TODO 如何控制好并发,必须控制不能重复转账 退款
* 使用乐观锁,带着查询到的状态且符合条件时再去更新
... ... @@ -194,7 +202,11 @@ public class SellerOrderCancelService {
}
public int cancelByTimeout(int uid, long orderCode){
public int cancelByTimeout(OrderRequest orderRequest){
int uid = orderRequest.getUid();
long orderCode = orderRequest.getOrderCode();
int result = 0;
SellerOrder sellerOrder = sellerOrderMapper.selectByOrderCodeUid(orderCode, uid);
... ... @@ -206,6 +218,28 @@ public class SellerOrderCancelService {
return result;
}
logger.info("in seller timeout cancel, uid {}, orderCode {}, SellerOrder status {}", uid, orderCode, status);
// 到第三方检查,如果已经支付了,则不能自动取消
Payment payment = Payment.getPayment(sellerOrder.getPayment());
if (SellerOrderStatus.WAITING_PAY.getCode() == status && payment != null){
AbstractPayService payService = paymentService.getPayService(payment.getCode());
PayQueryBo payQueryBo = payService.payQuery(String.valueOf(sellerOrder.getOrderCode()));
if (payQueryBo != null && payQueryBo.isPayStatus()){
logger.info("in seller timeout cancel failed, confirm paid. uid is {}, orderCode is {}", uid, orderCode);
PayConfirmEvent event = new PayConfirmEvent(sellerOrder.getUid(), sellerOrder.getOrderCode(), sellerOrder.getPayment());
EventBusPublisher.publishEvent(event);
return result;
}
}
// 如果近1分钟有预支付记录,则发送 自动取消延迟消息
if (orderRequest.getOrderCancelEvent()!= null && !orderRequest.getOrderCancelEvent().isFinalRetry()){
paymentService.checkPrePay(uid, orderCode);
}
//target seller Order Status
SellerOrderStatus targetSOStatus;
//case 1: 未支付时
... ...