Commit 4f3876a3 by box

Merge branch 'feature/box/三方券订单退款退券' into develop

parents 2fa9fd41 1024504e
...@@ -20,11 +20,10 @@ import cn.freemud.interceptor.ServiceException; ...@@ -20,11 +20,10 @@ import cn.freemud.interceptor.ServiceException;
import cn.freemud.management.entities.dto.request.order.OrderManagerRequest; import cn.freemud.management.entities.dto.request.order.OrderManagerRequest;
import cn.freemud.management.entities.dto.response.pay.PayRefundResponse; import cn.freemud.management.entities.dto.response.pay.PayRefundResponse;
import cn.freemud.management.enums.AfterSalesRefunStateEnum; import cn.freemud.management.enums.AfterSalesRefunStateEnum;
import cn.freemud.management.enums.coupon.EcologyChannelTypeEnum;
import cn.freemud.management.service.adapter.OrderManagerAdapter; import cn.freemud.management.service.adapter.OrderManagerAdapter;
import cn.freemud.management.service.handle.CouponQueryHandle; import cn.freemud.management.service.handle.*;
import cn.freemud.management.service.handle.OrderCouponHandle; import cn.freemud.management.util.ResponseUtil;
import cn.freemud.management.service.handle.OrderVerifyHandle;
import cn.freemud.management.service.handle.PaymentHandle;
import cn.freemud.redis.RedisCache; import cn.freemud.redis.RedisCache;
import cn.freemud.service.impl.OrderQueueService; import cn.freemud.service.impl.OrderQueueService;
import cn.freemud.service.store.StoreManager; import cn.freemud.service.store.StoreManager;
...@@ -95,6 +94,7 @@ public class RefundService { ...@@ -95,6 +94,7 @@ public class RefundService {
private final StoreManager storeManager; private final StoreManager storeManager;
private final CouponQueryHandle couponQueryHandle; private final CouponQueryHandle couponQueryHandle;
private final OrderCouponHandle orderCouponHandle; private final OrderCouponHandle orderCouponHandle;
private final ThirdCouponOrderHandle thirdCouponOrderHandle;
private OrderInfoReqs preValidRefund(String partnerId, String orderCode, Byte refundMode) { private OrderInfoReqs preValidRefund(String partnerId, String orderCode, Byte refundMode) {
// 查询订单信息 // 查询订单信息
...@@ -117,6 +117,20 @@ public class RefundService { ...@@ -117,6 +117,20 @@ public class RefundService {
throw new ServiceException("还未发货,请选择只退款不退货"); throw new ServiceException("还未发货,请选择只退款不退货");
} }
} }
// 判断订单是否使用了三方券,如果使用了三方券需要判断核销时间,抖音核销时间超过1h、美团核销时间超过60天,不允许申请退款(三方平台退不了)
if (ThirdCouponOrderHandle.isThirdCouponOrder(order)) {
ThirdCouponOrderHandle.ThirdCouponDto coupon = thirdCouponOrderHandle.getCoupon(order.getPartnerId(), order.getOrderCode());
if (coupon == null) {
throw new ServiceException("查询三方券信息失败,暂时无法退款");
}
// 如果券没有冲正,判断核销时间,默认使用订单的创建时间
if (!coupon.isCanceled()) {
if (ThirdCouponOrderHandle.isCancelTimeout(new Date(Long.parseLong(order.getCreateTime())), coupon.getEcologyChannelType())) {
throw new ServiceException("三方券核销时间过长,无法申请退款");
}
}
}
return order; return order;
} }
...@@ -149,18 +163,23 @@ public class RefundService { ...@@ -149,18 +163,23 @@ public class RefundService {
throw new ServiceException("该门店不支持线上退款,请联系门店进行线下处理"); throw new ServiceException("该门店不支持线上退款,请联系门店进行线下处理");
// 如果是买券订单,查询券是否已使用,如果没有使用,冻结券,否则不许退款 // 如果是买券订单,查询券是否已使用,如果没有使用,冻结券,否则不许退款
if (BizTypeEnum.SALE_COUPON.getBizType().equals(order.getBizType())) { if (BizTypeEnum.SALE_COUPON.getBizType().equals(order.getBizType()) || ThirdCouponOrderHandle.isThirdCouponOrder(order)) {
if (BooleanUtils.isTrue(orderRefundVo.getIsPartRefund())) { if (BooleanUtils.isTrue(orderRefundVo.getIsPartRefund())) {
throw new ServiceException("该订单不支持部分退款,请联系门店进行线下处理"); throw new ServiceException("该订单不支持部分退款,请联系门店进行线下处理");
} }
if (BizTypeEnum.SALE_COUPON.getBizType().equals(order.getBizType())) {
freezeCouponCode(partnerId, order); freezeCouponCode(partnerId, order);
} }
}
// 退款是否退配送费 // 退款是否退配送费
boolean refundDeliveryFee = isRefundDeliveryFee(order, refundAmount); boolean refundDeliveryFee = isRefundDeliveryFee(order, refundAmount);
// 未接单 // 未接单
if (NewOrderStatus.PLACE_AN_ORDER.getIndex().equals(order.getOrderState())) { if (NewOrderStatus.PLACE_AN_ORDER.getIndex().equals(order.getOrderState())) {
// 商家未接单逻辑处理 1.调用支付退款 2.根据支付退款返回状态组装订单取消参数,调用订单取消接口 // 商家未接单逻辑处理 1.调用支付退款 2.根据支付退款返回状态组装订单取消参数,调用订单取消接口
if (ThirdCouponOrderHandle.isThirdCouponOrder(order)) {
thirdCouponOrderHandle.thirdCouponRefund(partnerId, orderCode, order.getOperator());
}
refundOrder(order, isPartRefund, reason, remark, refundDeliveryFee, refundMode); refundOrder(order, isPartRefund, reason, remark, refundDeliveryFee, refundMode);
orderQueueService.backOrdersStatusChange(order.getOrderCode(), orderSdkAdapter.getOldStatus(order.getOrderState()), order.getPayState(), order.getPartnerId()); orderQueueService.backOrdersStatusChange(order.getOrderCode(), orderSdkAdapter.getOldStatus(order.getOrderState()), order.getPayState(), order.getPartnerId());
} else { } else {
......
...@@ -175,6 +175,7 @@ public enum ResponseResult { ...@@ -175,6 +175,7 @@ public enum ResponseResult {
ORDER_DELIVERY_EDIT_FAILED("45090", "修改配送状态失败"), ORDER_DELIVERY_EDIT_FAILED("45090", "修改配送状态失败"),
PARTNER_CAN_NOT_CANCEL_ORDER("45091","订单已部分退款,不能取消"), PARTNER_CAN_NOT_CANCEL_ORDER("45091","订单已部分退款,不能取消"),
DONE_FAIL_ONE_MINUTE("45092", "平台规定,接单一分钟后才能出餐"), DONE_FAIL_ONE_MINUTE("45092", "平台规定,接单一分钟后才能出餐"),
REFUND_THIRD_COUPON_TIME_LIMIT("45093", "核销时间超过可冲正期限,撤销核销失败"),
/** /**
* 支付 * 支付
......
...@@ -8,6 +8,9 @@ public enum SettlementTypeEnum { ...@@ -8,6 +8,9 @@ public enum SettlementTypeEnum {
DISCOUNT_COUPON(208, "折扣券"), DISCOUNT_COUPON(208, "折扣券"),
PRODUCT_COUPON(118, "商品券"), PRODUCT_COUPON(118, "商品券"),
SEND_COUPON(216, "购买券包发的优惠券"), SEND_COUPON(216, "购买券包发的优惠券"),
MT_TG_GOODS_COUPON(500, "美团团购套餐券"),
TIKTOK_TG_GOODS_COUPON(501, "抖音团购套餐券"),
; ;
private int settlementType; private int settlementType;
......
package cn.freemud.management.enums.coupon;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* © Copyright
*
* @author box 2023-03-14
*/
@Getter
@RequiredArgsConstructor
public enum CouponThirdChannelEnum {
UNKNOWN((short) 0, "未知"),
/**
* 抖音
*/
DOU_YIN((short) 1, "抖音"),
/**
* 美团点评
*/
MEI_TUAN((short) 2, "美团点评"),
;
/**
* 三方渠道
*/
private final Short channel;
private final String channelName;
public static final String DESC = "三方渠道类型枚举{1:抖音;2:美团点评}";
}
package cn.freemud.management.enums.coupon;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;
/**
* © Copyright
*
* @author box 2023-03-14
*/
@RequiredArgsConstructor
@Getter
public enum CouponThirdTypeEnum {
/**
* 券类型, 券类型名称
*/
UNKNOWN((short) -1, "未知"),
TAO_CAN((short) 0, "套餐券"),
DAI_JIN((short) 1, "代金券"),
TUAN_GOU((short) 2, "团购券"),
CI_KA((short) 3, "次卡"),
;
/**
* 三方渠道券类型
*/
private final Short type;
/**
* 券类型名称
*/
private final String typeName;
public static final String DESC = "三方券类型枚举{0:套餐券;1:代金券;2:团购券;3:次卡}";
}
package cn.freemud.management.enums.coupon;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 渠道类型 与生态应用枚举一致
*
* @author Clover.z
* @version 1.0.0
* @since 1.0.0
*/
@Getter
@AllArgsConstructor
public enum EcologyChannelTypeEnum {
/**
* 微信小程序
*/
WECHAT(1, "微信小程序", "wechat"),
/**
* 支付宝小程序
*/
ALIPAY(2, "支付宝小程序", "alipay"),
/**
* 抖音小程序
*/
TIKTOK(5, "抖音小程序", "tiktok"),
/**
* 美团点评
*/
MEITUAN(10, "美团点评", "meituan"),
/**
* 饿了么
*/
ELEME(15, "饿了么", "eleme"),
;
private final Integer val;
private final String desc;
private final String code;
}
package cn.freemud.management.enums.coupon;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* © Copyright
*
* @author box 2023-03-14
*/
@Getter
@RequiredArgsConstructor
public enum ThirdCouponReportStatusEnum {
SUCCESS((short) 1, "成功"),
FAIL((short) 2, "失败"),
;
private final Short status;
private final String msg;
public static final String DESC = "状态,1-成功,2-失败,不传默认成功";
}
package cn.freemud.management.enums.coupon;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* © Copyright
*
* @author box 2023-03-14
*/
@Getter
@RequiredArgsConstructor
public enum ThirdCouponReportTypeEnum {
REDEEM((short) 1, "核销"),
REVERSE((short) 2, "冲正"),
;
/**
* 三方渠道
*/
private final Short type;
private final String msg;
public static final String DESC = "'核销类型:1-核销券;2-核销冲正'";
}
package cn.freemud.management.intercept;
import cn.freemud.management.enums.ResponseResult;
import com.freemud.application.sdk.api.exception.IgnoreErrorAnnotation;
/**
* All rights Reserved, Designed By www.freemud.cn
*
* @version V1.0
* @Title: OrderServiceException
* @Package cn.freemud.management.intercept
* @Description:
* @author: shuhu.hou
* @date: 2020/4/23 11:37
* @Copyright: 2020 www.freemud.cn Inc. All rights reserved.
* 注意:本内容仅限于上海非码科技内部传阅,禁止外泄以及用于其他的商业目
*/
@IgnoreErrorAnnotation
public class CouponServiceException extends RuntimeException {
private ResponseResult result;
private String message;
public CouponServiceException(ResponseResult result) {
this.result = result;
this.message = result.getMessage();
}
public CouponServiceException(ResponseResult result, String message) {
this.result = result;
this.message = message;
}
public ResponseResult getResult() {
return result;
}
public void setResult(ResponseResult result) {
this.result = result;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
...@@ -40,4 +40,10 @@ public class OrderExceptionHandler { ...@@ -40,4 +40,10 @@ public class OrderExceptionHandler {
public BaseResponse bindException(DeliveryServiceException e) { public BaseResponse bindException(DeliveryServiceException e) {
return ResponseUtil.error(e.getResult().getCode(),e.getMessage()); return ResponseUtil.error(e.getResult().getCode(),e.getMessage());
} }
@ExceptionHandler(CouponServiceException.class)
@ResponseStatus(HttpStatus.OK)
public BaseResponse bindException(CouponServiceException e) {
return ResponseUtil.error(e.getResult().getCode(),e.getMessage());
}
} }
package cn.freemud.management.service.handle;
import cn.freemud.base.util.DateUtil;
import cn.freemud.management.enums.coupon.CouponThirdChannelEnum;
import cn.freemud.management.enums.coupon.EcologyChannelTypeEnum;
import cn.freemud.management.enums.SettlementTypeEnum;
import cn.freemud.management.enums.coupon.ThirdCouponReportStatusEnum;
import cn.freemud.management.enums.coupon.ThirdCouponReportTypeEnum;
import cn.freemud.management.thirdparty.CouponBackstageClient;
import cn.freemud.management.thirdparty.CouponOfflineClient;
import cn.freemud.management.thirdparty.EcologyAdapterClient;
import cn.freemud.management.thirdparty.request.coupon.RedeemReportQueryRequest;
import cn.freemud.management.thirdparty.request.coupon.ReverseThirdCouponRedeemReportReq;
import cn.freemud.management.thirdparty.request.ecology.adapter.CancelRedeemCouponRequest;
import cn.freemud.management.thirdparty.response.Result;
import cn.freemud.management.thirdparty.response.coupon.RedeemReportQueryResponse;
import cn.freemud.management.thirdparty.response.coupon.SaasPageResponse;
import cn.freemud.management.thirdparty.response.coupon.SaasResponse;
import cn.freemud.management.thirdparty.response.ecology.adapter.CancelRedeemCouponResponse;
import com.alibaba.fastjson.JSONObject;
import com.freemud.application.sdk.api.log.ApiLog;
import com.freemud.application.sdk.api.log.ErrorLog;
import com.freemud.application.sdk.api.ordercenter.entities.v1.OrderBeanV1;
import com.freemud.application.sdk.api.ordercenter.enums.CashTypeEnum;
import com.freemud.application.sdk.api.ordercenter.response.orderInfo.OrderInfoReqs;
import com.freemud.application.sdk.api.ordercenter.response.orderInfo.OrderPayItemResp;
import com.freemud.application.sdk.api.ordercenter.response.orderInfo.OrderSettlementResp;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import java.util.*;
/**
* © All rights Reserved
* 三方券订单
* @author wénkǎi.zhāng 2024-02-29
* @since 1.0
*/
@Component
public class ThirdCouponOrderHandle {
@Autowired
private CouponBackstageClient couponBackstageClient;
@Autowired
private EcologyAdapterClient ecologyAdapterClient;
@Autowired
private CouponOfflineClient couponOfflineClient;
/**
* 判断订单中是否使用了三方券
* <pre>
* 1. 小程序订单,使用了三方券抵扣
* 2. 订单宝订单,使用了三方券支付
* </pre>
* @param orderInfo
* @return 是/否
*/
public static boolean isThirdCouponOrder(OrderInfoReqs orderInfo) {
return isThirdCouponOrder(orderInfo.getOrderSettlementDetailList(), orderInfo.getOrderPayItemCreateReqList());
}
/**
* 判断订单中是否使用了三方券
* <pre>
* 1. 小程序订单,使用了三方券抵扣
* 2. 订单宝订单,使用了三方券支付
* </pre>
* @param orderBean
* @return 是/否
*/
public static boolean isThirdCouponOrder(OrderBeanV1 orderBean) {
return isThirdCouponOrder(orderBean.getOrderSettlementDetailList(), orderBean.getOrderPayItem());
}
/**
* 判断订单中是否使用了三方券
* <pre>
* 1. 小程序订单,使用了三方券抵扣
* 2. 订单宝订单,使用了三方券支付
* </pre>
* @param orderSettlementDetailList
* @param orderPayItemCreateReqList
* @return 是/否
*/
public static boolean isThirdCouponOrder(List<OrderSettlementResp> orderSettlementDetailList, List<OrderPayItemResp> orderPayItemCreateReqList) {
// 判断是否使用了三方券抵扣
if (CollectionUtils.isNotEmpty(orderSettlementDetailList)) {
boolean isThirdCoupon = orderSettlementDetailList.stream()
.anyMatch(o ->
o.getSettlementType().equals(SettlementTypeEnum.MT_TG_GOODS_COUPON.getSettlementType())
|| o.getSettlementType().equals(SettlementTypeEnum.TIKTOK_TG_GOODS_COUPON.getSettlementType())
);
if (isThirdCoupon) {
return true;
}
}
// 判断是否使用了三方券支付
if (CollectionUtils.isNotEmpty(orderPayItemCreateReqList)) {
return orderPayItemCreateReqList.stream().anyMatch(o -> o.getPayChannelType().toString().equals(CashTypeEnum.THIRD_COUPON.getCode()));
}
return false;
}
/**
* 判断是否超过了券冲正时间
* <pre></pre>
* @param redeemDate
* @param channelTypeEnum
* @return
*/
public static boolean isCancelTimeout(Date redeemDate, @Nullable EcologyChannelTypeEnum channelTypeEnum) {
if (EcologyChannelTypeEnum.MEITUAN.equals(channelTypeEnum)) {
// 美团券不能超过 60天
if (DateUtil.addDays(redeemDate, 60).compareTo(new Date()) < 0) {
return true;
}
} else if (EcologyChannelTypeEnum.TIKTOK.equals(channelTypeEnum)) {
// 抖音券不能超过 1 小时(3600秒)
if (redeemDate.getTime() + 3600_000 < System.currentTimeMillis()) {
return true;
}
}
return false;
}
/**
* 通过非码订单号查询三方券信息
* <pre></pre>
* @param partnerId
* @param orderCode
* @return
*/
@Nullable
public ThirdCouponDto getCoupon(String partnerId, String orderCode) {
RedeemReportQueryRequest request = new RedeemReportQueryRequest();
request.setPartnerId(partnerId);
request.setOrderCode(orderCode);
SaasResponse<SaasPageResponse<RedeemReportQueryResponse>> saasResponse = couponBackstageClient.queryCodesInfo(request);
ApiLog.infoMessage("查询券码服务:req={},resp={}", JSONObject.toJSONString(request), JSONObject.toJSONString(saasResponse));
if (!saasResponse.getStatusCode().equals(100) || saasResponse.getResult() == null || CollectionUtils.isEmpty(saasResponse.getResult().getList())) {
ErrorLog.errorDev("查询券码服务失败");
return null;
}
Optional<RedeemReportQueryResponse> first = saasResponse.getResult().getList().stream()
.filter(resp -> ThirdCouponReportTypeEnum.REDEEM.getType().equals(resp.getRedeemType()) && ThirdCouponReportStatusEnum.SUCCESS.getStatus().equals(resp.getRedeemStatus()))
.findFirst();
if (!first.isPresent()) {
ErrorLog.errorDev("查询券码服务失败,无核销记录");
return null;
}
RedeemReportQueryResponse redeemReport = first.get();
List<String> couponCodes = Arrays.asList(redeemReport.getCouponCode().split(","));
boolean canceled = saasResponse.getResult().getList().stream()
.anyMatch(resp -> ThirdCouponReportTypeEnum.REVERSE.getType().equals(resp.getRedeemType()) && ThirdCouponReportStatusEnum.SUCCESS.getStatus().equals(resp.getRedeemStatus()));
ThirdCouponDto thirdCouponDto = new ThirdCouponDto();
thirdCouponDto.setCouponCodes(couponCodes);
thirdCouponDto.setCanceled(canceled);
thirdCouponDto.setPartnerId(redeemReport.getPartnerId());
thirdCouponDto.setStoreId(redeemReport.getStoreId());
thirdCouponDto.setMemberId(redeemReport.getMemberId());
thirdCouponDto.setChannel(redeemReport.getChannel());
thirdCouponDto.setRedeemTime(redeemReport.getRedeemTime());
if (CouponThirdChannelEnum.MEI_TUAN.getChannel().equals(redeemReport.getChannel())) {
thirdCouponDto.setEcologyChannelType(EcologyChannelTypeEnum.MEITUAN);
} else if (CouponThirdChannelEnum.DOU_YIN.getChannel().equals(redeemReport.getChannel())) {
// 未知渠道的为早期对接抖音时的数据,默认为抖音渠道
thirdCouponDto.setEcologyChannelType(EcologyChannelTypeEnum.TIKTOK);
}
return thirdCouponDto;
}
/**
* 三方券支付冲正
* <pre></pre>
* @param request
* @param orderBean
*/
public void thirdCouponRefund(String partnerId, String orderCode, String operator) {
ThirdCouponOrderHandle.ThirdCouponDto coupon = this.getCoupon(partnerId, orderCode);
if (coupon == null) {
throw new RuntimeException("三方券信息查询失败,请重试");
}
if (coupon.isCanceled()) {
return;
}
if (ThirdCouponOrderHandle.isCancelTimeout(coupon.getRedeemTime(), coupon.getEcologyChannelType())) {
throw new RuntimeException("核销时间超过可冲正期限,撤销核销失败");
}
// 美团、抖音平台三方券
if (coupon.getEcologyChannelType() != null) {
CancelRedeemCouponRequest req = new CancelRedeemCouponRequest();
req.setTxn(orderCode);
req.setStoreId(coupon.getStoreId());
req.setMemberId(coupon.getMemberId());
req.setPartnerId(coupon.getPartnerId());
req.setChannel(coupon.getEcologyChannelType().getVal());
req.setOperator(operator);
coupon.getCouponCodes().forEach(code -> {
req.setCode(code);
Result<CancelRedeemCouponResponse> result = ecologyAdapterClient.couponCancel(req);
ApiLog.infoMessage("冲正三方券:req:{},resp:{}", JSONObject.toJSONString(req), JSONObject.toJSONString(result));
if (!result.isStatus()) {
throw new RuntimeException("撤销核销失败,请重试");
}
if (result.getResult().getCancelResult().equals(CancelRedeemCouponResponse.Result.TIME_LIMITED)) {
throw new RuntimeException("核销时间超过可冲正期限,撤销核销失败");
}
if (!result.getResult().getCancelResult().equals(CancelRedeemCouponResponse.Result.SUCCESS)) {
throw new RuntimeException("撤销核销失败,请重试");
}
});
}
try {
// 记录冲正明细
ReverseThirdCouponRedeemReportReq couponRequest = new ReverseThirdCouponRedeemReportReq();
couponRequest.setPartnerId(coupon.getPartnerId());
couponRequest.setChannel(coupon.getChannel());
couponRequest.setOrderCode(orderCode);
couponRequest.setRedeemStatus(ThirdCouponReportStatusEnum.SUCCESS.getStatus());
SaasResponse<Object> reverseResp = couponOfflineClient.reverseThirdCouponRedeemReport(couponRequest);
ApiLog.infoMessage("冲正三方券记录明细:req:{},resp:{}", JSONObject.toJSONString(couponRequest), JSONObject.toJSONString(reverseResp));
} catch (Exception e) {
ErrorLog.errorDev("冲正三方券记录明细异常:", e, e.getMessage());
}
}
@Data
public static class ThirdCouponDto {
/**
* 券渠道
*/
private Short channel;
/**
* 券渠道
*/
@Nullable
private EcologyChannelTypeEnum ecologyChannelType;
/**
* 券号列表
*/
private List<String> couponCodes;
/**
* 是否已冲正订单
*/
private boolean canceled;
/**
* 商户号
*/
private String partnerId;
/**
* 核销门店 id
*/
private String storeId;
/**
* 会员 id
*/
private String memberId;
/**
* 核销或冲正时间
*/
private Date redeemTime;
}
}
...@@ -10,15 +10,21 @@ import cn.freemud.management.entities.dto.response.order.OrderManagerResponse; ...@@ -10,15 +10,21 @@ import cn.freemud.management.entities.dto.response.order.OrderManagerResponse;
import cn.freemud.management.entities.dto.response.pay.PayRefundResponse; import cn.freemud.management.entities.dto.response.pay.PayRefundResponse;
import cn.freemud.management.enums.OperateType; import cn.freemud.management.enums.OperateType;
import cn.freemud.management.enums.*; import cn.freemud.management.enums.*;
import cn.freemud.management.enums.coupon.ThirdCouponReportStatusEnum;
import cn.freemud.management.handler.AssisantApplicationHandler; import cn.freemud.management.handler.AssisantApplicationHandler;
import cn.freemud.management.service.OrderBaseService; import cn.freemud.management.service.OrderBaseService;
import cn.freemud.management.service.OrderManagerService; import cn.freemud.management.service.OrderManagerService;
import cn.freemud.management.service.handle.*; import cn.freemud.management.service.handle.*;
import cn.freemud.management.thirdparty.ConsoleApiClient; import cn.freemud.management.thirdparty.*;
import cn.freemud.management.thirdparty.OrderCallbackClient; import cn.freemud.management.thirdparty.request.coupon.ReverseThirdCouponRedeemReportReq;
import cn.freemud.management.thirdparty.request.ecology.adapter.CancelRedeemCouponRequest;
import cn.freemud.management.thirdparty.response.Result;
import cn.freemud.management.thirdparty.response.coupon.SaasResponse;
import cn.freemud.management.thirdparty.response.ecology.adapter.CancelRedeemCouponResponse;
import cn.freemud.management.util.ResponseUtil; import cn.freemud.management.util.ResponseUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.freemud.application.sdk.api.log.ApiLog; import com.freemud.application.sdk.api.log.ApiLog;
import com.freemud.application.sdk.api.log.ErrorLog;
import com.freemud.application.sdk.api.log.LogThreadLocal; import com.freemud.application.sdk.api.log.LogThreadLocal;
import com.freemud.application.sdk.api.ordercenter.config.OrderCenterProperties; import com.freemud.application.sdk.api.ordercenter.config.OrderCenterProperties;
import com.freemud.application.sdk.api.ordercenter.entities.v1.OrderBeanV1; import com.freemud.application.sdk.api.ordercenter.entities.v1.OrderBeanV1;
...@@ -98,6 +104,15 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -98,6 +104,15 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
@Autowired @Autowired
private ConsoleApiClient consoleApiClient; private ConsoleApiClient consoleApiClient;
@Autowired
private ThirdCouponOrderHandle thirdCouponOrderHandle;
@Autowired
private EcologyAdapterClient ecologyAdapterClient;
@Autowired
private CouponOfflineClient couponOfflineClient;
/** /**
* 接单 * 接单
* *
...@@ -209,6 +224,15 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -209,6 +224,15 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
*/ */
@Override @Override
public BaseResponse<OrderManagerResponse> orderReject(OrderManagerRequest request, OrderBeanV1 orderBean) { public BaseResponse<OrderManagerResponse> orderReject(OrderManagerRequest request, OrderBeanV1 orderBean) {
// 三方券支付冲正
if (ThirdCouponOrderHandle.isThirdCouponOrder(orderBean)) {
try {
thirdCouponOrderHandle.thirdCouponRefund(request.getPartnerId(), request.getOrderId(), request.getOperator());
} catch (Exception e) {
return ResponseUtil.error(ResponseResult.REFUND_THIRD_COUPON_TIME_LIMIT.getCode(), e.getMessage());
}
}
PayRefundResponse refundResponse = null; PayRefundResponse refundResponse = null;
// 支付退款 包括线上小程序付款的订单,订单宝其他支付方式收款(汇来米,收钱吧,现金,其他) // 支付退款 包括线上小程序付款的订单,订单宝其他支付方式收款(汇来米,收钱吧,现金,其他)
refundResponse = this.payRefundCommon(request, orderBean); refundResponse = this.payRefundCommon(request, orderBean);
...@@ -243,6 +267,16 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -243,6 +267,16 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
public BaseResponse<OrderManagerResponse> orderAgreeRefund(OrderManagerRequest request, OrderBeanV1 orderBean) { public BaseResponse<OrderManagerResponse> orderAgreeRefund(OrderManagerRequest request, OrderBeanV1 orderBean) {
//取消配送单 //取消配送单
deliveryHandle.cancelDelivery(orderBean, request); deliveryHandle.cancelDelivery(orderBean, request);
// 三方券支付冲正
if (ThirdCouponOrderHandle.isThirdCouponOrder(orderBean)) {
try {
thirdCouponOrderHandle.thirdCouponRefund(request.getPartnerId(), request.getOrderId(), request.getOperator());
} catch (Exception e) {
return ResponseUtil.error(ResponseResult.REFUND_THIRD_COUPON_TIME_LIMIT.getCode(), e.getMessage());
}
}
// 支付退款 包括线上小程序付款的订单,订单宝其他支付方式收款(汇来米,收钱吧,现金,其他)老板通发起退款 // 支付退款 包括线上小程序付款的订单,订单宝其他支付方式收款(汇来米,收钱吧,现金,其他)老板通发起退款
PayRefundResponse refundResponse = this.payRefundCommon(request, orderBean); PayRefundResponse refundResponse = this.payRefundCommon(request, orderBean);
// 退款异常 // 退款异常
...@@ -270,7 +304,6 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -270,7 +304,6 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
} }
return ResponseUtil.success(); return ResponseUtil.success();
} }
/** /**
* 拒单退款、同意退款、取消订单退款 * 拒单退款、同意退款、取消订单退款
* 小程序点餐订单退款,无单收银退款,订单宝扫码付、其他支付方式收款(汇来米,收钱吧,现金,其他)等有支付流水等退款 * 小程序点餐订单退款,无单收银退款,订单宝扫码付、其他支付方式收款(汇来米,收钱吧,现金,其他)等有支付流水等退款
...@@ -395,6 +428,9 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -395,6 +428,9 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
if (PAY_RETURN_FILTER_CODES.contains(baseResponse.getCode())) { if (PAY_RETURN_FILTER_CODES.contains(baseResponse.getCode())) {
return ResponseUtil.error(ResponseResult.getResponseResult(baseResponse.getCode())); return ResponseUtil.error(ResponseResult.getResponseResult(baseResponse.getCode()));
} }
if (ResponseResult.REFUND_THIRD_COUPON_TIME_LIMIT.getCode().equals(baseResponse.getCode())) {
return baseResponse;
}
ApiLog.infoMessage("商家取消订单失败, 请求json:{} 返回msg:{}", JSONObject.toJSONString(orderBean), baseResponse.getMessage()); ApiLog.infoMessage("商家取消订单失败, 请求json:{} 返回msg:{}", JSONObject.toJSONString(orderBean), baseResponse.getMessage());
emailAlertService.sendEmailAlert("商家取消订单失败", "请求json:" + JSONObject.toJSONString(orderBean) + "返回msg:" + baseResponse.getMessage()); emailAlertService.sendEmailAlert("商家取消订单失败", "请求json:" + JSONObject.toJSONString(orderBean) + "返回msg:" + baseResponse.getMessage());
...@@ -438,6 +474,14 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService { ...@@ -438,6 +474,14 @@ public class SaasOrderMangerServiceImpl implements OrderManagerService {
} }
public BaseResponse<OrderManagerResponse> posCancel(OrderBeanV1 order, OrderManagerRequest request) { public BaseResponse<OrderManagerResponse> posCancel(OrderBeanV1 order, OrderManagerRequest request) {
// 三方券支付冲正
if (ThirdCouponOrderHandle.isThirdCouponOrder(order)) {
try {
thirdCouponOrderHandle.thirdCouponRefund(request.getPartnerId(), request.getOrderId(), request.getOperator());
} catch (Exception e) {
return ResponseUtil.error(ResponseResult.REFUND_THIRD_COUPON_TIME_LIMIT.getCode(), e.getMessage());
}
}
//调用cancel接口,商家退款 //调用cancel接口,商家退款
OrderCancelReq orderCancelReq = new OrderCancelReq(); OrderCancelReq orderCancelReq = new OrderCancelReq();
orderCancelReq.setPartnerId(order.getCompanyId()); orderCancelReq.setPartnerId(order.getCompanyId());
......
/**
* All rights Reserved, Designed By www.freemud.cn
*
* @Title: CouponQueryClient
* @Package cn.freemud.service.thirdparty
* @Description:
* @author: song.cai
* @date: 2019/2/20 16:42
* @version V1.0
* @Copyright: 2019 www.freemud.cn Inc. All rights reserved.
* 注意:本内容仅限于上海非码科技内部传阅,禁止外泄以及用于其他的商业目
*/
package cn.freemud.management.thirdparty;
import cn.freemud.management.thirdparty.request.coupon.RedeemReportQueryRequest;
import cn.freemud.management.thirdparty.request.coupon.ReverseThirdCouponRedeemReportReq;
import cn.freemud.management.thirdparty.response.coupon.RedeemReportQueryResponse;
import cn.freemud.management.thirdparty.response.coupon.SaasPageResponse;
import cn.freemud.management.thirdparty.response.coupon.SaasResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(name = "OPEN-STORE-COUPON-BACKSTAGE-SERVICE", url = "${saas.coupon-backstage-service.feign.url:}")
@RequestMapping(produces = {"application/json;charset=UTF-8"})
public interface CouponBackstageClient {
/**
* 查询三方券核销记录列表
* <pre></pre>
* @param request
* @return
*/
@PostMapping("thirdChannelCoupon/redeemReport/query")
SaasResponse<SaasPageResponse<RedeemReportQueryResponse>> queryCodesInfo(RedeemReportQueryRequest request);
}
package cn.freemud.management.thirdparty;
import cn.freemud.management.thirdparty.request.coupon.ReverseThirdCouponRedeemReportReq;
import cn.freemud.management.thirdparty.response.coupon.SaasResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
@FeignClient(name = "OPEN-STORE-OFFLINE-COUPON-SERVICE")
public interface CouponOfflineClient {
/**
* 冲正三方券核销明细
* <pre></pre>
* @param couponRequest
* @return
*/
@PostMapping(value = "/thirdChannelCoupon/redeemReport/reverse")
SaasResponse<Object> reverseThirdCouponRedeemReport(ReverseThirdCouponRedeemReportReq couponRequest);
}
package cn.freemud.management.thirdparty;
import cn.freemud.management.thirdparty.request.ecology.adapter.CancelRedeemCouponRequest;
import cn.freemud.management.thirdparty.response.Result;
import cn.freemud.management.thirdparty.response.ecology.adapter.CancelRedeemCouponResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
@FeignClient(name = "ECOLOGY-ADAPTER")
public interface EcologyAdapterClient {
@PostMapping(value = "/ecologyadapter/coupon/cancel")
Result<CancelRedeemCouponResponse> couponCancel(CancelRedeemCouponRequest req);
}
package cn.freemud.management.thirdparty.request.coupon;
import cn.freemud.management.enums.coupon.CouponThirdChannelEnum;
import cn.freemud.management.enums.coupon.CouponThirdTypeEnum;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import java.util.Date;
/**
* &copy; All rights Reserved, Designed By www.freemud.cn
*
* @author wénkǎi.zhāng 2023-08-24
* @since 1.0
*/
@Data
public class RedeemReportQueryRequest {
@ApiModelProperty(value = "商户号", required = true, example = "1864")
@NotBlank(message = "商户号不能为空")
private String partnerId;
@ApiModelProperty(value = "分页查询页码,默认第1页")
@Min(value = 1, message = "分页查询页码必须为大于零的整数")
private int pageNum = 1;
@ApiModelProperty(value = "分页查询单页数据条数,默认10条")
@Min(value = 1, message = "分页查询单页数据条数必须为大于零的整数")
private int pageSize = 10;
@ApiModelProperty(value = "核销时间(起)", required = true, example = "2023-08-31 23:59:59")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date beginTime;
@ApiModelProperty(value = "核销时间(止)", required = true, example = "2023-08-31 23:59:59")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
@ApiModelProperty(value = CouponThirdChannelEnum.DESC, example = "2")
private Short channel;
@ApiModelProperty(value = "门店id", example = "382349534-83635444-353463426463545")
private String storeId;
@ApiModelProperty(value = CouponThirdTypeEnum.DESC, example = "0")
private Short couponType;
@ApiModelProperty(value = "三方券号", example = "876372838748325")
private String couponCode;
/**
* 核销场景{0:未知,历史数据;1:订单宝;2:小程序;}
* @ApiModelProperty(value = ThirdCouponRedeemSceneEnum.DESC, example = "0")
*/
private Short redeemCouponScene;
private String orderCode;
@ApiModelProperty(value = "三方订单号")
private String thirdOrderCode;
@ApiModelProperty(value = "是否按核销时间倒序,true:是(默认);false:正序", notes = "导出报表时建议按正序,防止翻页查询时新记产生导致分页数据错乱")
private boolean orderByRedeemTimeDesc = true;
}
package cn.freemud.management.thirdparty.request.coupon;
import cn.freemud.management.enums.coupon.ThirdCouponReportStatusEnum;
import lombok.Data;
/**
* &copy; All rights Reserved
*
* @author wénkǎi.zhāng 2023-11-29
* @since 1.0
*/
@Data
public class ReverseThirdCouponRedeemReportReq {
/**
* 商户号
*/
private String partnerId;
/**
* {@link cn.freemud.management.enums.coupon.CouponThirdChannelEnum#channel}
*/
private Short channel;
/**
* 订单编号
*/
private String orderCode;
/**
* 是否冲正成功,1-成功,2-失败,不传默认成功
* {@link ThirdCouponReportStatusEnum#status}
*/
private Short redeemStatus = 1;
}
package cn.freemud.management.thirdparty.request.ecology.adapter;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author Clover.z
* @version 1.0.0
* @since 1.0.0
*/
@Data
public class BaseRequest {
/**
* 商户号
*/
@ApiModelProperty(value = "商户号", required = true)
@NotBlank
private String partnerId;
@ApiModelProperty(value = "业务渠道, 1-微信;2-支付宝;5-抖音;10-美团点评;15-饿了么", required = true)
@NotNull(message = "业务渠道不能为空")
private Integer channel;
@ApiModelProperty(value = "操作人")
private String operator;
}
package cn.freemud.management.thirdparty.request.ecology.adapter;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotBlank;
/**
* @author Clover.z
* @version 1.0.0
* @since 1.0.0
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class CancelRedeemCouponRequest extends BaseRequest {
@ApiModelProperty(value = "交易流水号", required = true)
@NotBlank
private String txn;
@ApiModelProperty(value = "券撤销非码门店ID", required = true)
private String storeId;
@ApiModelProperty(value = "券撤销非码会员ID", required = true)
private String memberId;
@ApiModelProperty(value = "撤销券号", required = true)
private String code;
}
package cn.freemud.management.thirdparty.response;
import lombok.Data;
/**
* &copy; All rights Reserved
*
* @author wénkǎi.zhāng 2024-02-29
* @since 1.0
*/
@Data
public class Result<T> {
private boolean status = false;
private String statusCode;
private String message;
private T result;
}
package cn.freemud.management.thirdparty.response.coupon;
import cn.freemud.management.enums.coupon.CouponThirdChannelEnum;
import cn.freemud.management.enums.coupon.CouponThirdTypeEnum;
import cn.freemud.management.enums.coupon.ThirdCouponReportStatusEnum;
import cn.freemud.management.enums.coupon.ThirdCouponReportTypeEnum;
import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* &copy; All rights Reserved, Designed By www.freemud.cn
*
* @author wénkǎi.zhāng 2023-08-24
* @since 1.0
*/
@Data
public class RedeemReportQueryResponse {
@ApiModelProperty(value = "核销时间", required = true, example = "2023-08-31 10:50:50")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@JSONField(format = "yyyy-MM-dd HH:mm:ss")
private Date redeemTime;
@ApiModelProperty(value = CouponThirdChannelEnum.DESC, required = true, example = "2")
private Short channel;
@ApiModelProperty(value = ThirdCouponReportStatusEnum.DESC, required = true, example = "2")
private Short redeemStatus;
@ApiModelProperty(value = ThirdCouponReportTypeEnum.DESC, required = true, example = "2")
private Short redeemType;
private Short redeemCouponScene;
@ApiModelProperty(value = "券核销张数")
private Integer couponNum;
@ApiModelProperty(value = "商户号")
private String partnerId;
@ApiModelProperty(value = "核销门店id", example = "382349534-83635444-353463426463545")
private String storeId;
@ApiModelProperty(value = "核销门店编号", example = "c08222")
private String storeCode;
@ApiModelProperty(value = "核销门店名称", example = "xxx门店")
private String storeName;
@ApiModelProperty(value = CouponThirdTypeEnum.DESC, required = true, example = "0")
private Short couponType;
@ApiModelProperty(value = "三方券号", required = true, example = "876372838748325")
private String couponCode;
@ApiModelProperty(value = "券名称", required = true, example = "鱼香肉丝套餐券")
private String couponName;
@ApiModelProperty(value = "券优惠金额,单位:分", required = true, example = "1000")
private Integer discountAmount;
@ApiModelProperty(value = "订单编号", example = "10002937273295444")
private String orderCode;
@ApiModelProperty(value = "三方单号", example = "12345678")
private String thirdOrderCode;
@ApiModelProperty(value = "会员 id", example = "12345678")
private String memberId;
}
package cn.freemud.management.thirdparty.response.coupon;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* &copy; All rights Reserved, Designed By www.freemud.cn
*
* @author wénkǎi.zhāng 2023-05-15
* @since 1.0
*/
@Data
public class SaasPageResponse<T> {
@ApiModelProperty("总记录数")
private long totalCount;
@ApiModelProperty("分页查询单页数据条数")
private int pageSize;
@ApiModelProperty("分页查询页码")
private int pageNum;
@ApiModelProperty("总页数")
private int totalPages;
@ApiModelProperty("数据列表")
private List<T> list;
}
package cn.freemud.management.thirdparty.response.coupon;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* &copy; All rights Reserved, Designed By www.freemud.cn
*
* @author wénkǎi.zhāng 2023-05-09
* @since 1.0
*/
@Data
public class SaasResponse<T> {
@ApiModelProperty(value = "请求状态,100 为成功", required = true, example = "100")
private Integer statusCode;
@ApiModelProperty(value = "状态码描述", required = true, example = "成功")
private String message;
@ApiModelProperty("业务数据")
private T result;
}
package cn.freemud.management.thirdparty.response.ecology.adapter;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* &copy; All rights Reserved
*
* @author wénkǎi.zhāng 2024-03-01
* @since 1.0
*/
@Data
public class CancelRedeemCouponResponse {
@ApiModelProperty("冲正结果")
private Result cancelResult;
public enum Result{
/**
* 成功
*/
SUCCESS,
/**
* 超过撤销时限不可撤销
*/
TIME_LIMITED,
/**
* 未知错误
*/
UNKNOWN_ERROR
}
}
...@@ -5,7 +5,9 @@ public enum CashTypeEnum { ...@@ -5,7 +5,9 @@ public enum CashTypeEnum {
OTHER("10", "其他"), OTHER("10", "其他"),
HLM("50", "汇来米"), HLM("50", "汇来米"),
SQB("51", "收钱吧"), SQB("51", "收钱吧"),
CASH("52", "现金"); CASH("52", "现金"),
THIRD_COUPON("53", "三方券支付"),
;
private String code; private String code;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment