预订单时间槽列表逻辑调整,分账、提现 报文字段 保存
This commit is contained in:
parent
7810340cde
commit
0248a38268
@ -155,6 +155,9 @@ public class LklOrderDraw {
|
||||
@ApiModelProperty(value = "异步通知地址", example = "https://api.example.com/notify")
|
||||
private String notify_url;
|
||||
|
||||
@ApiModelProperty(value = "接口请求报文")
|
||||
private String lkl_req;
|
||||
|
||||
/**
|
||||
* 异步通知返回的JSON数据
|
||||
*/
|
||||
|
||||
@ -94,6 +94,9 @@ public class LklOrderSeparate {
|
||||
@ApiModelProperty(value = "处理状态:ACCEPTED-已受理, PROCESSING-处理中, FAIL-失败, SUCCESS-成功")
|
||||
private String final_status;
|
||||
|
||||
@ApiModelProperty(value = "接口请求报文")
|
||||
private String lkl_req;
|
||||
|
||||
@ApiModelProperty(value = "异步通知数据")
|
||||
private String notify_resp;
|
||||
|
||||
|
||||
@ -10,8 +10,7 @@ import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeFormatterBuilder;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
public class DateTimeUtils {
|
||||
@ -363,6 +362,76 @@ public class DateTimeUtils {
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算多个时间段之间的交集(不跨天)
|
||||
* <p>
|
||||
* 算法逻辑:
|
||||
* 1. 遍历所有时间段,找到最晚的开始时间和最早的结束时间
|
||||
* 2. 如果最晚开始时间小于等于最早结束时间,则存在交集
|
||||
* 3. 如果最晚开始时间大于最早结束时间,则不存在交集
|
||||
*
|
||||
* @param timeList 时间段列表,每个时间段是一个Map,包含开始时间startTimeStr和结束时间endTimeStr
|
||||
* startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||
* endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||
* @return 返回一个Map,包含交集的时间段(startTimeStr和endTimeStr),如果无交集则返回空Map
|
||||
*/
|
||||
public static Map<String, String> findTimeInterSection(List<Map<String, String>> timeList) {
|
||||
// 参数校验
|
||||
if (timeList == null || timeList.isEmpty()) {
|
||||
log.warn("时间段列表为空或null,无法计算交集");
|
||||
return new HashMap<>();
|
||||
}
|
||||
|
||||
try {
|
||||
LocalTime latestStartTime = null;
|
||||
LocalTime earliestEndTime = null;
|
||||
|
||||
// 遍历所有时间段
|
||||
for (Map<String, String> timeMap : timeList) {
|
||||
if (timeMap == null || !timeMap.containsKey("startTimeStr") || !timeMap.containsKey("endTimeStr")) {
|
||||
log.warn("时间段数据不完整或格式不正确: {}", timeMap);
|
||||
continue;
|
||||
}
|
||||
|
||||
String startTimeStr = timeMap.get("startTimeStr");
|
||||
String endTimeStr = timeMap.get("endTimeStr");
|
||||
|
||||
if (startTimeStr == null || endTimeStr == null) {
|
||||
log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
LocalTime startTime = parseTime(startTimeStr);
|
||||
LocalTime endTime = parseTime(endTimeStr);
|
||||
|
||||
// 更新最晚开始时间和最早结束时间
|
||||
if (latestStartTime == null || startTime.isAfter(latestStartTime)) {
|
||||
latestStartTime = startTime;
|
||||
}
|
||||
|
||||
if (earliestEndTime == null || endTime.isBefore(earliestEndTime)) {
|
||||
earliestEndTime = endTime;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否存在交集
|
||||
if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("startTimeStr", latestStartTime.toString());
|
||||
result.put("endTimeStr", earliestEndTime.toString());
|
||||
return result;
|
||||
} else {
|
||||
// 无交集情况
|
||||
log.debug("给定的时间段列表无交集");
|
||||
return new HashMap<>();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("计算时间段交集时发生异常", e);
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断指定时间是否在两个时间点之间(包含边界)
|
||||
*
|
||||
@ -537,14 +606,52 @@ public class DateTimeUtils {
|
||||
// System.out.println(formatLocalDate(LocalDate.now(), "yyyy-MM-dd"));
|
||||
|
||||
// 判断当前时间是否在工作时间(9:00-18:00)内
|
||||
boolean isWorkTime = isCurrentTimeInRange("09:00", "22:36");
|
||||
// boolean isWorkTime = isCurrentTimeInRange("09:00", "22:36");
|
||||
|
||||
// 判断特定时间是否在夜间时间(22:00-06:00)内
|
||||
// LocalDateTime testTime = LocalDateTime.of(2025, 1, 1, 23, 30);
|
||||
Date testTime = Date.from(LocalDateTime.of(2025, 10, 23, 21, 30).atZone(ZoneId.systemDefault()).toInstant());
|
||||
boolean isNight = isTimeInRange("08:30", "22:20", testTime);
|
||||
// Date testTime = Date.from(LocalDateTime.of(2025, 10, 23, 21, 30).atZone(ZoneId.systemDefault()).toInstant());
|
||||
// boolean isNight = isTimeInRange("08:30", "22:20", testTime);
|
||||
|
||||
// System.out.println("当前时间是否在工作时间内:" + isWorkTime);
|
||||
// System.out.println("多个时间段的交集结果:" + isNight);
|
||||
|
||||
System.out.println("=== 测试 findTimeIntersection ===");
|
||||
|
||||
// 测试正常交集情况
|
||||
List<Map<String, String>> timeList1 = new ArrayList<>();
|
||||
Map<String, String> range1 = new HashMap<>();
|
||||
range1.put("startTimeStr", "06:00");
|
||||
range1.put("endTimeStr", "17:00");
|
||||
timeList1.add(range1);
|
||||
|
||||
Map<String, String> range2 = new HashMap<>();
|
||||
range2.put("startTimeStr", "06:00");
|
||||
range2.put("endTimeStr", "17:00");
|
||||
timeList1.add(range2);
|
||||
|
||||
Map<String, String> intersection1 = findTimeInterSection(timeList1);
|
||||
System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
|
||||
|
||||
// 测试无交集情况
|
||||
List<Map<String, String>> timeList2 = new ArrayList<>();
|
||||
Map<String, String> range3 = new HashMap<>();
|
||||
range3.put("startTimeStr", "09:00");
|
||||
range3.put("endTimeStr", "12:00");
|
||||
timeList2.add(range3);
|
||||
|
||||
Map<String, String> range4 = new HashMap<>();
|
||||
range4.put("startTimeStr", "13:00");
|
||||
range4.put("endTimeStr", "17:00");
|
||||
timeList2.add(range4);
|
||||
|
||||
Map<String, String> intersection2 = findTimeInterSection(timeList2);
|
||||
System.out.println("交集结果2: " + intersection2); // 应该是空Map
|
||||
|
||||
// 测试空列表
|
||||
Map<String, String> intersection3 = findTimeInterSection(null);
|
||||
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是空Map
|
||||
|
||||
|
||||
System.out.println("当前时间是否在工作时间内:" + isWorkTime);
|
||||
System.out.println("特定时间是否在夜间时间段内:" + isNight);
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.suisung.mall.common.api.ResultCode;
|
||||
import com.suisung.mall.common.api.StateCode;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.domain.UserDto;
|
||||
import com.suisung.mall.common.exception.ApiException;
|
||||
import com.suisung.mall.common.exception.ApiUserException;
|
||||
@ -648,8 +649,10 @@ public class ShopActivityGroupbookingServiceImpl extends BaseServiceImpl<ShopAct
|
||||
for (ShopActivityGroupbookingHistory history : historyList) {
|
||||
// 已支付
|
||||
ShopOrderInfo order_info_data = shopOrderInfoService.get(history.getOrder_id());
|
||||
if (history.getGbh_flag() == 1) {
|
||||
boolean used = shopUserVoucherService.useNeedNotPin(history.getUser_id(), history.getOrder_id());
|
||||
if (history.getGbh_flag() == 1) { // 已支付
|
||||
|
||||
// 是否使用了免拼券
|
||||
boolean used = shopUserVoucherService.tryUseFreeGroupVoucher(history.getUser_id(), history.getOrder_id());
|
||||
|
||||
if (used) {
|
||||
if (order_info_data.getOrder_is_sync() == 2) {
|
||||
@ -664,30 +667,33 @@ public class ShopActivityGroupbookingServiceImpl extends BaseServiceImpl<ShopAct
|
||||
// 如果使用免拼券成功,则改变参团人员拼单状态为成功.不往下走。
|
||||
ShopActivityGroupbookingHistory groupbookingHistory = new ShopActivityGroupbookingHistory();
|
||||
groupbookingHistory.setGbh_id(history.getGbh_id());
|
||||
groupbookingHistory.setGb_enable(1);
|
||||
groupbookingHistory.setGb_enable(CommonConstant.Enable);
|
||||
if (!shopActivityGroupbookingHistoryService.edit(groupbookingHistory)) {
|
||||
throw new ApiException(I18nUtil._("修正订单为可同步状态失败!"));
|
||||
}
|
||||
|
||||
free_fight_num++;
|
||||
|
||||
// TODO 平团成功,需要发同城配送
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
|
||||
List<String> order_ids = Convert.toList(String.class, history.getOrder_id());
|
||||
|
||||
//只有一条记录
|
||||
List<ShopOrderInfo> shopOrderInfos = shopOrderInfoService.gets(order_ids);
|
||||
List<Map> rows = Convert.toList(Map.class, shopOrderInfos);
|
||||
|
||||
// 取消订单
|
||||
flag = shopOrderBaseService.cancel(order_ids, rows, false);
|
||||
//已经调用cancelActivity, 重复
|
||||
if (flag) {
|
||||
// 改变参团人员拼单状态
|
||||
ShopActivityGroupbookingHistory groupbookingHistory = new ShopActivityGroupbookingHistory();
|
||||
groupbookingHistory.setGbh_id(history.getGbh_id());
|
||||
groupbookingHistory.setGb_enable(0);
|
||||
groupbookingHistory.setGb_enable(CommonConstant.Disable);
|
||||
if (!shopActivityGroupbookingHistoryService.edit(groupbookingHistory)) {
|
||||
throw new ApiException(I18nUtil._("改变参团人员拼单状态失败!"));
|
||||
}
|
||||
@ -727,12 +733,10 @@ public class ShopActivityGroupbookingServiceImpl extends BaseServiceImpl<ShopAct
|
||||
orderReturn.setReturn_tel("");
|
||||
orderReturn.setReturn_store_user_id(buyer_store_id);
|
||||
orderReturn.setReturn_telephone("");
|
||||
|
||||
orderReturn.setSubsite_id(order_info_data.getSubsite_id());
|
||||
|
||||
|
||||
ShopOrderReturnItem orderReturnItem = new ShopOrderReturnItem();
|
||||
|
||||
orderReturnItem.setOrder_item_id(order_item_row.getOrder_item_id()); // 退货商品编号(DOT):0为退款
|
||||
orderReturnItem.setOrder_id(history.getOrder_id()); // 订单编号
|
||||
orderReturnItem.setReturn_item_num(order_item_row.getOrder_item_quantity()); // 退货商品编号(DOT):0为退款
|
||||
@ -760,7 +764,7 @@ public class ShopActivityGroupbookingServiceImpl extends BaseServiceImpl<ShopAct
|
||||
}
|
||||
}
|
||||
|
||||
//如果免拼数量等于成团数量,则此团完成
|
||||
//如果使用免拼券人数等于成团数量,则此团完成
|
||||
if (gb_quantity <= free_fight_num) {
|
||||
groupbooking.setGb_enable(1);
|
||||
shopActivityGroupbookingService.saveOrUpdate(groupbooking);
|
||||
|
||||
@ -176,19 +176,7 @@ public interface LakalaApiService {
|
||||
* @return
|
||||
*/
|
||||
CommonResult getBankCardBin(String bankCardNo);
|
||||
|
||||
|
||||
/**
|
||||
* 拉卡拉订单分账,用户下单成功之后,进行分账
|
||||
* 说明:分账指令是异步处理模式,响应报文成功时,指令状态是”status”: “PROCESSING”,需要等待分账结果通知,或者主动发起查询,建议主动发起查询与分账指令动作之间间隔15秒以上。
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=389
|
||||
*
|
||||
* @param orderId 平台订单号,必填参数
|
||||
* @param storeId 商户门店编号,非必填参数
|
||||
* @return
|
||||
*/
|
||||
Pair<Boolean, String> innerDoOrderSeparate(String orderId, String storeId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据商户号、交易号和收货流水号执行订单分账操作
|
||||
* <p>
|
||||
|
||||
@ -65,7 +65,6 @@ import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Slf4j
|
||||
@ -1838,7 +1837,7 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
||||
|
||||
// 10. 检查商户绑定状态是否完成, 更改总的审核状态
|
||||
shopMchEntryService.checkMerchEntryFinished(mchId);
|
||||
|
||||
|
||||
// 11. 日志记录并返回成功响应
|
||||
log.info("商家绑定分账接收方异步通知处理完成,mchId:{} merCupNo:{}", mchId, merCupNo);
|
||||
return JSONUtil.createObj().set("code", "SUCCESS").set("message", "分账接收方绑定成功");
|
||||
@ -2265,270 +2264,6 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 执行拉卡拉订单分账操作
|
||||
* <p>
|
||||
* 用户确认收货成功之后(大约15秒后),进行分账操作。
|
||||
* 分账指令是异步处理模式,响应报文成功时,指令状态是"status": "PROCESSING",
|
||||
* 需要等待分账结果通知,或者主动发起查询。
|
||||
* 建议主动发起查询与分账指令动作之间间隔15秒以上。
|
||||
* </p>
|
||||
* <p>
|
||||
* 参考文档:https://o.lakala.com/#/home/document/detail?id=389
|
||||
* </p>
|
||||
*
|
||||
* @param orderId 平台订单Id
|
||||
* @param storeId 店铺Id,可为空
|
||||
* @return Pair<Boolean, String> 处理结果对,first为是否成功,second为结果描述信息
|
||||
*/
|
||||
@Override
|
||||
public Pair<Boolean, String> innerDoOrderSeparate(String orderId, String storeId) {
|
||||
// 1. 输入参数校验
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
log.warn("[分账操作] 参数校验失败:订单号为空");
|
||||
return Pair.of(false, "订单号不能为空");
|
||||
}
|
||||
|
||||
try {
|
||||
// TODO 检查可分账余额是否足够?
|
||||
|
||||
// 2. 查询订单信息
|
||||
log.info("[分账操作] 开始执行订单[{}]分账操作", orderId);
|
||||
List<ShopOrderLkl> shopOrderLklList = shopOrderLklService.selectByOrderId(orderId, "", storeId);
|
||||
if (CollectionUtil.isEmpty(shopOrderLklList)) {
|
||||
log.warn("[分账操作] 失败:订单[{}]不存在", orderId);
|
||||
return Pair.of(false, "订单不存在");
|
||||
}
|
||||
|
||||
int totalCount = shopOrderLklList.size();
|
||||
int successCount = 0;
|
||||
StringBuilder errorMessages = new StringBuilder();
|
||||
|
||||
// 3. 初始化拉卡拉SDK
|
||||
initLKLSDK();
|
||||
|
||||
// 4. 遍历处理每个店铺订单的分账
|
||||
log.info("[分账操作] 订单[{}]包含{}个子订单,开始逐一处理", orderId, totalCount);
|
||||
for (ShopOrderLkl shopOrderLkl : shopOrderLklList) {
|
||||
log.debug("[分账操作] 处理子订单:storeId={}, subLogNo={}, receive_log_no={}", shopOrderLkl.getStore_id(), shopOrderLkl.getLkl_sub_log_no(), shopOrderLkl.getLkl_receive_log_no());
|
||||
|
||||
if (!CommonConstant.Enable.equals(shopOrderLkl.getReceive_status()) || StrUtil.isBlank(shopOrderLkl.getLkl_receive_log_no())) {
|
||||
log.warn("[分账操作] 订单[{}]对账流水号[{}]未被确认收货,跳过处理", orderId, shopOrderLkl.getLkl_receive_log_no());
|
||||
continue;
|
||||
}
|
||||
|
||||
// 5. 检查分账状态,避免重复处理
|
||||
LklOrderSeparate existingSeparateRecord = lklOrderSeparateService.getByLogNoAndOutTradeNo(shopOrderLkl.getLkl_sub_log_no(), orderId);
|
||||
if (existingSeparateRecord != null) {
|
||||
String status = existingSeparateRecord.getStatus();
|
||||
if ("SUCCESS".equals(status)) {
|
||||
log.info("[分账操作] 订单[{}]交易对账流水号[{}]已完成分账,跳过处理", orderId, shopOrderLkl.getLkl_sub_log_no());
|
||||
successCount++;
|
||||
continue;
|
||||
}
|
||||
if ("PROCESSING".equals(status) || "ACCEPTED".equals(status)) {
|
||||
log.info("[分账操作] 订单[{}]交易对账流水号[{}]分账处理中或已受理,跳过处理", orderId, shopOrderLkl.getLkl_sub_log_no());
|
||||
successCount++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 获取订单分账相关参数
|
||||
String merchantNo = shopOrderLkl.getLkl_merchant_no();
|
||||
|
||||
// 分账金额 = 应付总金额-运费(支付时已计算好)
|
||||
Integer splitAmount = shopOrderLkl.getSplit_amt();
|
||||
splitAmount = CheckUtil.isEmpty(splitAmount) ? 0 : splitAmount;
|
||||
|
||||
// 7. 分账金额校验
|
||||
if (splitAmount < 1) {
|
||||
String errorMsg = String.format("[分账操作] 店铺[%s]订单[%s]分账金额[%d]低于1分钱,跳过分账",
|
||||
shopOrderLkl.getStore_id(), orderId, splitAmount);
|
||||
log.error(errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
if (existingSeparateRecord != null) {
|
||||
lklOrderSeparateService.updateRemark(existingSeparateRecord.getId(), errorMsg);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取分账平台接收方信息
|
||||
LklLedgerMerReceiverBind platformReceiver = lklLedgerMerReceiverBindService.getPlatformByMerCupNo(merchantNo);
|
||||
|
||||
if (platformReceiver == null) {
|
||||
String errorMsg = String.format("[分账操作] 店铺[%s]未绑定平台方接收账户,跳过分账", shopOrderLkl.getStore_id());
|
||||
log.error(errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 8. 构建分账接收方列表
|
||||
List<V3SacsSeparateRecvDatas> recvDatas = new ArrayList<>();
|
||||
|
||||
// 9. 获取商家分账比例并校验
|
||||
BigDecimal merchantSplitRatioRaw = shopOrderLkl.getSplit_ratio(); // 如:94 代表94%
|
||||
// 判断商家分账比例是否有效(必须在(0, 100]范围内)
|
||||
boolean canSplitForMerchant = merchantSplitRatioRaw != null
|
||||
&& merchantSplitRatioRaw.compareTo(BigDecimal.ZERO) > 0
|
||||
&& merchantSplitRatioRaw.compareTo(new BigDecimal(100)) <= 0;
|
||||
if (!canSplitForMerchant) {
|
||||
String errorMsg = String.format("[分账操作] 店铺[%s]商家分账比例[%s]不在(0-100]范围内,无法分账",
|
||||
shopOrderLkl.getStore_id(), merchantSplitRatioRaw);
|
||||
log.error(errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
continue;
|
||||
}
|
||||
// 商家分账
|
||||
BigDecimal merchantSplitRatio = merchantSplitRatioRaw.divide(new BigDecimal(100)); // 比如:94%
|
||||
|
||||
BigDecimal distributorSplitRatio = BigDecimal.ZERO;
|
||||
BigDecimal platformSplitRatio = BigDecimal.ONE;
|
||||
// 分账代理商接收方信息
|
||||
List<LklLedgerMerReceiverBind> distributorReceivers = lklLedgerMerReceiverBindService.selectAgentByMerCupNo(merchantNo);
|
||||
if (distributorReceivers != null && !distributorReceivers.isEmpty()) {
|
||||
distributorSplitRatio = new BigDecimal("0.8");
|
||||
platformSplitRatio = new BigDecimal("0.2");
|
||||
}
|
||||
|
||||
// 记录关键分账参数,便于问题排查
|
||||
log.info("[分账操作] 参数信息:订单={}, 商户={}, 总金额={}分, 商家比例={}, 平台比例={}, 代理商比例={}, 是否有代理商={}",
|
||||
orderId, merchantNo, splitAmount, merchantSplitRatio, platformSplitRatio, distributorSplitRatio,
|
||||
(distributorReceivers != null && !distributorReceivers.isEmpty()));
|
||||
|
||||
// 返回值如下:{platformAmount=6, merchantAmount=94, agentAmount=0}
|
||||
Map<String, Integer> splitAmountMap = CommonUtil.calculateProfitSharing(splitAmount, merchantSplitRatio, platformSplitRatio, distributorSplitRatio);
|
||||
Integer merchantAmount = splitAmountMap.get("merchantAmount");
|
||||
Integer platformAmount = splitAmountMap.get("platformAmount");
|
||||
Integer agentAmount = splitAmountMap.get("agentAmount");
|
||||
|
||||
// 记录分账结果,便于问题排查
|
||||
log.info("[分账操作] 金额计算结果:订单={}, 商户={}, 总金额={}分, 商家分得={}分, 平台分得={}分, 代理商分得={}分",
|
||||
orderId, merchantNo, splitAmount, merchantAmount, platformAmount, agentAmount);
|
||||
|
||||
if (merchantAmount > 0) {
|
||||
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
|
||||
receiver.setRecvMerchantNo(merchantNo);
|
||||
receiver.setSeparateValue(merchantAmount.toString());
|
||||
recvDatas.add(receiver);
|
||||
}
|
||||
|
||||
if (platformAmount > 0) {
|
||||
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
|
||||
receiver.setRecvNo(platformReceiver.getReceiver_no());
|
||||
receiver.setSeparateValue(platformAmount.toString());
|
||||
recvDatas.add(receiver);
|
||||
}
|
||||
|
||||
if (agentAmount > 0 && distributorReceivers != null && !distributorReceivers.isEmpty()) {
|
||||
V3SacsSeparateRecvDatas receiver = new V3SacsSeparateRecvDatas();
|
||||
receiver.setRecvNo(distributorReceivers.get(0).getReceiver_no());
|
||||
receiver.setSeparateValue(agentAmount.toString());
|
||||
recvDatas.add(receiver);
|
||||
}
|
||||
|
||||
// 14. 构建分账请求对象
|
||||
V3SacsSeparateRequest separateRequest = new V3SacsSeparateRequest();
|
||||
separateRequest.setMerchantNo(merchantNo);
|
||||
separateRequest.setLogNo(shopOrderLkl.getLkl_receive_log_no()); // 合单和非合单的流水号保存在此字段
|
||||
separateRequest.setLogDate(shopOrderLkl.getLkl_log_date());
|
||||
separateRequest.setOutSeparateNo(shopOrderLkl.getOut_separate_no());
|
||||
separateRequest.setTotalAmt(splitAmount.toString());
|
||||
separateRequest.setLklOrgNo(orgCode);
|
||||
separateRequest.setCalType("0"); // 0- 按照指定金额,1- 按照指定比例。默认 0
|
||||
separateRequest.setNotifyUrl(projectDomain + "/api/mobile/shop/lakala/sacs/separateNotify");
|
||||
|
||||
// 15. 设置分账接收方列表
|
||||
separateRequest.setRecvDatas(recvDatas);
|
||||
log.info("[分账操作] 请求参数: 订单={}, 商户={}, 金额={}分, 分账接收方数量={}",
|
||||
orderId, merchantNo, splitAmount, recvDatas.size());
|
||||
|
||||
log.debug("[分账操作] 请求详细参数: {}", JSONUtil.toJsonStr(separateRequest));
|
||||
|
||||
// 16. 发送分账请求
|
||||
log.info("[分账操作] 向拉卡拉发送分账请求:订单={}, 商户={}, 分账流水号={}",
|
||||
orderId, merchantNo, shopOrderLkl.getLkl_sub_log_no());
|
||||
String response = LKLSDK.httpPost(separateRequest);
|
||||
if (StrUtil.isBlank(response)) {
|
||||
String errorMsg = String.format("[分账操作] 拉卡拉无响应,订单=%s,商户=%s,分账流水号=%s",
|
||||
orderId, merchantNo, shopOrderLkl.getLkl_sub_log_no());
|
||||
log.error(errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
continue;
|
||||
}
|
||||
|
||||
log.debug("[分账操作] 响应结果: {}", response);
|
||||
|
||||
// 17. 解析响应结果
|
||||
JSONObject respJson = JSONUtil.parseObj(response);
|
||||
if (respJson == null || !lklSacsSuccessCode.equals(respJson.getStr("code")) || respJson.getJSONObject("resp_data") == null) {
|
||||
String errorMsg = String.format("[分账操作] 拉卡拉返回格式异常,订单=%s,商户=%s,分账流水号=%s,响应=%s,respJson=%s",
|
||||
orderId, merchantNo, shopOrderLkl.getLkl_sub_log_no(), response, respJson);
|
||||
log.error(errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 18. 保存分账记录
|
||||
JSONObject respData = respJson.getJSONObject("resp_data");
|
||||
LklOrderSeparate separateRecord = new LklOrderSeparate();
|
||||
separateRecord.setSeparate_no(respData.getStr("separate_no"));
|
||||
separateRecord.setOut_separate_no(separateRequest.getOutSeparateNo());
|
||||
separateRecord.setMerchant_no(merchantNo);
|
||||
separateRecord.setLog_no(separateRequest.getLogNo()); // 发货完成交易流水号后14位 。分账商户用该流水号发起分账
|
||||
separateRecord.setLog_date(separateRequest.getLogDate());
|
||||
separateRecord.setOrder_id(shopOrderLkl.getOrder_id());
|
||||
separateRecord.setNotify_url(separateRequest.getNotifyUrl());
|
||||
separateRecord.setLkl_org_no(separateRequest.getLklOrgNo());
|
||||
separateRecord.setRecv_datas(JSONUtil.toJsonStr(separateRequest.getRecvDatas()));
|
||||
separateRecord.setStatus(respData.getStr("status"));
|
||||
separateRecord.setTotal_amt(separateRequest.getTotalAmt());
|
||||
separateRecord.setActual_separate_amt(Convert.toStr(shopOrderLkl.getSplit_amt()));
|
||||
|
||||
try {
|
||||
if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) {
|
||||
log.info("[分账操作] 记录保存成功:订单={}, 分账单号={}, 状态={}, 分账流水号={}",
|
||||
orderId, separateRecord.getSeparate_no(), separateRecord.getStatus(), separateRecord.getLog_no());
|
||||
successCount++;
|
||||
} else {
|
||||
String errorMsg = String.format("[分账操作] 保存分账记录失败,订单=%s,分账单号=%s,分账流水号=%s",
|
||||
orderId, separateRecord.getSeparate_no(), separateRecord.getLog_no());
|
||||
log.error(errorMsg);
|
||||
lklOrderSeparateService.updateRemark(separateRecord.getLog_no(), separateRecord.getSeparate_no(), errorMsg);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("[分账操作] 保存分账记录异常,订单=%s,分账单号=%s,流水号=%s,错误=%s",
|
||||
orderId,
|
||||
separateRecord.getSeparate_no(),
|
||||
separateRecord.getLog_no(),
|
||||
e.getMessage());
|
||||
log.error(errorMsg, e);
|
||||
errorMessages.append(errorMsg).append("; ");
|
||||
}
|
||||
}
|
||||
|
||||
// 19. 返回最终处理结果
|
||||
log.info("[分账操作] 处理完成:总订单数={},成功处理数={}", orderId, totalCount, successCount);
|
||||
if (successCount == 0) {
|
||||
String result = "分账全部失败: " + errorMessages;
|
||||
log.warn("[分账操作] 结果:订单[{}] {}", orderId, result);
|
||||
return Pair.of(false, result);
|
||||
} else if (successCount < totalCount) {
|
||||
String result = "部分分账成功,处理中: " + errorMessages;
|
||||
log.info("[分账操作] 结果:订单[{}] {}", orderId, result);
|
||||
return Pair.of(true, result);
|
||||
} else {
|
||||
String result = "全部订单分账已提交处理";
|
||||
log.info("[分账操作] 结果:订单[{}] {}", orderId, result);
|
||||
return Pair.of(true, result);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("[分账操作] 系统异常,订单=%s,错误=%s", orderId, e.getMessage());
|
||||
log.error(errorMsg, e);
|
||||
return Pair.of(false, "系统异常,请稍后重试");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户号、交易号和收货流水号执行订单分账操作
|
||||
* <p>
|
||||
@ -2838,6 +2573,9 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
||||
separateRecord.setTotal_amt(separateRequest.getTotalAmt());
|
||||
separateRecord.setActual_separate_amt(Convert.toStr(refCanSeparateAmt));
|
||||
separateRecord.setTotal_fee_amt(Convert.toStr(lklSeparateDTO.getLklAmount()));
|
||||
if (separateRequest != null) {
|
||||
separateRecord.setLkl_req(JSONUtil.toJsonStr(separateRequest));
|
||||
}
|
||||
|
||||
if (lklOrderSeparateService.addOrUpdateByReceiverNo(separateRecord)) {
|
||||
log.info("[分账操作] 分账记录保存成功, orderId={}, separateNo={}, status={}, logNo={}",
|
||||
@ -3798,6 +3536,9 @@ public class LakalaApiServiceImpl implements LakalaApiService {
|
||||
lklOrderDraw.setBatch_auto_settle(payType);
|
||||
lklOrderDraw.setNotify_url(request.getNotifyUrl());
|
||||
lklOrderDraw.setNotify_resp(responseStr);
|
||||
if (request != null) {
|
||||
lklOrderDraw.setLkl_req(JSONUtil.toJsonStr(request));
|
||||
}
|
||||
lklOrderDraw.setRemark(remark);
|
||||
if (StrUtil.isNotBlank(summary)) {
|
||||
lklOrderDraw.setSummary(summary);
|
||||
|
||||
@ -324,7 +324,7 @@ public class UserOrderController extends BaseControllerImpl {
|
||||
|
||||
@ApiOperation(value = "可预约订单的时间槽", notes = "可预约订单的时间槽")
|
||||
@RequestMapping(value = "/booking_time_args", method = RequestMethod.GET)
|
||||
public CommonResult listInvoice(@RequestParam(name = "store_id", defaultValue = "0") Integer store_id) {
|
||||
public CommonResult listInvoice(@RequestParam(name = "store_id", defaultValue = "0") String store_id) {
|
||||
List<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_id);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
@ -139,8 +139,8 @@ public interface ShopOrderInfoService extends IBaseService<ShopOrderInfo> {
|
||||
/**
|
||||
* 根据店铺的营业时间范围生成可预约下单的参数
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param storeId 店铺ID,多个店铺,使用英文半角逗号隔开,如:57,58,59
|
||||
* @return
|
||||
*/
|
||||
List<BookingArgDTO> genBookingOrderArgList(Integer storeId);
|
||||
List<BookingArgDTO> genBookingOrderArgList(String storeId);
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package com.suisung.mall.shop.order.service.impl;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
@ -1017,36 +1018,20 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
/**
|
||||
* 根据店铺的营业时间范围生成可预约下单的参数
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param storeIds 店铺ID,多个店铺,使用英文半角逗号隔开,如:57,58,59
|
||||
* @return 预约参数列表
|
||||
*/
|
||||
@Override
|
||||
public List<BookingArgDTO> genBookingOrderArgList(Integer storeId) {
|
||||
public List<BookingArgDTO> genBookingOrderArgList(String storeIds) {
|
||||
// 初始化默认营业时间
|
||||
String openingHours = "09:00";
|
||||
String closeHours = "18:00";
|
||||
Map<String, String> timesMap = new HashMap<>();
|
||||
|
||||
// 如果storeId不为空,则尝试获取店铺信息
|
||||
if (storeId != null) {
|
||||
try {
|
||||
ShopStoreInfo shopStoreInfo = shopStoreInfoService.getShopStoreInfoByStoreId(storeId);
|
||||
if (shopStoreInfo != null) {
|
||||
// 检查并使用店铺设置的营业时间
|
||||
if (StrUtil.isNotBlank(shopStoreInfo.getStore_opening_hours())) {
|
||||
openingHours = shopStoreInfo.getStore_opening_hours();
|
||||
}
|
||||
if (StrUtil.isNotBlank(shopStoreInfo.getStore_close_hours())) {
|
||||
closeHours = shopStoreInfo.getStore_close_hours();
|
||||
}
|
||||
logger.debug("[生成预约参数] 使用店铺营业时间,storeId: {}, opening: {}, close: {}", storeId, openingHours, closeHours);
|
||||
} else {
|
||||
logger.warn("[生成预约参数] 未找到店铺信息,使用默认营业时间,storeId: {}", storeId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("[生成预约参数] 获取店铺信息异常,使用默认营业时间,storeId: {}", storeId, e);
|
||||
if (StrUtil.isNotBlank(storeIds)) {
|
||||
List<Map<String, String>> timesMapList = selStoreBizTimeMapList(storeIds);
|
||||
if (!CollUtil.isEmpty(timesMapList)) {
|
||||
timesMap = DateTimeUtils.findTimeInterSection(timesMapList);
|
||||
}
|
||||
} else {
|
||||
logger.warn("[生成预约参数] 店铺ID为空,使用默认营业时间");
|
||||
}
|
||||
|
||||
List<BookingArgDTO> result = new ArrayList<>();
|
||||
@ -1082,18 +1067,98 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
bookingArgDTO.setDate(dateStr);
|
||||
|
||||
// 生成时间项
|
||||
List<BookingArgDTO.BookingArgItem> items = generateTimeSlots(dateStr, openingHours, closeHours, i == 0);
|
||||
bookingArgDTO.setItems(items != null ? items : new ArrayList<>());
|
||||
List<BookingArgDTO.BookingArgItem> items = new ArrayList<>();
|
||||
|
||||
// 如果是今天,始终添加"立即送出"选项
|
||||
if (i == 0) {
|
||||
BookingArgDTO.BookingArgItem immediateItem = new BookingArgDTO.BookingArgItem();
|
||||
immediateItem.setTime_title("立即送出");
|
||||
immediateItem.setBooking_at(0L);
|
||||
immediateItem.setBooking_state(1);
|
||||
immediateItem.setBooking_begin_time("");
|
||||
immediateItem.setBooking_end_time("");
|
||||
items.add(immediateItem);
|
||||
}
|
||||
|
||||
// 只有当timesMap不为空时才生成其他时间槽
|
||||
if (!ObjectUtil.isEmpty(timesMap) && StrUtil.isNotBlank(timesMap.get("startTimeStr")) && StrUtil.isNotBlank(timesMap.get("endTimeStr"))) {
|
||||
List<BookingArgDTO.BookingArgItem> timeSlots = generateTimeSlots(dateStr, timesMap.get("startTimeStr"), timesMap.get("endTimeStr"), i == 0);
|
||||
if (i == 0) {
|
||||
// 对于今天,移除除"立即送出"外的所有时间槽
|
||||
items.addAll(timeSlots.stream().filter(item -> item.getBooking_state() != 1).collect(Collectors.toList()));
|
||||
} else {
|
||||
// 对于其他日期,添加所有时间槽
|
||||
items.addAll(timeSlots);
|
||||
}
|
||||
} else if (i == 0) {
|
||||
// 如果timesMap为空,今天只保留"立即送出"选项
|
||||
logger.debug("[生成预约参数] timesMap为空,今天只生成立即送出选项");
|
||||
} else {
|
||||
// 如果timesMap为空,其他日期不生成任何时间槽
|
||||
logger.debug("[生成预约参数] timesMap为空,不生成{}的预约时间槽", dateStr);
|
||||
continue; // 跳过当前日期
|
||||
}
|
||||
|
||||
bookingArgDTO.setItems(items);
|
||||
result.add(bookingArgDTO);
|
||||
}
|
||||
|
||||
logger.debug("[生成预约参数] 成功生成预约参数,storeId: {}, opening: {}, close: {}, 参数数量: {}",
|
||||
storeId, openingHours, closeHours, result.size());
|
||||
logger.debug("[生成预约参数] 成功生成预约参数,storeId: {}, timesMap: {}, 参数数量: {}",
|
||||
storeIds, timesMap, result.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据 storeIds(一个或多个 storeid,如 34,23,43,23,),先对id去重,再获取多个店铺的营业时间 List<map{startTimeStr, endTimeStr}> 列表list
|
||||
*
|
||||
* @param storeIds 以逗号分隔的店铺ID字符串
|
||||
* @return 包含店铺营业时间信息的列表,每个元素为包含opening_hours和close_hours的Map
|
||||
*/
|
||||
private List<Map<String, String>> selStoreBizTimeMapList(String storeIds) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(storeIds)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 解析并去重店铺ID
|
||||
List<String> uniqueStoreIds = Arrays.stream(storeIds.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 2. 如果没有有效的店铺ID,返回空列表
|
||||
if (uniqueStoreIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 3. 批量获取店铺信息
|
||||
QueryWrapper<ShopStoreInfo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("store_opening_hours", "store_close_hours"); // 只查询必要字段
|
||||
queryWrapper.in("store_id", storeIds);
|
||||
|
||||
List<ShopStoreInfo> shopStoreInfos = shopStoreInfoService.find(queryWrapper);
|
||||
|
||||
// 4. 转换为营业时间映射列表
|
||||
return shopStoreInfos.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(storeInfo -> {
|
||||
Map<String, String> timeSlot = new HashMap<>();
|
||||
timeSlot.put("startTimeStr", storeInfo.getStore_opening_hours());
|
||||
timeSlot.put("endTimeStr", storeInfo.getStore_close_hours());
|
||||
return timeSlot;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("[获取店铺营业时间] 处理店铺营业时间异常,storeIds: {}", storeIds, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成时间槽列表
|
||||
*
|
||||
|
||||
@ -39,7 +39,7 @@ public interface ShopUserVoucherService extends IBaseService<ShopUserVoucher> {
|
||||
|
||||
Map getLists(QueryWrapper<ShopUserVoucher> voucherQueryWrapper, int page, int rows);
|
||||
|
||||
boolean useNeedNotPin(Integer user_id, String order_id);
|
||||
boolean tryUseFreeGroupVoucher(Integer user_id, String order_id);
|
||||
|
||||
// 线下优惠券核销
|
||||
boolean exitWriteoffUserVoucher(ShopUserVoucher shopUserVoucher);
|
||||
|
||||
@ -65,7 +65,7 @@ public class ShopUserVoucherServiceImpl extends BaseServiceImpl<ShopUserVoucherM
|
||||
@Lazy
|
||||
@Autowired
|
||||
private ShopStoreActivityBaseService storeActivityBaseService;
|
||||
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private ShopStoreBaseService shopStoreBaseService;
|
||||
@ -331,17 +331,20 @@ public class ShopUserVoucherServiceImpl extends BaseServiceImpl<ShopUserVoucherM
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户使用免拼券
|
||||
* 用户是否使用了免拼券
|
||||
*
|
||||
* @param user_id
|
||||
* @param order_id
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean useNeedNotPin(Integer user_id, String order_id) {
|
||||
public boolean tryUseFreeGroupVoucher(Integer user_id, String order_id) {
|
||||
Date date = new Date();
|
||||
QueryWrapper<ShopUserVoucher> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("voucher_type", 1).eq("user_id", user_id).eq("voucher_state_id", StateCode.VOUCHER_STATE_UNUSED).ge("voucher_end_date", date);
|
||||
queryWrapper.eq("voucher_type", 1)
|
||||
.eq("user_id", user_id)
|
||||
.eq("voucher_state_id", StateCode.VOUCHER_STATE_UNUSED)
|
||||
.ge("voucher_end_date", date);
|
||||
ShopUserVoucher user_voucher_row = findOne(queryWrapper);
|
||||
|
||||
if (user_voucher_row != null) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user