Compare commits

...

38 Commits

Author SHA1 Message Date
fd2fd5c899 Merge branch 'main' into dev 2025-11-04 15:25:54 +08:00
85294f332a 规格计算单价 2025-11-04 14:39:49 +08:00
81fe3a88b8 更改 diy 装修组件 2025-11-04 14:34:20 +08:00
eb5ddd03df 新增事务方案修复掉线问题导致的重复消费 2025-11-04 11:43:01 +08:00
ddd20b825e 同步价改为订单价格 2025-11-04 11:18:35 +08:00
f7a6cecd31 活动同步问题修复 2025-11-03 18:02:43 +08:00
88e9a52071 活动同步问题修复 2025-11-03 18:02:43 +08:00
d3790fae0d 主题活动同步改同步 2025-11-03 18:02:22 +08:00
2746f11442 思迅同步销售流水和支付流水,调价时间查询新增 2025-11-03 16:12:21 +08:00
7932c2cdf7 思迅同步销售流水和支付流水 2025-11-03 16:11:59 +08:00
3b7a923109 新增单价营业员相关字段,实现思迅流水 2025-11-03 15:46:22 +08:00
98f5a160af docker 镜像打包到 registry ,改变原来的做法,未双机部署,做好准备 2025-11-03 14:16:56 +08:00
ee3437005b docker 镜像打包到 registry ,改变原来的做法,未双机部署,做好准备 2025-11-03 08:44:31 +08:00
3d31e4cd74 砍价下单验证是否达标,不达标不允许下单购买 2025-11-02 00:09:09 +08:00
2af8bc966a 砍价下单验证是否达标,不达标不允许下单购买 2025-11-01 17:48:20 +08:00
abfefb6f57 砍价详情增加 判断可以立即出手字段 2025-11-01 16:49:39 +08:00
6acf94acb5 推送消息 2025-11-01 14:39:19 +08:00
60c4df7625 内部运费的优化,增加店铺可以设置内部运费。 2025-11-01 11:00:39 +08:00
fd88f45625 思迅同步地址 2025-11-01 10:51:31 +08:00
eb268d35b3 预订单逻辑优化 2025-10-31 21:58:43 +08:00
c91fc181c9 活动逻辑处理 2025-10-31 16:05:08 +08:00
002baf3ffd Merge remote-tracking branch 'origin/main' 2025-10-31 16:04:01 +08:00
a20ca370b9 发布后更改发版时间 2025-10-31 10:23:38 +08:00
3aef824fbe Merge remote-tracking branch 'origin/main' 2025-10-31 09:41:50 +08:00
a0b92c219e 优化商品切割 2025-10-31 09:06:12 +08:00
a7f2a7ac14 调整了打票机 订单商品间距 2025-10-31 08:55:15 +08:00
621f557e34 调整了打票机 订单商品间距 2025-10-30 19:58:49 +08:00
c64c404495 店铺信息表增加 两个时间和店铺内部运费字段 2025-10-30 12:41:44 +08:00
6315b69246 重新上架,上架时间改为当前时间 2025-10-30 11:49:01 +08:00
c3997d074f 优化同步的单位判断 2025-10-30 10:48:54 +08:00
5ff094de7a 预订单时间槽列表逻辑调整,分账、提现 报文字段 保存 2025-10-30 10:47:14 +08:00
6a335ef1aa 预订单时间槽列表逻辑调整,分账、提现 报文字段 保存 2025-10-30 01:22:17 +08:00
0248a38268 预订单时间槽列表逻辑调整,分账、提现 报文字段 保存 2025-10-30 01:01:40 +08:00
7810340cde 同步数据不更新商品名称 2025-10-29 16:21:51 +08:00
975f438137 新增分类查询排序 2025-10-29 15:48:38 +08:00
32ef77830d 解决线程不安全的时间操作,防止时间溢出 2025-10-29 12:12:28 +08:00
8815fb9d94 修改商品货架号同步到base表 2025-10-29 12:12:12 +08:00
a6877b3684 思迅设置用户名称 2025-10-29 10:20:54 +08:00
48 changed files with 1162 additions and 753 deletions

View File

@ -125,4 +125,6 @@ public class CommonConstant {
public static final Integer Order_Booking_State_LJ = 1;
public static final Integer Order_Booking_State_YY = 2;
// 预约下单从当前时间延迟的最小分钟数单位分钟不能低于35分钟
public final static Integer MIN_DELAY_MINUTES_FOR_BOOKING_ORDER = 50;
}

View File

@ -326,5 +326,16 @@ public interface ShopService {
BigDecimal getOrderShippingFee(@RequestParam(name = "order_id") String order_id);
/**
* 获取店铺的内部运费 shopping_fee_inner 远程调用用途
*
* @param store_id
* @return
*/
@ApiOperation(value = "获取店铺的内部运费 shopping_fee_inner", notes = "获取店铺的内部运费 shopping_fee_inner (远程调用用途)")
@RequestMapping(value = "/admin/shop/shop-store-info/shopping-fee-inner", method = RequestMethod.POST)
Integer storeShoppingFeeInner(@RequestParam(name = "store_id") Integer store_id);
}

View File

@ -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数据
*/

View File

@ -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;

View File

@ -3,11 +3,13 @@ package com.suisung.mall.common.modules.store;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
@ -121,5 +123,18 @@ public class ShopStoreInfo implements Serializable {
@ApiModelProperty(value = "线下买单折扣:10代表原价")
private BigDecimal store_discount;
@ApiModelProperty(value = "店铺内部运费单位0-使用平台的内部运费;>0 使用店铺的内部运费")
private Integer shopping_fee_inner;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "新增时间")
private Date created_at;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@ApiModelProperty(value = "最后更新时间")
private Date updated_at;
}

View File

@ -33,6 +33,11 @@ public class BookingArgDTO {
@ApiModelProperty(value = "日期")
private String date;
/**
* 工作时间依据"09:00-21:00"
*/
private String working_hours;
/**
* 时间项列表
*/

View File

@ -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);
}
}

View File

@ -48,10 +48,11 @@ public interface AccountBaseConfigService extends IBaseService<AccountBaseConfig
String getSystemConfig(String configKey);
/**
* 获取平台内部最低配送费单位
* 获取店铺或平台内部最低配送费单位
*
* @return
* @param storeId 店铺Id
* @return 配送费单位分
*/
Integer getInnerMinDeliveryFee();
Integer getInnerMinDeliveryFee(Integer storeId);
}

View File

@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.constant.CommonConstant;
import com.suisung.mall.common.constant.RedisConstant;
import com.suisung.mall.common.feignService.AccountService;
import com.suisung.mall.common.feignService.ShopService;
import com.suisung.mall.common.modules.account.AccountBaseConfig;
import com.suisung.mall.common.modules.account.AccountUserInfo;
import com.suisung.mall.common.utils.CheckUtil;
@ -20,6 +21,7 @@ import com.suisung.mall.pay.service.AccountBaseConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@ -47,6 +49,11 @@ public class AccountBaseConfigServiceImpl extends BaseServiceImpl<AccountBaseCon
public static Long version = 0L;
@Autowired
private AccountService accountService;
@Lazy
@Autowired
private ShopService shopService;
@Autowired
private RedisService redisService;
@ -254,16 +261,41 @@ public class AccountBaseConfigServiceImpl extends BaseServiceImpl<AccountBaseCon
}
/**
* 获取平台内部最低配送费单位
* 获取店铺或平台内部最低配送费单位
*
* @return
* @param storeId 店铺Id
* @return 配送费单位分
*/
@Override
public Integer getInnerMinDeliveryFee() {
String v = getSystemConfig(CommonConstant.Inner_Min_DeliveryFee_Key);
return NumberUtil.isNumber(v) ? Convert.toInt(v) : 0;
public Integer getInnerMinDeliveryFee(Integer storeId) {
// 如果storeId无效直接使用平台默认配置
if (storeId == null || storeId <= 0) {
return getDefaultMinDeliveryFee();
}
// 获取店铺内部运费
Integer storeFee = shopService.storeShoppingFeeInner(storeId);
// 如果店铺未设置有效运费则使用平台默认配置
if (storeFee == null || storeFee <= 0) {
return getDefaultMinDeliveryFee();
}
return storeFee;
}
/**
* 获取平台默认最低配送费
*
* @return 默认配送费单位分
*/
private Integer getDefaultMinDeliveryFee() {
String configValue = getSystemConfig(CommonConstant.Inner_Min_DeliveryFee_Key);
int fee = NumberUtil.isNumber(configValue) ? Convert.toInt(configValue) : 0;
return fee > 0 ? fee : 0;
}
/**
* 交易模式 2直接交易:商家直接收款 1担保交易平台收款平台和商家结算
*/

View File

@ -251,8 +251,8 @@ public class LakalaPayServiceImpl implements LakalaPayService {
}
// 平台最低配送费单位
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
reqData.set("shopping_fee_inner", innerMinDeliverFee); // 平台内部最低配送费单位
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee(Convert.toInt(storeId));
reqData.set("shopping_fee_inner", innerMinDeliverFee); // 重要平台内部最低配送费单位
log.info("[拉卡拉预下单] 支付成功,准备保存订单记录, orderId={}", orderId);
// 新增一个拉卡拉订单记录 shop_order_lkl
@ -280,7 +280,7 @@ public class LakalaPayServiceImpl implements LakalaPayService {
/**
* 拉卡拉合单预下单主要有运费的订单使用合单
* 暂时废弃拉卡拉合单预下单主要有运费的订单使用合单
* 参考https://o.lakala.com/#/home/document/detail?id=208
*
* @param merchantNo 商户号(商城商家)
@ -299,6 +299,7 @@ public class LakalaPayServiceImpl implements LakalaPayService {
* @param remark 备注
* @return 拉卡拉合单预下单响应结果
**/
@Deprecated
@Override
public JSONObject lklTransMergePreOrder(String merchantNo, String termNo, String agentMerchantNo, String agentTermNo, String xcxAppId, String openId, String storeId, String orderId, String subject, String totalAmount, String agentAmount, String notifyURL, String requestIP, String remark) {
log.info("[拉卡拉合单预下单] 开始处理请求, merchantNo={}, termNo={}, agentMerchantNo={}, agentTermNo={}, orderId={}",
@ -558,7 +559,7 @@ public class LakalaPayServiceImpl implements LakalaPayService {
log.warn("[拉卡拉退款] 退款金额不合法: refundAmount={}", refundAmount);
return Pair.of(false, I18nUtil._("退款金额不合法!"));
}
if (StrUtil.hasBlank(lklMerchantNo, lklTermNo)) {
// 4. 获取店铺的拉卡拉商户号和终端号
ShopStoreBase shopStoreBase = shopService.getLklMerchantNoAndTermNo(storeId);

View File

@ -671,15 +671,14 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
String requestIP = IpKit.getRealIp(request);
String notifyUrl = domain + "/lkl_wxPay_notify_url";
String storeIdStr = Convert.toStr(storeId);// 店铺Id
BigDecimal shippingFee = shopService.getOrderShippingFee(out_trade_no);
if (shippingFee == null || shippingFee.intValue() <= 0) {
shippingFee = BigDecimal.ZERO;
}
// 平台最低配送费单位
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee(storeId);
logger.debug("预支付时,查到的订单{},商家配送费:{}元,平台最低配送费要求:{}分", out_trade_no, shippingFee, innerMinDeliverFee);
// 平台最低配送费单位
@ -687,7 +686,7 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
// 内部运费统一由分账接收方收取了 2025-10-11
// 拉卡拉预支付返回参数
// TODO 判断订单有没有运费有运费请求合单交易没有运费请求聚合主扫交易
// 聚合主扫交易
cn.hutool.json.JSONObject lakalaRespJSON = lakalaPayService.lklTransPreOrder(shopStoreBase.getLkl_merchant_no(), shopStoreBase.getLkl_term_no(),
appId, openId, storeIdStr, out_trade_no, subject, total_amt,
notifyUrl,

View File

@ -10,6 +10,7 @@ import com.suisung.mall.common.exception.ApiUserException;
import com.suisung.mall.common.modules.activity.ShopActivityGroupbookingHistory;
import com.suisung.mall.common.modules.activity.ShopActivityGroupbuyStoreHistory;
import com.suisung.mall.common.service.impl.BaseControllerImpl;
import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.shop.activity.service.ShopActivityCutpriceHistoryService;
import com.suisung.mall.shop.activity.service.ShopActivityCutpriceService;
@ -64,6 +65,7 @@ public class UserActivityController extends BaseControllerImpl {
@Autowired
private ShopStoreActivityBaseService shopStoreActivityBaseService;
@ApiOperation(value = "列出我的团购", notes = "列出我的团购")
@RequestMapping(value = "/listsUserGroupbooking", method = RequestMethod.GET)
public CommonResult listsUserGroupbooking(@RequestParam(name = "page", defaultValue = "1") Integer page,
@RequestParam(name = "rows", defaultValue = "10") Integer rows,
@ -96,6 +98,7 @@ public class UserActivityController extends BaseControllerImpl {
return CommonResult.success(shopActivityGroupbookingService.listsUserGroupbooking(queryWrapper, page, rows));
}
@ApiOperation(value = "我的团购详情", notes = "我的团购详情")
@RequestMapping(value = "/getUserGroupbooking", method = RequestMethod.GET)
public CommonResult getUserGroupbooking(@RequestParam(name = "gb_id") Integer gb_id) {
return CommonResult.success(shopActivityGroupbookingService.getUserGroupbooking(gb_id));
@ -111,10 +114,28 @@ public class UserActivityController extends BaseControllerImpl {
@ApiOperation(value = "砍价活动详情", notes = "砍价活动详情")
@RequestMapping(value = "/getCutPriceActivity", method = RequestMethod.GET)
public CommonResult getCutPriceActivity() {
public CommonResult getCutPriceActivityDetail() {
return CommonResult.success(shopActivityCutpriceService.getCutPriceActivity());
}
@ApiOperation(value = "立即砍价", notes = "自己砍价、要求朋友过来也能砍价")
@RequestMapping(value = "/doCutPrice", method = RequestMethod.GET)
public CommonResult doCutPrice(@RequestParam(name = "ac_id", defaultValue = "0") Integer ac_id) {
UserDto user = getCurrentUser();
if (user == null || CheckUtil.isEmpty(user.getId())) {
throw new ApiException(ResultCode.NEED_LOGIN);
}
Integer user_id = user.getId();
return shopActivityCutpriceService.doCutPrice(ac_id, user_id);
}
@ApiOperation(value = "砍价历史记录", notes = "砍价历史记录(砍价排行榜)")
@RequestMapping(value = "/listsCutPriceHistory", method = RequestMethod.GET)
public CommonResult listsCutPriceHistory() {
return CommonResult.success(shopStoreActivityBaseService.listsCutPriceHistory());
}
@ApiOperation(value = "列出我的团购记录", notes = "列出我的团购记录")
@RequestMapping(value = "/listsUserGroupbuyStore", method = RequestMethod.GET)
public CommonResult listsUserGroupbuyStore(@RequestParam(name = "page", defaultValue = "1") Integer page,
@ -216,22 +237,6 @@ public class UserActivityController extends BaseControllerImpl {
return CommonResult.success(shopStoreActivityBaseService.listsLotteryHistory());
}
@ApiOperation(value = "参加活动,并报名", notes = "参加活动,并报名")
@RequestMapping(value = "/doCutPrice", method = RequestMethod.GET)
public CommonResult doCutPrice(@RequestParam(name = "ac_id", defaultValue = "0") Integer ac_id) {
UserDto user = getCurrentUser();
if (user == null) {
throw new ApiException(ResultCode.NEED_LOGIN);
}
Integer user_id = user.getId();
return shopActivityCutpriceService.doCutPrice(ac_id, user_id);
}
@ApiOperation(value = "砍价历史记录", notes = "砍价历史记录")
@RequestMapping(value = "/listsCutPriceHistory", method = RequestMethod.GET)
public CommonResult listsCutPriceHistory() {
return CommonResult.success(shopStoreActivityBaseService.listsCutPriceHistory());
}
@ApiOperation(value = "根据条件列出形成的团", notes = "根据条件列出形成的团")
@RequestMapping(value = "/listsGroupbooking", method = RequestMethod.GET)
@ -246,6 +251,5 @@ public class UserActivityController extends BaseControllerImpl {
public CommonResult getGiftbag(@RequestParam(name = "activity_id") Integer activity_id) {
return CommonResult.success(shopStoreActivityBaseService.getGiftbag(activity_id));
}
//
}

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.suisung.mall.common.api.CommonResult;
import com.suisung.mall.common.modules.activity.ShopActivityCutprice;
import com.suisung.mall.core.web.service.IBaseService;
import org.springframework.data.util.Pair;
import java.util.Map;
@ -28,4 +29,13 @@ public interface ShopActivityCutpriceService extends IBaseService<ShopActivityCu
CommonResult doCutPrice(Integer ac_id, Integer user_id);
/**
* 砍价活动是否可以下单
*
* @param ac_id 活动 自增Id
* @param order_user_id 下单用户
* @return
*/
Pair<Boolean, String> canDoOrderCutPriceActivity(Integer ac_id, Integer order_user_id);
}

View File

@ -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.CommonResult;
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;
@ -33,8 +34,10 @@ import com.suisung.mall.shop.activity.service.ShopActivityGroupbookingHistorySer
import com.suisung.mall.shop.activity.service.ShopActivityGroupbookingService;
import com.suisung.mall.shop.base.service.AccountBaseConfigService;
import com.suisung.mall.shop.store.service.ShopStoreActivityBaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -54,6 +57,7 @@ import static com.suisung.mall.common.utils.ContextUtil.getCurrentUser;
* @author Xinze
* @since 2021-07-12
*/
@Slf4j
@Service
public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivityCutpriceMapper, ShopActivityCutprice> implements ShopActivityCutpriceService {
@ -319,9 +323,30 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
// activity_row.put("activity_endtime", Convert.toDate(time));
// }
// 界面做一个简单的底价验证
boolean canBuyNow = cutprice_row != null
&& CheckUtil.isNotEmpty(cutprice_row.getAc_sale_price())
&& CheckUtil.isNotEmpty(cutprice_row.getAc_mix_limit_price())
&& NumberUtil.isLessOrEqual(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
BigDecimal subtractPrice = NumberUtil.sub(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
// 能否立即出手 1-可以2-不可以
activity_row.put("can_buy_now", canBuyNow ? CommonConstant.Enable : CommonConstant.Disable2);
activity_row.put("cannot_buy_now_reason",
canBuyNow ? "恭喜您,商品可以立即出手了。" :
String.format("还剩%.2f元到达商品底价,请继续加油!", subtractPrice));
return activity_row;
}
/**
* 砍价
*
* @param ac_id 活动 Id
* @param user_id 砍价用户
* @return
*/
@Override
@Transactional
public CommonResult doCutPrice(Integer ac_id, Integer user_id) {
@ -344,7 +369,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
Integer activity_id = cutprice_row.getActivity_id();
ShopStoreActivityBase storeActivityBase = shopStoreActivityBaseService.get(activity_id);
Map activity_row = Convert.toMap(String.class, Object.class, storeActivityBase);
if (!shopStoreActivityBaseService.verifyActivity(activity_row)) {
if (!shopStoreActivityBaseService.isActivityTimeValid(activity_row)) {
throw new ApiException(I18nUtil._("该活动不存在!"));
}
@ -390,4 +415,82 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
return CommonResult.success(ach_data);
}
/**
* 砍价活动是否符合立即下单了
*
* @param activity_id 活动 Id
* @param order_user_id 下单用户
* <p>
* 判断逻辑
* 1活动是否过期
* 2活动是否是下单用户的
* 3活动是否是砍到最低价
* @return Pair<Boolean, String> 第一个值表示是否可以下单第二个值是相关信息或错误信息
*/
@Override
public Pair<Boolean, String> canDoOrderCutPriceActivity(Integer activity_id, Integer order_user_id) {
try {
// 参数校验
if (activity_id == null || activity_id <= 0) {
return Pair.of(false, I18nUtil._("活动ID无效。"));
}
if (order_user_id == null || order_user_id <= 0) {
return Pair.of(false, I18nUtil._("用户ID无效。"));
}
// 1. 检查活动是否存在
QueryWrapper<ShopStoreActivityBase> params = new QueryWrapper<>();
params.eq("activity_id", activity_id);
ShopStoreActivityBase storeActivityBase = shopStoreActivityBaseService.getOne(params);
if (storeActivityBase == null) {
return Pair.of(false, I18nUtil._("抱歉,系统未找到活动记录。"));
}
// 2. 检查活动是否在有效期内
if (!shopStoreActivityBaseService.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime())) {
return Pair.of(false, I18nUtil._("该活动已过期,请检查。"));
}
// 3. 检查用户是否有发起的砍价记录
QueryWrapper<ShopActivityCutprice> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("activity_id", activity_id);
queryWrapper.eq("user_id", order_user_id);
queryWrapper.orderByDesc("ac_id");
ShopActivityCutprice shopActivityCutprice = getOne(queryWrapper);
if (shopActivityCutprice == null) {
return Pair.of(false, I18nUtil._("系统未找到您发起的砍价记录。"));
}
// 4. 检查是否已砍到最低价
// 砍价底价
BigDecimal mixSalePrice = shopActivityCutprice.getAc_mix_limit_price();
// 当前最终被砍价后的最新价格
BigDecimal currPrice = shopActivityCutprice.getAc_sale_price();
// 还剩多少到底价 = 当前价格 - 活动底价
BigDecimal subPrice = NumberUtil.sub(currPrice, mixSalePrice);
// 当前价格已经等于或低于底价可以立即出手下单
if (NumberUtil.isGreater(subPrice, BigDecimal.ZERO)) {
return Pair.of(false, String.format(I18nUtil._("还剩%.2f元可达活动底价,请继续加油。"), subPrice));
}
// 5. 检查是否存在砍价历史记录
QueryWrapper<ShopActivityCutpriceHistory> queryWrapperHistory = new QueryWrapper<>();
queryWrapperHistory.eq("ac_id", shopActivityCutprice.getAc_id());
queryWrapperHistory.eq("activity_id", shopActivityCutprice.getActivity_id());
List<ShopActivityCutpriceHistory> shopActivityCutpriceHistoryList = shopActivityCutpriceHistoryService.list(queryWrapperHistory);
if (CollUtil.isEmpty(shopActivityCutpriceHistoryList)) {
return Pair.of(false, I18nUtil._("未找到砍价记录,请检查"));
}
// 所有条件都满足可以下单
return Pair.of(true, "");
} catch (Exception e) {
// 异常处理防止影响主流程
log.error("检查砍价活动是否可下单时发生异常activity_id={}, order_user_id={}", activity_id, order_user_id, e);
return Pair.of(false, I18nUtil._("系统繁忙,请稍后再试。"));
}
}
}

View File

@ -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;
@ -474,7 +475,7 @@ public class ShopActivityGroupbookingServiceImpl extends BaseServiceImpl<ShopAct
ShopStoreActivityBase activityBase = shopStoreActivityBaseService.get(gbh_row.getActivity_id());
Map activity_row = Convert.toMap(String.class, Object.class, activityBase);
if (shopStoreActivityBaseService.verifyActivity(activity_row)) if (gbh_row != null && gb_row != null) {
if (shopStoreActivityBaseService.isActivityTimeValid(activity_row)) if (gbh_row != null && gb_row != null) {
// 判断拼团是否满员在加入该成员后是否满员
if (ifFull(gb_row, true)) {
// 修改拼团记录
@ -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);

View File

@ -152,7 +152,7 @@ public class ShopActivityGroupbuyStoreServiceImpl extends BaseServiceImpl<ShopAc
ShopStoreActivityBase storeActivityBase = shopStoreActivityBaseService.findOne(baseQueryWrapper);
Map activity_base_row = Convert.toMap(String.class, Object.class, storeActivityBase);
if (gbsh_row != null && activity_base_row != null && shopStoreActivityBaseService.verifyActivity(activity_base_row)) {
if (gbsh_row != null && activity_base_row != null && shopStoreActivityBaseService.isActivityTimeValid(activity_base_row)) {
// 修改团员支付状态
ShopActivityGroupbuyStoreHistory storeHistory = new ShopActivityGroupbuyStoreHistory();

View File

@ -168,7 +168,7 @@ public class ShopActivityPfGroupbuyStoreHistoryServiceImpl extends BaseServiceIm
Map activity_base_row = Convert.toMap(String.class, Object.class, storeActivityBase);
if (pfgbsh_row != null && activity_base_row != null
&& shopStoreActivityBaseService.verifyActivity(activity_base_row)) {
&& shopStoreActivityBaseService.isActivityTimeValid(activity_base_row)) {
// 修改团员支付状态
ShopActivityPfGroupbuyStoreHistory storeHistory = new ShopActivityPfGroupbuyStoreHistory();

View File

@ -60,9 +60,10 @@ public interface AccountBaseConfigService extends IBaseService<AccountBaseConfig
String getSystemConfig(String configKey);
/**
* 获取平台内部最低配送费单位
* 获取店铺或平台内部最低配送费单位
*
* @return
* @param storeId 店铺Id
* @return 配送费单位分
*/
Integer getInnerMinDeliveryFee();
Integer getInnerMinDeliveryFee(Integer storeId);
}

View File

@ -22,9 +22,11 @@ import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.base.mapper.AccountBaseConfigMapper;
import com.suisung.mall.shop.base.service.AccountBaseConfigService;
import com.suisung.mall.shop.config.CookieUtil;
import com.suisung.mall.shop.store.service.ShopStoreInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
@ -51,6 +53,11 @@ public class AccountBaseConfigServiceImpl extends BaseServiceImpl<AccountBaseCon
public static Long version = 0L;
@Autowired
private AccountService accountService;
@Lazy
@Autowired
private ShopStoreInfoService shopStoreInfoService;
@Autowired
private RedisService redisService;
@ -369,14 +376,38 @@ public class AccountBaseConfigServiceImpl extends BaseServiceImpl<AccountBaseCon
}
/**
* 获取平台内部最低配送费单位
* 获取店铺或平台内部最低配送费单位
*
* @return
* @param storeId 店铺Id
* @return 配送费单位分
*/
@Override
public Integer getInnerMinDeliveryFee() {
String v = getSystemConfig(CommonConstant.Inner_Min_DeliveryFee_Key);
return NumberUtil.isNumber(v) ? Convert.toInt(v) : 0;
public Integer getInnerMinDeliveryFee(Integer storeId) {
// 如果storeId无效直接使用平台默认配置
if (storeId == null || storeId <= 0) {
return getDefaultMinDeliveryFee();
}
// 获取店铺内部运费
Integer storeFee = shopStoreInfoService.getStoreShippingFeeInner(storeId);
// 如果店铺未设置有效运费则使用平台默认配置
if (storeFee == null || storeFee <= 0) {
return getDefaultMinDeliveryFee();
}
return storeFee;
}
/**
* 获取平台默认最低配送费
*
* @return 默认配送费单位分
*/
private Integer getDefaultMinDeliveryFee() {
String configValue = getSystemConfig(CommonConstant.Inner_Min_DeliveryFee_Key);
int fee = NumberUtil.isNumber(configValue) ? Convert.toInt(configValue) : 0;
return fee > 0 ? fee : 0;
}

View File

@ -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>

View File

@ -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响应=%srespJson=%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);

View File

@ -322,10 +322,10 @@ public class UserOrderController extends BaseControllerImpl {
return CommonResult.success(data);
}
@ApiOperation(value = "可预约订单的时间槽", notes = "可预约订单的时间槽")
@ApiOperation(value = "可预约订单的时间槽", notes = "结算中心页面,可预约订单的时间槽")
@RequestMapping(value = "/booking_time_args", method = RequestMethod.GET)
public CommonResult listInvoice(@RequestParam(name = "store_id", defaultValue = "0") Integer store_id) {
List<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_id);
public CommonResult listInvoice(@RequestParam(name = "store_ids", defaultValue = "") String store_ids) {
List<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_ids);
return CommonResult.success(list);
}

View File

@ -187,12 +187,13 @@ public class OrderPayedListener {
continue;
}
// 同城配送或普通快递都发送 unipush 推送您有一个新的订单请查收
// 同城配送或普通快递都发送 unipush 推送您有一个新的[同城][预约]订单请及时拣货配送[请及时查看预约订单]
String bookingTip = isBookingOrder ? "预约" : "";
String orderType = orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" + bookingTip : bookingTip;
String title = String.format("您有一笔新的%s订单请注意查收。", orderType);
String content = String.format("这笔新的%s订单%s用户于%s下的单请注意查收。", orderType, orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
String actionTip = isBookingOrder ? "请及时检查预约订单信息" : "请及时拣货配送";
String orderType = (orderInfoOld.getDelivery_type_id() == StateCode.DELIVERY_TYPE_SAME_CITY ? "同城" : "") + bookingTip;
String title = String.format("您有一笔新的%s订单%s。", orderType, actionTip);
String content = String.format("这笔新的%s订单%s用户于%s下的单%s。", orderType, orderId,
DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"), actionTip);
JSONObject payload = new JSONObject();
payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST);
payload.put("orderId", orderId);

View File

@ -96,6 +96,7 @@ public class RedisKeyExpiredListener implements MessageListener {
// orderId
String orderId = expiredKey.replace(RedisConstant.Order_Booking_Task_Key, "");
log.info("[预约订单顺丰同城下单Redis过期监听] 处理预约订单顺丰同城下单超时消息. 订单号: {}", orderId);
if (StrUtil.isBlank(orderId)) {
log.error("[预约订单顺丰同城下单Redis过期监听] 键: {} 不符合预期格式,无法解析订单号", expiredKey);
return;

View File

@ -607,4 +607,13 @@ public interface ShopOrderBaseService extends IBaseService<ShopOrderBase> {
* @return
*/
Long sameCityOrderExpireSeconds(Long defaultValue);
/**
* 更新订单下单时间
*
* @param orderId
* @param orderTime
* @return
*/
Boolean updateOrderTime(String orderId, Date orderTime);
}

View File

@ -8,7 +8,6 @@
package com.suisung.mall.shop.order.service;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.suisung.mall.common.modules.order.ShopOrderBooking;
import com.suisung.mall.core.web.service.IBaseService;
@ -40,15 +39,7 @@ public interface ShopOrderBookingService extends IBaseService<ShopOrderBooking>
* @return 分页结果
*/
Page<ShopOrderBooking> findValidBookingList(Integer pageNum, Integer pageSize);
/**
* 更新预约订单任务
*
* @param updateWrapper 更新条件包装器
* @return 是否更新成功
*/
Boolean update(UpdateWrapper<ShopOrderBooking> updateWrapper);
/**
* 更改预约订单任务下单完成状态同时删除 redis 的定时任务
*

View File

@ -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);
}

View File

@ -171,7 +171,7 @@ public interface ShopOrderReturnService extends IBaseService<ShopOrderReturn> {
* @param productId 商品ID
* @return boolean true表示禁止退货false表示允许退货
*/
boolean ifOrderItemDenyReturn(String orderId, Long productId);
boolean isOrderItemDenyReturn(String orderId, Long productId);
/**
* 判断订单是否禁止退货

View File

@ -1395,8 +1395,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
Integer quantity = getParameter("quantity", 1);
Integer channelType = getParameter("channel_type", 0);
Map activityRows = new HashMap();
Map activityRows = new HashMap();
if (!activityId.equals(0)) {
activityRows = shopStoreActivityBaseService.getActivityFormatOne(activityId, quantity);
}
@ -1580,8 +1580,17 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
// 判断是否砍价购买
Integer acId = getParameter("ac_id", Integer.class);
if (acId != null) {
cartData.put("ac_id", acId); // 拼团ID, 为0在创建拼团
// 判断是否为砍价活动
Boolean isCutPriceActivity = shopStoreActivityBaseService.isCutPriceActivity(activityId);
if (CheckUtil.isNotEmpty(acId) && isCutPriceActivity) {
cartData.put("ac_id", acId);
// 校验砍价活动用户是否可以立即购买了
Pair<Boolean, String> canDoOrderCutPriceActivity = shopActivityCutpriceService.canDoOrderCutPriceActivity(acId, userId);
if (!canDoOrderCutPriceActivity.getFirst()) {
logger.error(canDoOrderCutPriceActivity.getSecond());
throw new ApiException(I18nUtil._("砍价活动未达标,请继续努力,再提交订单。"));
}
}
// 重要邮费检测和计算
@ -6675,15 +6684,22 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
bookingAt = bookingBeginTime.getTime() / 1000; // 预订单到达时间戳
}
// 计算订单下单时间预约开始时间前35分钟
Date scheduledOrderTime = DateUtil.offsetSecond(bookingBeginTime, -Convert.toInt(sameCityOrderExpireSeconds(2100L))); // 35分钟超时60秒*35分钟 = 2100秒
info_row.setBooking_state(bookingState);
info_row.setBooking_at(bookingAt);
info_row.setBooking_begin_time(bookingBeginTime);
info_row.setBooking_end_time(bookingEndTime);
info_row.setOrder_time(scheduledOrderTime.getTime());
// 重要预约订单任务创建处理
if (!shopOrderBookingService.setupRedisBookingTask(info_row.getOrder_id(), info_row.getBooking_at())) {
throw new ApiException(I18nUtil._("保存预约订单任务失败!"));
}
// 更新订单的下单时间
updateOrderTime(order_id, scheduledOrderTime);
}
@ -7756,7 +7772,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
data_row.setPacking_fee(packingFee);
// 平台最低配送费单位
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee(store_id);
// 平台最低配送费单位
BigDecimal shoppingFeeInner = Convert.toBigDecimal(innerMinDeliverFee).divide(BigDecimal.valueOf(100));
@ -8759,7 +8775,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
// 支付渠道处理
m.put("payment_channel_name", m.getOrDefault("payment_channel_name", "线上支付").toString());
// 配送时间= 支付时间+35分钟
// 配送到达时间= 支付时间+35分钟
Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(2100L);
// 配送时间
Date paymentTime = Convert.toDate(m.get("payment_time"));
@ -9054,13 +9070,22 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
order.setOrder_pickup_num_str(StringUtils.fmtPickNum(order.getOrder_pickup_num()));
// 订单是否禁止退款
int isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(order.getOrder_id()) ? 1 : 2;
order.setIs_deny_return(isOrderDenyReturn);
boolean isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(order.getOrder_id());
order.setIs_deny_return(isOrderDenyReturn ? CommonConstant.Enable : CommonConstant.Disable2);
// 订单商品列表是否禁止退款
order.getOrder_items().forEach(item -> {
item.setIs_deny_return(isOrderDenyReturn);
});
if (CollUtil.isNotEmpty(order.getOrder_items())) {
if (!isOrderDenyReturn) { // 允许退款的订单再进一步检查每个商品是否不能退款
// 订单商品列表是否禁止退款
order.getOrder_items().forEach(item -> {
// 注意应该使用 order_item_id 而不是 product_id
boolean isOrderItemDenyReturn = shopOrderReturnService.isOrderItemDenyReturn(order.getOrder_id(), item.getProduct_id());
item.setIs_deny_return(isOrderItemDenyReturn ? CommonConstant.Enable : CommonConstant.Disable2);
});
} else {
// 如果整个订单都禁止退款则所有商品都禁止退款
order.getOrder_items().forEach(item -> item.setIs_deny_return(CommonConstant.Enable));
}
}
});
@ -9161,13 +9186,22 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
orderDetail.setOrder_pickup_num_str(StringUtils.fmtPickNum(orderDetail.getOrder_pickup_num()));
// 订单是否禁止退款
int isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(orderId) ? 1 : 2;
orderDetail.setIs_deny_return(isOrderDenyReturn);
boolean isOrderDenyReturn = shopOrderReturnService.isOrderDenyReturn(orderId);
orderDetail.setIs_deny_return(isOrderDenyReturn ? CommonConstant.Enable : CommonConstant.Disable2);
// 订单商品列表是否禁止退款
orderDetail.getOrder_items().forEach(item -> {
item.setIs_deny_return(isOrderDenyReturn);
});
if (CollUtil.isNotEmpty(orderDetail.getOrder_items())) {
if (!isOrderDenyReturn) { // 允许退款的订单再进一步检查每个商品是否不能退款
// 订单商品列表是否禁止退款
orderDetail.getOrder_items().forEach(item -> {
// 注意应该使用 order_item_id 而不是 product_id
boolean isOrderItemDenyReturn = shopOrderReturnService.isOrderItemDenyReturn(orderId, item.getProduct_id());
item.setIs_deny_return(isOrderItemDenyReturn ? CommonConstant.Enable : CommonConstant.Disable2);
});
} else {
// 如果整个订单都禁止退款则所有商品都禁止退款
orderDetail.getOrder_items().forEach(item -> item.setIs_deny_return(CommonConstant.Enable));
}
}
return orderDetail;
}
@ -9271,6 +9305,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
*/
@Override
public Long sameCityOrderExpireSeconds(Long defaultValue) {
if (CheckUtil.isEmpty(defaultValue)) {
defaultValue = 2100L;
}
try {
String value = accountService.getAccountBaseConfigValue(CommonConstant.CONF_KEY_SAME_CITY_ORDER_EXPIRE_SECONDS);
if (StringUtils.isBlank(value)) {
@ -9284,4 +9322,48 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
return defaultValue;
}
}
/**
* 更新订单下单时间
*
* @param orderId 订单ID
* @param orderTime 新的订单时间
* @return Boolean 更新是否成功
*/
@Override
public Boolean updateOrderTime(String orderId, Date orderTime) {
// 1. 参数校验
if (StrUtil.isBlank(orderId)) {
log.warn("[更新订单时间] 订单ID不能为空");
return false;
}
if (orderTime == null) {
log.warn("[更新订单时间] 订单时间不能为空, orderId={}", orderId);
return false;
}
try {
// 2. 构建更新条件
UpdateWrapper<ShopOrderBase> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("order_id", orderId)
.set("order_time", orderTime);
// 3. 执行更新操作
boolean result = update(updateWrapper);
if (result) {
log.info("[更新订单时间] 订单时间更新成功, orderId={}, orderTime={}", orderId, orderTime);
} else {
log.warn("[更新订单时间] 订单时间更新失败, 可能订单不存在, orderId={}", orderId);
}
return result;
} catch (Exception e) {
// 4. 异常处理
log.error("[更新订单时间] 更新订单时间时发生异常, orderId={}, orderTime={}", orderId, orderTime, e);
return false;
}
}
}

View File

@ -21,6 +21,7 @@ import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.order.mapper.ShopOrderBookingMapper;
import com.suisung.mall.shop.order.service.ShopOrderBaseService;
import com.suisung.mall.shop.order.service.ShopOrderBookingService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -30,17 +31,18 @@ import org.springframework.util.StringUtils;
import javax.annotation.Resource;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookingMapper, ShopOrderBooking> implements ShopOrderBookingService {
private static final Long MINUTES_BEFORE_BOOKING = 35L;
@Resource
private ShopOrderBookingMapper shopOrderBookingMapper;
@Lazy
@Resource
private ShopOrderBaseService shopOrderBaseService;
@Lazy
@Autowired
private RedisService redisService;
@ -64,7 +66,8 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
try {
// 计算执行时间预约时间前35分钟
long runAt = bookingAt - TimeUnit.MINUTES.toSeconds(MINUTES_BEFORE_BOOKING);
// long runAt = bookingAt - TimeUnit.MINUTES.toSeconds(CommonConstant.MIN_DELAY_MINUTES_FOR_SF_EXPRESS_ORDER);
long runAt = bookingAt - shopOrderBaseService.sameCityOrderExpireSeconds(2100L); // 35分钟超时60秒*35分钟 = 2100秒
ShopOrderBooking shopOrderBooking = new ShopOrderBooking();
shopOrderBooking.setOrder_id(orderId);
@ -77,9 +80,10 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
// 如果保存成功设置Redis过期键
if (shopOrderBooking != null) {
String redisKey = RedisConstant.Order_Booking_Task_Key + orderId;
long remainingSeconds = runAt - System.currentTimeMillis() / 1000;
// 设置过期时间为runAt时间点相对于当前时间的秒数
if (runAt > 0) {
redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), runAt);
if (remainingSeconds > 0) {
redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), remainingSeconds);
log.debug("Redis键设置成功: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt);
} else {
log.warn("过期时间无效未设置Redis键: key={}, bookingAt={}, runAt={}", redisKey, bookingAt, runAt);
@ -111,7 +115,7 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
try {
if (CheckUtil.isEmpty(shopOrderBooking.getRun_at())) {
long runAt = shopOrderBooking.getBooking_at() - TimeUnit.MINUTES.toSeconds(MINUTES_BEFORE_BOOKING);
long runAt = shopOrderBooking.getBooking_at() - shopOrderBaseService.sameCityOrderExpireSeconds(2100L); // 35分钟超时60秒*35分钟 = 2100秒
shopOrderBooking.setRun_at(runAt);
}
@ -166,22 +170,6 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
return this.page(page, queryWrapper);
}
@Override
public Boolean update(UpdateWrapper<ShopOrderBooking> updateWrapper) {
log.debug("更新预约订单任务");
if (updateWrapper == null) {
log.warn("更新条件不能为空");
return false;
}
try {
return this.update(updateWrapper);
} catch (Exception e) {
log.error("更新预约订单任务时发生异常", e);
return false;
}
}
/**
* 更改预约订单任务下单完成状态同时删除 redis 的定时任务
*
@ -205,7 +193,7 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
updateWrapper.set("status", CommonConstant.Disable2);
// 执行更新操作
boolean result = this.update(updateWrapper);
boolean result = update(updateWrapper);
if (result) {
log.debug("成功完成预约订单任务的同城配送下单: orderId={}", orderId);
@ -246,13 +234,18 @@ public class ShopOrderBookingServiceImpl extends BaseServiceImpl<ShopOrderBookin
continue;
}
redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), shopOrderBooking.getRun_at());
if (shopOrderBooking == null || shopOrderBooking.getRun_at() == null) {
log.debug("run_at 参数空不能同步到 Redis");
continue;
}
count++;
long remainingSeconds = shopOrderBooking.getRun_at() - System.currentTimeMillis() / 1000;
if (remainingSeconds > 0) {
redisService.set(redisKey, Convert.toStr(shopOrderBooking.getRun_at()), remainingSeconds);
count++;
}
}
return count;
}
}

View File

@ -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;
@ -118,27 +119,21 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
private ShopStorePrinterService shopStorePrinterService;
@Autowired
private ShopOrderInfoMapper shopOrderInfoMapper;
@Lazy
@Autowired
private ShopStoreSfOrderService shopStoreSfOrderService;
@Lazy
@Autowired
private ShopOrderBookingService shopOrderBookingService;
@Lazy
@Autowired
private SFExpressApiService sfExpressApiService;
@Lazy
@Autowired
private PushMessageService pushMessageService;
@Autowired
private ThreadPoolExecutor executor;
@Override
public Map dashboard() {
@ -870,55 +865,55 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
public Pair<Boolean, String> checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTimeStr, String bookingEndTimeStr) {
// 1. 必填参数检查
if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(bookingState)) {
return Pair.of(false, "[预约单校验] 缺少必要参数");
return Pair.of(false, "[预约单校验] 缺少必要参数");
}
// 2. 预约状态检查
if (!CommonConstant.Order_Booking_State_YY.equals(bookingState)) {
return Pair.of(true, "[预约单校验] 非预订单,跳过验证");
return Pair.of(true, "[预约单校验] 非预订单,跳过验证");
}
// 3. 时间格式检查
if (StrUtil.isBlank(bookingBeginTimeStr)) {
return Pair.of(false, "[预约单校验] 预约时间不能为空");
return Pair.of(false, "[预约单校验] 预约时间不能为空");
}
Date bookingBeginTime = DateTimeUtils.tryParseDateTimeToDate(bookingBeginTimeStr);
Date bookingEndTime = DateTimeUtils.tryParseDateTimeToDate(bookingEndTimeStr);
if (bookingBeginTime == null) {
return Pair.of(false, "[预约单校验] 预约时间格式有误");
return Pair.of(false, "[预约单校验] 预约时间格式有误");
}
// 4. 时间逻辑检查 - 开始时间不能晚于结束时间
if (bookingEndTime != null && bookingBeginTime.after(bookingEndTime)) {
return Pair.of(false, "[预约单校验] 开始时间不能晚于截止时间");
return Pair.of(false, "[预约单校验] 开始时间不能晚于截止时间");
}
// 5. 店铺信息检查
ShopStoreInfo shopStoreInfo = shopStoreInfoService.getShopStoreInfoByStoreId(storeId);
if (shopStoreInfo == null) {
return Pair.of(false, "[预约单校验] 店铺信息有误");
return Pair.of(false, "[预约单校验] 店铺信息有误");
}
if (StrUtil.isBlank(shopStoreInfo.getStore_opening_hours()) || StrUtil.isBlank(shopStoreInfo.getStore_close_hours())) {
shopStoreInfo.setStore_opening_hours("09:00");
shopStoreInfo.setStore_close_hours("06:00");
logger.warn("[预约单校验] 店铺营业时间未设置,请联系商家,默认指定 {}-{}", shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours());
logger.warn("[预约单校验] 店铺营业时间未设置,请联系商家,默认指定 {}-{}", shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours());
}
// 6. 店铺营业时间检查
if (!DateTimeUtils.isTimeInRange(shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours(), bookingBeginTime)) {
String message = StrUtil.format("[预约单校验] 请在 {}-{} 店铺营业时间内预约下单", shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours());
String message = StrUtil.format("[预约单校验] 请在 {}-{} 店铺营业时间内预约下单", shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours());
return Pair.of(false, message);
}
// 7. 预约时间范围检查 - 仅能预约50分钟后的订单防止用户在下单页面停留太长45分钟也是可以通过的
Date fortyFiveMinutesLater = DateUtil.offsetMinute(new Date(), 45);
if (!bookingBeginTime.after(fortyFiveMinutesLater)) {
return Pair.of(false, "[预约订单校验] 请预约50分后的订单");
Date fortyFiveMinutesLater = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER - 5);
if (bookingBeginTime.before(fortyFiveMinutesLater)) {
return Pair.of(false, String.format("[预约单校验] 请预约%d分后的订单", CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER));
}
return Pair.of(true, "[预约单校验] 成功");
return Pair.of(true, "[预约单校验] 成功");
}
/**
@ -974,7 +969,6 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
return Pair.of(false, "预约订单已下过单,请勿重复操作");
}
// 调用顺丰接口创建订单
Pair<Boolean, String> sfResult = sfExpressApiService.innerCreateSfExpressOrder(orderId, 0L);
if (sfResult == null) {
@ -997,8 +991,8 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
shopOrderBookingService.updateOrderBookingFinishState(orderId);
// 发送推送消息给商家
String title = "您有一笔新的订单,请注意查收";
String content = String.format("这笔新的订单:%s用户于%s下的单注意查收",
String title = "您有一笔新的订单,请及时拣货配送";
String content = String.format("这笔新的订单:%s用户于%s下的单及时拣货配送",
orderId, DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd HH:mm:ss"));
JSONObject payload = new JSONObject();
payload.set("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST);
@ -1017,36 +1011,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<>();
@ -1061,8 +1039,9 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
String dateStr = DateUtil.format(currentDate, "yyyy-MM-dd");
String displayDateStr = DateUtil.format(currentDate, "MM月dd日");
// 安全获取星期信息
String weekStr = "未知";
String weekStr = "星期";
try {
weekStr = DateTimeUtils.getWeekOfDate(currentDate);
} catch (Exception e) {
@ -1082,14 +1061,48 @@ 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"))) {
String startTimeStr = timesMap.get("startTimeStr");
String endTimeStr = timesMap.get("endTimeStr");
List<BookingArgDTO.BookingArgItem> timeSlots = generateTimeSlots(dateStr, startTimeStr, endTimeStr, i == 0);
if (i == 0) {
// 对于今天移除除"立即送出"外的所有时间槽
items.addAll(timeSlots.stream().filter(item -> item.getBooking_state() != 1).collect(Collectors.toList()));
} else {
// 对于其他日期添加所有时间槽
items.addAll(timeSlots);
}
bookingArgDTO.setWorking_hours(String.format("%s-%s", startTimeStr, endTimeStr));
} 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;
}
@ -1135,13 +1148,13 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
}
// 时间槽间隔分钟
int slotInterval = 30;
int slotInterval = 15;
// 对于今天需要特殊处理第二个时间槽从当前时间+50分钟开始
Date startTime = openTime;
if (isToday) {
// 当前时间+50分钟作为第二个时间槽的开始时间
Date nowPlusFifty = DateUtil.offsetMinute(new Date(), 50);
Date nowPlusFifty = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER);
// 如果当前时间+50分钟在营业时间范围内则从该时间开始
if (nowPlusFifty.after(openTime) && nowPlusFifty.before(closeTime)) {
@ -1207,5 +1220,55 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
}
}
/**
* 根据 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<Integer> uniqueStoreIds = Arrays.stream(storeIds.split(","))
.map(String::trim)
.filter(StrUtil::isNotBlank)
.map(Integer::valueOf)
.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();
}
}
}

View File

@ -1512,7 +1512,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (lestFrozenQuantity.compareTo(0) < 0) {
lestFrozenQuantity = 0;
}
ShopProductItem upadteShopProductItem=new ShopProductItem();
ShopProductItem upadteShopProductItem = new ShopProductItem();
upadteShopProductItem.setItem_id(shopOrderItem.getItem_id());
upadteShopProductItem.setItem_quantity(productItem.getItem_quantity() + returnNum);
upadteShopProductItem.setItem_quantity_frozen(lestFrozenQuantity);
@ -2112,32 +2112,27 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
* 如果订单状态为"已收货""已完成"并且当前时间超过允许提现时间则标记为不可退货
*
* @param shopOrderInfo 订单信息
* @param shopOrderItem 订单商品项
* @param shopProductIndex 商品索引信息
* @param shopOrderItem 订单商品项可选
* @param shopProductIndex 商品索引信息可选如果shopOrderItem存在则可以为空
* @return boolean true表示禁止退货false表示允许退货
*/
public boolean ifDenyReturn(ShopOrderInfo shopOrderInfo, ShopOrderItem shopOrderItem, ShopProductIndex shopProductIndex) {
// 1. 参数校验
if (shopOrderItem == null) {
log.debug("[是否禁止退货] 订单商品数据为空,不允许退货");
return true;
}
if (shopOrderInfo == null) {
log.debug("[是否禁止退货] 订单信息为空,不允许退货");
log.debug("[是否禁止退货] 订单信息为空,禁止退货");
return true;
}
String orderId = shopOrderInfo.getOrder_id();
Integer orderStateId = shopOrderInfo.getOrder_state_id();
if (StrUtil.isBlank(orderId) || CheckUtil.isEmpty(orderStateId)) {
log.debug("[是否禁止退货] 订单ID或订单状态为空不允许退货");
log.debug("[是否禁止退货] 订单ID或订单状态为空禁止退货");
return true;
}
if (orderStateId.intValue() == StateCode.ORDER_STATE_CANCEL ||
orderStateId.intValue() == StateCode.ORDER_STATE_WAIT_PAY) {
log.debug("[是否禁止退货] 订单已取消或未支付,不允许退货 order_id: {}", orderId);
log.debug("[是否禁止退货] 订单状态为已取消或未支付禁止退货order_id: {}, orderStateId: {}", orderId, orderStateId);
return true;
}
@ -2152,11 +2147,13 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
// 检查是否超过退货期限
if (orderDealTime != null && withdrawTime.compareTo(orderDealTime) > 0) {
log.debug("[是否禁止退货] 订单已超过退货期限不允许退货order_id: {}", orderId);
log.debug("[是否禁止退货] 订单已超过退货期限禁止退货order_id: {}, orderDealTime: {}, withdrawTime: {}",
orderId, orderDealTime, withdrawTime);
return true;
}
} catch (Exception e) {
log.error("[是否禁止退货] 检查订单退货期限时发生异常不允许退货order_id: {}", orderId, e);
log.error("[是否禁止退货] 检查订单退货期限时发生异常禁止退货order_id: {}", orderId, e);
return true;
}
}
@ -2165,32 +2162,40 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (lklOrderSeparateService != null && lklOrderDrawService != null) {
boolean isSeparated = lklOrderSeparateService.isOrderSeparated(orderId);
boolean isDrawn = lklOrderDrawService.isOrderDrawed(orderId);
boolean isLklProcessed = isSeparated && isDrawn;
if (isSeparated && isDrawn) {
log.debug("[是否禁止退货] 拉卡拉分账订单已提现不允许退货order_id: {}", orderId);
if (isLklProcessed) {
log.debug("[是否禁止退货] 拉卡拉分账订单已提现禁止退货order_id: {}, separated: {}, drawed: {}",
orderId, isSeparated, isDrawn);
return true;
}
}
} catch (Exception e) {
log.error("[是否禁止退货] 检查拉卡拉分账状态时发生异常不允许退货order_id: {}", orderId, e);
log.error("[是否禁止退货] 检查拉卡拉分账状态时发生异常禁止退货order_id: {}", orderId, e);
return true;
}
Long productId = shopOrderItem.getProduct_id();
if (CheckUtil.isEmpty(productId)) {
log.debug("[是否禁止退货] 商品ID为空不允许退货");
return true;
// 如果没有商品相关信息则只检查订单级别
if (shopOrderItem == null && shopProductIndex == null) {
log.debug("[是否禁止退货] 无商品信息仅检查订单级别允许退货order_id: {}", orderId);
return false;
}
Long productId = null;
if (shopOrderItem != null) {
productId = shopOrderItem.getProduct_id();
}
// 3. 再判断具体商品是否可以退货商品级别检查
// 获取商品索引信息
ShopProductIndex productIndex = shopProductIndex;
if (productIndex == null) {
if (productIndex == null && productId != null) {
productIndex = shopProductIndexService.get(productId);
}
if (productIndex == null) {
log.debug("[是否禁止退货] 商品索引信息不存在,不允许退货product_id: {}", productId);
return true;
log.debug("[是否禁止退货] 商品索引信息不存在,允许退货order_id: {}", orderId);
return false;
}
// 3.1 检查商品是否设置了禁止退货标识
@ -2199,17 +2204,17 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
try {
List<Integer> contractTypeIds = Convert.toList(Integer.class, contractTypeIdsStr);
if (contractTypeIds != null && contractTypeIds.contains(StateCode.CONTRACT_TYPE_DENY_RETURN)) {
log.debug("[是否禁止退货] 商品设置了禁止退货标识,不允许退货order_id: {}, product_id: {}", orderId, productId);
log.debug("[是否禁止退货] 商品设置了禁止退货标识,禁止退货order_id: {}, product_id: {}", orderId, productIndex.getProduct_id());
return true;
}
} catch (Exception e) {
log.error("[是否禁止退货] 解析商品保障类型失败,允许退货order_id: {}, product_id: {}", orderId, productId, e);
return true;
log.error("[是否禁止退货] 解析商品保障类型失败,允许退货order_id: {}, product_id: {}", orderId, productIndex.getProduct_id(), e);
return false;
}
}
// 默认允许退货
log.debug("[是否禁止退货] 允许退货}");
log.debug("[是否禁止退货] 商品允许退货order_id: {}, product_id: {}", orderId, productIndex.getProduct_id());
return false;
}
@ -2226,7 +2231,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
* @param productId 商品ID
* @return boolean true表示禁止退货false表示允许退货
*/
public boolean ifOrderItemDenyReturn(String orderId, Long productId) {
public boolean isOrderItemDenyReturn(String orderId, Long productId) {
// 复用已有的方法逻辑避免重复代码
return ifDenyReturn(
shopOrderInfoService.get(orderId),
@ -2235,7 +2240,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
);
}
/**
* 判断订单是否禁止退货
*
@ -2246,7 +2250,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
public boolean isOrderDenyReturn(String orderId) {
// 参数校验
if (StrUtil.isBlank(orderId)) {
log.warn("[订单是否禁止退货] 订单ID为空不允许退货无法判断退货状态");
log.debug("[订单是否禁止退货] 订单ID为空禁止退货orderId: {}", orderId);
return true;
}
@ -2254,20 +2258,20 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
// 获取订单信息
ShopOrderInfo shopOrderInfo = shopOrderInfoService.get(orderId);
if (shopOrderInfo == null) {
log.error("[订单是否禁止退货] 订单信息不存在不允许退货订单ID: {}", orderId);
log.debug("[订单是否禁止退货] 订单信息不存在禁止退货orderId: {}", orderId);
return true;
}
// 检查订单状态是否已收货或已完成
Integer orderStateId = shopOrderInfo.getOrder_state_id();
if (CheckUtil.isEmpty(orderStateId)) {
log.warn("[订单是否禁止退货] 订单状态为空,不允许退货");
log.debug("[订单是否禁止退货] 订单状态为空禁止退货orderId: {}", orderId);
return true;
}
if (orderStateId.intValue() == StateCode.ORDER_STATE_CANCEL ||
orderStateId.intValue() == StateCode.ORDER_STATE_WAIT_PAY) {
log.debug("[是否禁止退货] 订单已取消或未支付,不允许退货, order_id: {}", orderId);
log.debug("[订单是否禁止退货] 订单状态为已取消或未支付禁止退货orderId: {}, orderStateId: {}", orderId, orderStateId);
return true;
}
@ -2277,27 +2281,32 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
// 获取可提现时间戳
Long withdrawTime = shopOrderBaseService.getWithdrawTime();
Long orderDealTime = shopOrderInfo.getOrder_deal_time();
log.debug("[订单是否禁止退货] 订单:{} 可提现时间戳: {},确认收货时间:{}", orderId, withdrawTime, orderDealTime);
// 如果订单成交时间早于可提现时间说明已过退货期
if (orderDealTime != null && withdrawTime.compareTo(orderDealTime) > 0) {
log.debug("[订单是否禁止退货] 订单:{} 已过退货期,不允许退货", orderId);
log.debug("[订单是否禁止退货] 订单已超过退货期限禁止退货orderId: {}, orderDealTime: {}, withdrawTime: {}",
orderId, orderDealTime, withdrawTime);
return true;
}
}
// 检查拉卡拉分账和提现状态
if (lklOrderSeparateService.isOrderSeparated(orderId) &&
lklOrderDrawService.isOrderDrawed(orderId)) {
log.debug("[订单是否禁止退货] 订单[{}]已提现,不允许退货", orderId);
boolean isLklSeparated = lklOrderSeparateService.isOrderSeparated(orderId);
boolean isLklDrawed = lklOrderDrawService.isOrderDrawed(orderId);
boolean isLklProcessed = isLklSeparated && isLklDrawed;
if (isLklProcessed) {
log.debug("[订单是否禁止退货] 拉卡拉分账订单已提现禁止退货orderId: {}, separated: {}, drawed: {}",
orderId, isLklSeparated, isLklDrawed);
return true;
}
// 默认允许退货
// 允许退货
log.debug("[订单是否禁止退货] 订单允许退货orderId: {}", orderId);
return false;
} catch (Exception e) {
log.error("[订单是否禁止退货] 查询订单{}退货状态异常,不允许退货", orderId, e);
log.error("[订单是否禁止退货] 查询订单退货状态异常禁止退货orderId: {}", orderId, e);
return true;
}
}

View File

@ -31,16 +31,16 @@ public interface SFExpressApiService {
*
* @param mchId 商家入驻编号
* @param storeId 商家门店ID
* @param shopName 店名
* @param cityName 城市
* @param shopAddress 店铺详细地址
* // * @param shopName 店名
* // * @param cityName 城市
* // * @param shopAddress 店铺详细地址
* @param contactName 店铺联系人
* @param contactPhone 店铺电话
* @param longitude 经度
* @param latitude 纬度
* @return
*/
Pair<Boolean, String> createSfExpressShop(Long mchId, Integer storeId, String shopName, String cityName, String shopAddress, String contactName, String contactPhone, String longitude, String latitude);
Pair<Boolean, String> createSfExpressShop(Long mchId, Integer storeId, String contactName, String contactPhone, String longitude, String latitude);
/**

View File

@ -185,42 +185,42 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
return Pair.of(false, "联系人手机号不能为空");
}
AddressParseResultTO addressParseResultTO = AddressUtil.parseAddress(shopMchEntry.getStore_address());
// 解析城市名称
String cityName = "桂平市"; // 默认城市
// 去掉省市区的详细地址
String storeAddress = addressParseResultTO.getDetailAddress();
if (StrUtil.isNotBlank(shopMchEntry.getStore_area())) {
String[] areaNames = shopMchEntry.getStore_area().split("/");
if (areaNames.length >= 3) {
cityName = areaNames[areaNames.length - 1];
} else {
cityName = shopMchEntry.getStore_area().replace("/", "");
}
} else {
cityName = addressParseResultTO.getCity();
}
// 如果解析后城市名为空使用默认值
if (StrUtil.isBlank(cityName)) {
cityName = "桂平市";
logger.warn("[顺丰] 城市名为空,使用默认城市: {}", cityName);
} else {
logger.debug("[顺丰] 解析得到城市名: {}", cityName);
}
// 为了其他顺丰店同名店铺名称加上[门店ID]; xxxx[xxxx] 聚万家生鲜超市[69]
String shopStoreName = String.format("%s[%s]", shopMchEntry.getStore_name(), shopMchEntry.getStore_id());
// AddressParseResultTO addressParseResultTO = AddressUtil.parseAddress(shopMchEntry.getStore_address());
// // 解析城市名称
// String cityName = "桂平市"; // 默认城市
//
// // 去掉省市区的详细地址
// String storeAddress = addressParseResultTO.getDetailAddress();
//
// if (StrUtil.isNotBlank(shopMchEntry.getStore_area())) {
// String[] areaNames = shopMchEntry.getStore_area().split("/");
// if (areaNames.length >= 3) {
// cityName = areaNames[areaNames.length - 1];
// } else {
// cityName = shopMchEntry.getStore_area().replace("/", "");
// }
// } else {
// cityName = addressParseResultTO.getCity();
// }
//
// // 如果解析后城市名为空使用默认值
// if (StrUtil.isBlank(cityName)) {
// cityName = "桂平市";
// logger.warn("[顺丰] 城市名为空,使用默认城市: {}", cityName);
// } else {
// logger.debug("[顺丰] 解析得到城市名: {}", cityName);
// }
//
// // 为了其他顺丰店同名店铺名称加上[门店ID]; xxxx[xxxx] 聚万家生鲜超市[69]
// String shopStoreName = String.format("%s[%s]", shopMchEntry.getStore_name(), shopMchEntry.getStore_id());
// 调用创建店铺方法
Pair<Boolean, String> result = createSfExpressShop(
mchId,
Convert.toInt(shopMchEntry.getStore_id()),
shopStoreName,
cityName,
storeAddress,
// shopStoreName,
// cityName,
// storeAddress,
shopMchEntry.getContact_name(),
contactMobile,
shopMchEntry.getStore_longitude(),
@ -237,9 +237,6 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
*
* @param mchId 商家入驻编号
* @param storeId 商家门店ID
* @param shopName 店名
* @param cityName 城市
* @param shopAddress 店铺详细地址
* @param contactName 店铺联系人
* @param contactPhone 店铺电话
* @param longitude 经度
@ -247,17 +244,16 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
* @return Pair<Boolean, String> 第一个元素表示是否成功第二个元素表示结果信息或错误信息
*/
@Override
public Pair<Boolean, String> createSfExpressShop(Long mchId, Integer storeId, String shopName, String cityName,
String shopAddress, String contactName, String contactPhone,
public Pair<Boolean, String> createSfExpressShop(Long mchId, Integer storeId, String contactName, String contactPhone,
String longitude, String latitude) {
logger.info("开始创建顺丰同城店铺, storeId: {}", storeId);
try {
// 1. 验证必要参数
if ((CheckUtil.isEmpty(mchId) && CheckUtil.isEmpty(storeId)) ||
StringUtils.isAnyBlank(shopName, shopAddress, contactName, contactPhone)) {
logger.error("创建顺丰店铺缺少必要参数mchId:{}, storeId:{},shopName:{},shopAddress:{},contactName:{},contactPhone:{}",
mchId, storeId, shopName, shopAddress, contactName, contactPhone);
StringUtils.isAnyBlank(contactName, contactPhone)) {
logger.error("创建顺丰店铺缺少必要参数mchId:{}, storeId:{},contactName:{},contactPhone:{}",
mchId, storeId, contactName, contactPhone);
return Pair.of(false, "创建顺丰店铺,缺少必要参数!");
}
@ -280,6 +276,29 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
}
}
AddressParseResultTO addressParseResultTO = AddressUtil.parseAddress(shopMchEntry.getStore_address());
String cityName = "桂平市";
String shopAddress = addressParseResultTO != null ? addressParseResultTO.getDetailAddress() : "";
if (StrUtil.isNotBlank(shopMchEntry.getStore_area())) {
String[] areaNames = shopMchEntry.getStore_area().split("/");
cityName = areaNames.length >= 3 ? areaNames[2] :
areaNames.length > 0 ? areaNames[areaNames.length - 1] : cityName;
} else if (addressParseResultTO != null && StrUtil.isNotBlank(addressParseResultTO.getCity())) {
cityName = addressParseResultTO.getCity();
}
if (StrUtil.isBlank(cityName)) {
cityName = "桂平市";
logger.warn("[顺丰] 城市名为空,使用默认城市: {}", cityName);
} else {
logger.debug("[顺丰] 解析得到城市名: {}", cityName);
}
// 为了其他顺丰店同名店铺名称加上[门店ID]; xxxx[xxxx] 聚万家生鲜超市[69]
String shopName = String.format("%s[%s]", shopMchEntry.getStore_name(), shopMchEntry.getStore_id());
// 3. 获取或初始化商家配送信息
ShopStoreSameCityTransportBase transportBase = shopStoreSameCityTransportBaseService
.getShopStoreSameCityTransportBaseById(Long.valueOf(storeId));

View File

@ -79,5 +79,18 @@ public class ShopStoreInfoController extends BaseControllerImpl {
return CommonResult.success(shopStoreInfoService.remove(store_id));
}
/**
* 获取店铺的内部运费 shopping_fee_inner 远程调用用途
*
* @param store_id
* @return
*/
@ApiOperation(value = "获取店铺的内部运费 shopping_fee_inner", notes = "获取店铺的内部运费 shopping_fee_inner (远程调用用途)")
@RequestMapping(value = "/shopping-fee-inner", method = RequestMethod.POST)
public Integer storeShoppingFeeInner(@RequestParam(name = "store_id") Integer store_id) {
return shopStoreInfoService.getStoreShippingFeeInner(store_id);
}
}

View File

@ -10,6 +10,7 @@ import com.suisung.mall.core.web.service.IBaseService;
import com.suisung.mall.shop.product.pojo.vo.ProductVo;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
@ -57,7 +58,16 @@ public interface ShopStoreActivityBaseService extends IBaseService<ShopStoreActi
List<Long> getActivityAllItemIds(Map activity_row);
boolean verifyActivity(Map activity_row);
boolean isActivityTimeValid(Map activity_row);
/**
* 验证活动时间是否有效
*
* @param starTime 活动开始时间
* @param endTime 活动结束时间
* @return 时间是否有效
*/
boolean isActivityTimeValid(Date starTime, Date endTime);
Map listsMarketing();
@ -169,4 +179,14 @@ public interface ShopStoreActivityBaseService extends IBaseService<ShopStoreActi
* @return
*/
Map getGiftbag(Integer activity_id);
/**
* 判断是否是砍价活动
*
* @param activity_id
* @return
*/
Boolean isCutPriceActivity(Integer activity_id);
}

View File

@ -38,4 +38,13 @@ public interface ShopStoreInfoService extends IBaseService<ShopStoreInfo> {
*/
List<String> getStoreKeeperMobile(Integer storeId);
/**
* 获取店铺的内部运费 shopping_fee_inner
* <p>
* 店铺内部运费单位0-使用平台的内部运费>0 使用店铺的内部运费
*
* @param storeId
* @return
*/
Integer getStoreShippingFeeInner(Integer storeId);
}

View File

@ -646,27 +646,7 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl<ShopMchEntryMapper,
return;
}
// 这是E签宝的逻辑
// if (CommonConstant.MCH_APPR_STA_PASS.equals(record.getApproval_status())
// && (StrUtil.isBlank(record.getContract_download_url()) || !CommonConstant.CONTRACT_SIGN_STA_FINISH.equals(record.getSigned_status()))) {
// // 审核通过的但是没有合同文件的情况要进一步同步状态和合同文件
// Pair<Integer, String> contractInfo = esignContractService.checkSignFlowStatus(record.getLogin_mobile());
// if (contractInfo != null) {
// record.setSigned_status(contractInfo.getFirst());
// record.setContract_download_url(contractInfo.getSecond());
//
// // 更改同步合同审核状态和合同下载地址
// taskService.executeTask(() -> {
// log.debug("###更改同步合同审核状态和下载地址###");
// if (!updateMerchEntrySignedStatusAndContractDownloadUrl(record.getLogin_mobile(), record.getSigned_status(), record.getContract_download_url())) {
// log.error("###更改同步合同审核状态和下载地址失败###");
// }
// });
// }
// }
// === 拉卡拉签约逻辑 ===
Long mchId = record.getId();
if (CommonConstant.Enable.equals(record.getHas_ec_signed())) {
LklLedgerEc ec = lklLedgerEcService.getByMchId(
@ -830,7 +810,7 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl<ShopMchEntryMapper,
&& CommonConstant.Enable.equals(shopMchEntry.getHas_bind_receiver());
if (!isValidStatus) {
log.debug("入驻记录状态未全部通过审核不能创建店铺入驻ID: {}", mchId);
log.error("入驻记录状态未全部通过审核不能创建店铺入驻ID: {}", mchId);
return false;
}

View File

@ -36,7 +36,10 @@ import com.suisung.mall.common.modules.product.ShopProductItem;
import com.suisung.mall.common.modules.store.ShopStoreActivityBase;
import com.suisung.mall.common.modules.store.ShopStoreActivityItem;
import com.suisung.mall.common.modules.store.ShopStoreBase;
import com.suisung.mall.common.utils.*;
import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.common.utils.I18nUtil;
import com.suisung.mall.common.utils.StringUtils;
import com.suisung.mall.common.utils.UserInfoService;
import com.suisung.mall.core.web.service.RedisService;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.activity.service.*;
@ -2046,13 +2049,51 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
return item_id_row;
}
public boolean verifyActivity(Map activity_row) {
if (CollUtil.isNotEmpty(activity_row)) {
Date beginMillisecond = Convert.toDate(activity_row.get("activity_starttime"));
Date endMillisecond = Convert.toDate(activity_row.get("activity_endtime"));
return RegexUtil.isWithinTimeRange(beginMillisecond, endMillisecond);
/**
* 验证活动时间是否有效
*
* @param activityRow 活动数据
* @return 时间是否有效
*/
@Override
public boolean isActivityTimeValid(Map activityRow) {
if (CollUtil.isEmpty(activityRow)) {
return false;
}
return false;
Object startTimeObj = activityRow.get("activity_starttime");
Object endTimeObj = activityRow.get("activity_endtime");
if (startTimeObj == null || endTimeObj == null) {
return false;
}
Date startTime = Convert.toDate(startTimeObj);
Date endTime = Convert.toDate(endTimeObj);
if (startTime == null || endTime == null) {
return false;
}
Date now = new Date();
return !now.before(startTime) && !now.after(endTime);
}
/**
* 验证活动时间是否有效
*
* @param starTime 活动开始时间
* @param endTime 活动结束时间
* @return 时间是否有效
*/
@Override
public boolean isActivityTimeValid(Date starTime, Date endTime) {
if (starTime == null || endTime == null) {
return false;
}
Date now = new Date();
return !now.before(starTime) && !now.after(endTime);
}
@Override
@ -2534,9 +2575,9 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
ShopStoreActivityBase one = shopStoreActivityBaseService.get(activity_id);
Map activity_row = Convert.toMap(Object.class, Object.class, one);
/* Map activity_row = Convert.toMap(Object.class, Object.class, one);
/*if (!verifyActivity(activity_row)) {
if (!verifyActivity(activity_row)) {
Map data = new HashMap();
data.put("msg", I18nUtil._("该砍价活动不存在"));
return data;
@ -3960,43 +4001,68 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
return item_row;
}
/**
* 计算砍价活动中每次砍价的价格
*
* @param activity_row 活动基础信息
* @param ac_row 砍价活动记录
* @return 砍价金额
*/
@Override
public BigDecimal getCutDownPrice(ShopStoreActivityBase activity_row, ShopActivityCutprice ac_row) {
// 1. 参数校验
if (activity_row == null || ac_row == null) {
throw new ApiException(I18nUtil._("活动不存在!"));
}
// 2. 活动类型校验
Integer activity_type_id = activity_row.getActivity_type_id();
if (ObjectUtil.notEqual(StateCode.ACTIVITY_TYPE_CUTPRICE, activity_type_id)) {
throw new ApiException(I18nUtil._("活动不存在!"));
}
BigDecimal price = BigDecimal.ZERO;
BigDecimal price;
// 3. 解析活动规则
String str_activity_rule = activity_row.getActivity_rule();
JSONObject activity_rule = JSONObject.parseObject(str_activity_rule);
Integer cut_down_type = activity_rule.getObject("cut_down_type", Integer.class);
BigDecimal ac_sale_price = ac_row.getAc_sale_price();
BigDecimal ac_mix_limit_price = ac_row.getAc_mix_limit_price();
// 4. 根据砍价类型计算砍价金额
switch (cut_down_type) {
case 1:
// 固定金额砍价
price = activity_rule.getObject("cut_down_fixed_price", BigDecimal.class);
break;
case 2:
// 随机金额砍价
BigDecimal cut_down_min_limit_price = activity_rule.getObject("cut_down_min_limit_price", BigDecimal.class);
Integer cut_down_user_num = activity_rule.getObject("cut_down_user_num", Integer.class);
Integer ac_num = ac_row.getAc_num();
List<Integer> arr = randMoney(NumberUtil.sub(ac_sale_price, cut_down_min_limit_price), cut_down_user_num - ac_num);
Integer randPrice = arr.size() == 1 ? arr.get(0) : arr.get(RandomUtil.randomInt(0, arr.size() - 1));
// 计算剩余可砍价金额和剩余次数
List<Integer> arr = randMoney(
NumberUtil.sub(ac_sale_price, cut_down_min_limit_price),
cut_down_user_num - ac_num
);
// 随机选择一个砍价金额
Integer randPrice = arr.size() == 1 ? arr.get(0) : arr.get(RandomUtil.randomInt(0, arr.size()));
price = NumberUtil.div(BigDecimal.valueOf(randPrice), 100, 2);
break;
default:
// 未知砍价类型返回0
price = BigDecimal.ZERO;
break;
}
// 5. 返回砍价金额不超过可砍价的上限
return NumberUtil.min(NumberUtil.sub(ac_sale_price, ac_mix_limit_price), price);
}
@Override
public List<ShopStoreActivityBase> getCouponsList(Integer activity_type_id) {
QueryWrapper<ShopStoreActivityBase> wrapper = new QueryWrapper<>();
@ -4310,6 +4376,33 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
return data;
}
/**
* 判断是否是砍价活动
*
* @param activity_id 活动ID
* @return 是否为砍价活动
*/
@Override
public Boolean isCutPriceActivity(Integer activity_id) {
// 参数校验
if (activity_id == null || activity_id <= 0) {
return false;
}
try {
// 使用exists查询优化性能只判断是否存在而不加载数据
QueryWrapper<ShopStoreActivityBase> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("activity_id", activity_id);
queryWrapper.eq("activity_type_id", StateCode.ACTIVITY_TYPE_CUTPRICE);
return shopStoreActivityBaseMapper.selectCount(queryWrapper) > 0;
} catch (Exception e) {
// 异常处理防止影响主流程
logger.error("判断是否为砍价活动时发生异常activity_id={}", activity_id, e);
return false;
}
}
@Transactional
public boolean removeActivityBase(Integer activity_id, ShopStoreActivityBase activity_row) {

View File

@ -3430,15 +3430,14 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
shopMchEntryService.updateMerchEntryStoreStatus(shopMchEntry.getId(), storeId);
}
// 创建顺丰店铺修复经纬度参数错误
if (storeArea != null) {
String[] areaNames = storeArea.split("/");
String cityName = areaNames.length > 0 ? areaNames[areaNames.length - 1] : storeArea.replace("/", "");
// 创建顺丰店铺
// if (storeArea != null) {
// String[] areaNames = storeArea.split("/");
// String cityName = areaNames.length > 0 ? areaNames[areaNames.length - 1] : storeArea.replace("/", "");
sfExpressApiService.createSfExpressShop(mchId, storeId, shopMchEntry.getStore_name(),
cityName, shopMchEntry.getStore_address(), shopMchEntry.getContact_name(),
contact_mobile, shopMchEntry.getStore_longitude(), shopMchEntry.getStore_latitude());
}
sfExpressApiService.createSfExpressShop(mchId, storeId, shopMchEntry.getContact_name(),
contact_mobile, shopMchEntry.getStore_longitude(), shopMchEntry.getStore_latitude());
// }
return Pair.of(storeId, "新增成功");
} catch (Exception e) {

View File

@ -8,19 +8,18 @@ import com.suisung.mall.common.feignService.AccountService;
import com.suisung.mall.common.modules.account.AccountUserBase;
import com.suisung.mall.common.modules.store.ShopStoreAnalytics;
import com.suisung.mall.common.modules.store.ShopStoreInfo;
import com.suisung.mall.common.utils.CheckUtil;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.store.mapper.ShopStoreInfoMapper;
import com.suisung.mall.shop.store.service.ShopStoreAnalyticsService;
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
import com.suisung.mall.shop.store.service.ShopStoreInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
@ -32,6 +31,7 @@ import java.util.stream.Collectors;
* @author Xinze
* @since 2021-06-16
*/
@Slf4j
@Service
public class ShopStoreInfoServiceImpl extends BaseServiceImpl<ShopStoreInfoMapper, ShopStoreInfo> implements ShopStoreInfoService {
@ -101,15 +101,64 @@ public class ShopStoreInfoServiceImpl extends BaseServiceImpl<ShopStoreInfoMappe
/**
* 获取店长的手机号码
*
* @param storeId
* @return
* @param storeId 店铺ID
* @return 手机号码列表如果未找到则返回null
*/
@Override
public List<String> getStoreKeeperMobile(Integer storeId) {
// 参数校验
if (storeId == null) {
return null;
}
QueryWrapper<ShopStoreInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("store_id", storeId).select("store_tel");
return null;
List<ShopStoreInfo> list = find(queryWrapper);
// 空值检查优化
if (list == null || list.isEmpty()) {
return null;
}
// 使用方法引用简化代码
return list.stream()
.map(ShopStoreInfo::getStore_tel)
.filter(Objects::nonNull) // 过滤掉null值
.collect(Collectors.toList());
}
/**
* 获取店铺的内部运费 shopping_fee_inner
* <p>
* 店铺内部运费单位0-使用平台的内部运费>0 使用店铺的内部运费
*
* @param storeId 店铺ID
* @return 内部运费单位分
*/
@Override
public Integer getStoreShippingFeeInner(Integer storeId) {
// 参数校验
if (storeId == null) {
return 0;
}
try {
QueryWrapper<ShopStoreInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("store_id", storeId).select("shopping_fee_inner");
ShopStoreInfo shopStoreInfo = getOne(queryWrapper);
if (shopStoreInfo == null || CheckUtil.isEmpty(shopStoreInfo.getShopping_fee_inner())) {
return 0;
}
return shopStoreInfo.getShopping_fee_inner();
} catch (Exception e) {
// 记录日志或打印错误信息
log.error("获取店铺内部运费失败storeId: {}", storeId, e);
// 返回默认值避免影响主流程
return 0;
}
}
}

View File

@ -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);

View File

@ -2670,7 +2670,7 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl<ShopUserCartMapper,
}
// RMK 平台最低配送费单位add:2025-09-26
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee(Convert.toInt(storeId));
if (canThrow
&& CheckUtil.isNotEmpty(innerMinDeliverFee)
&& CheckUtil.isNotEmpty(orderSelMoneyAmount)

View File

@ -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) {

View File

@ -20686,7 +20686,7 @@
14 == t[_x41903[4420]] && t[_x41903[124]] == a && publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4592])),
t[_x41903[4594]][_x41903[4593]] = e[_x41903[473]][_x41903[688]],
c(), s = r = o = 100;
c(), s = r = o = 10240;
}, s, o, r);
}) : 5 == e && $[_x41903[39]](l[_x41903[820]], function(e, t) {
15 == t[_x41903[4420]] && t[_x41903[124]] == a && publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
@ -20700,39 +20700,39 @@
if (i[_x41903[124]] == $(n[_x41903[476]])[_x41903[217]](_x41903[4584])) {
switch (parseInt(i[_x41903[4488]][_x41903[4596]])) {
case 1:
r = o = 750, s = 300;
r = o = 750, s = 10240;
break;
case 2:
o = 750, r = 375, s = 300;
o = 750, r = 375, s = 10240;
break;
case 3:
o = 375, r = 750, s = 300;
o = 375, r = 750, s = 10240;
break;
case 4:
o = 750, r = 188, s = 300;
o = 750, r = 188, s = 10240;
break;
case 5:
r = o = 375, s = 300;
r = o = 375, s = 10240;
break;
case 6:
o = 375, r = 188, s = 300;
o = 375, r = 188, s = 10240;
break;
case 7:
o = 188, r = 375, s = 300;
o = 188, r = 375, s = 10240;
break;
case 8:
r = o = 188, s = 300;
r = o = 188, s = 10240;
break;
case 9:
o = 750, r = 500, s = 300;
o = 750, r = 500, s = 10240;
}
publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4597])),
@ -20750,19 +20750,19 @@
if (t[_x41903[124]] == $(n[_x41903[476]])[_x41903[217]](_x41903[4584])) {
switch (parseInt(i[_x41903[4600]][_x41903[4601]])) {
case 1:
r = o = 350, s = 200;
r = o = 350, s = 10240;
break;
case 2:
r = o = 180, s = 200;
r = o = 180, s = 10240;
break;
case 3:
o = 355, r = 166, s = 200;
o = 355, r = 166, s = 10240;
break;
case 4:
r = o = 140, s = 150;
r = o = 140, s = 10240;
}
publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4602])),
@ -20791,13 +20791,13 @@
i[_x41903[4612]][_x41903[4611]] = e[_x41903[473]][_x41903[688]],
c();
}, 1e3, 700, 700) : 14 == i[_x41903[4420]] ? $[_x41903[39]](i[_x41903[4594]][_x41903[473]], function(e, t) {
t[_x41903[124]] == $(n[_x41903[476]])[_x41903[217]](_x41903[4584]) && (s = r = o = 100,
t[_x41903[124]] == $(n[_x41903[476]])[_x41903[217]](_x41903[4584]) && (s = r = o = 10240,
publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4613])),
t[_x41903[2345]] = e[_x41903[473]][_x41903[688]],
c();
}, s, o, r));
}) : 15 == i[_x41903[4420]] ? i[_x41903[4478]][_x41903[473]][_x41903[124]] == a && (s = r = o = 100,
}) : 15 == i[_x41903[4420]] ? i[_x41903[4478]][_x41903[473]][_x41903[124]] == a && (s = r = o = 10240,
publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4614])),
i[_x41903[4478]][_x41903[473]][_x41903[2345]] = e[_x41903[473]][_x41903[688]],
@ -20820,11 +20820,11 @@
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4619])),
t[_x41903[4555]] = e[_x41903[473]][_x41903[688]],
c();
}, 40) : e + 12 == $(n[_x41903[476]])[_x41903[217]](_x41903[4584]) && publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
}, 10240) : e + 12 == $(n[_x41903[476]])[_x41903[217]](_x41903[4584]) && publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4620])),
t[_x41903[4556]] = e[_x41903[473]][_x41903[688]],
c();
}, 40);
}, 10240);
}), _x41903[4621] == $(n[_x41903[476]])[_x41903[217]](_x41903[4584]) && publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[4584]), function(e) {
250 == e[_x41903[686]] && $[_x41903[2030]][_x41903[4089]](e[_x41903[4587]] || __(_x41903[4622])),
l[_x41903[4458]][_x41903[4623]][_x41903[2345]] = e[_x41903[473]][_x41903[688]],

File diff suppressed because one or more lines are too long

View File

@ -20,16 +20,16 @@
<L>可口可乐CocaC</L> <L><BOLD> x110 </BOLD></L><L> 8100.45</L><BR>
<L>ola经典美味汽水1.2</L><BR>
<L>5L/瓶</L><BR>
<BOLD>6970448170051</BOLD><BR>
<BOLD>6970448170051</BOLD><BR><BR>
<L>排骨约350g默 </L><L><BOLD> 1 </BOLD></L><L> 150.13 </L><BR>
<L>认砍小块)</L><BR>
<BOLD>6970448170053</BOLD><BR>
<BOLD>6970448170053</BOLD><BR><BR>
<L>新鲜虫草花1包约2 </L><L><BOLD> x11 </BOLD></L><L> 4.01 </L><BR>
<L>00g 韭菜1000g 鸡蛋</L><BR>
<L>2003克</L><BR>
<BOLD>6970448170054</BOLD><BR>
<BOLD>6970448170054</BOLD><BR><BR>
<L>冰红茶风味饮料 <L><BOLD> 1 </BOLD></L><L> 13.24 </L></L><BR>
<BOLD>6970448170055</BOLD><BR>
<BOLD>6970448170055</BOLD><BR><BR>
--------------------------------<BR>
商品总件数:<BOLD>3</BOLD><BR>
商品总额:<BOLD>¥18.7</BOLD><BR>
@ -61,4 +61,4 @@
<CB>${store_name}</CB><BR>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR><#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR></#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'-'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'店长'}<BR>
第三版带变量的模版(有预约订单)
<CB>${store_name}</CB><BR><#if is_booking_order><CB><B>预约订单</B></CB><BR></#if>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR><#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR></#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'-'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'店长'}<BR>
<#if is_booking_order><CB><B>预约订单<BR></B></CB></#if><CB>${store_name}</CB><BR>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if><BR></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR><#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR></#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'-'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'店长'}<BR>

24
pom.xml
View File

@ -306,6 +306,7 @@
<spring.profile>local</spring.profile>
<!-- Docker 远程管理地址全局-->
<docker.host>https://114.132.210.208:2375</docker.host>
<docker.registry>10.1.8.3:5000</docker.registry>
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
<!-- nacos配置 -->
<nacos.server.address>114.132.210.208:8848</nacos.server.address>
@ -363,6 +364,7 @@
<spring.profile>dev</spring.profile>
<!-- Docker 远程管理地址全局-->
<docker.host>https://114.132.210.208:2375</docker.host>
<docker.registry>10.1.8.3:5000</docker.registry>
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
<!-- nacos配置 -->
<nacos.server.address>114.132.210.208:8848</nacos.server.address>
@ -414,6 +416,7 @@
<spring.profile>test</spring.profile>
<!-- Docker 远程管理地址全局-->
<docker.host>https://114.132.210.208:2375</docker.host>
<docker.registry>10.1.8.3:5000</docker.registry>
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
<!-- nacos配置 -->
<nacos.server.address>10.1.8.3:8848</nacos.server.address>
@ -465,6 +468,7 @@
<spring.profile>prod</spring.profile>
<!-- Docker 远程管理地址全局-->
<docker.host>https://159.75.249.163:2275</docker.host>
<docker.registry>172.16.0.11:5000</docker.registry>
<docker.ca>/Users/panjunjie/code/docker_registry_ca_prod</docker.ca>
<!-- nacos配置 -->
<nacos.server.address>172.16.0.11:8848</nacos.server.address>
@ -552,20 +556,30 @@
</executions>
<configuration>
<!--定义镜像名称-->
<imageName>mall/${project.artifactId}:${project.version}</imageName>
<!-- <imageName>mall/${project.artifactId}:${project.version}</imageName>-->
<!-- &lt;!&ndash; Docker 远程管理地址&ndash;&gt;-->
<!-- <dockerHost>${docker.host}</dockerHost>-->
<imageName>${docker.registry}/mall/${project.artifactId}:${project.version}</imageName>
<!-- Docker 远程管理地址-->
<dockerHost>${docker.host}</dockerHost>
<!--推送镜像仓库校验安全证书,无安全证书无法推送-->
<dockerCertPath>${docker.ca}</dockerCertPath>
<!-- 打包镜像到 docker registry 中心添加认证配置(账号密码在 maven 配置) -->
<pushImage>true</pushImage>
<!--定义基础镜像-->
<!-- <baseImage>java:8</baseImage>-->
<!-- <baseImage>java:8</baseImage>-->
<baseImage>openjdk:8-jre</baseImage>
<!-- 打包镜像到 docker registry 中心添加认证配置(账号密码在 maven 配置) -->
<serverId>docker-registry</serverId>
<!--定义容器启动命令,注意不能换行-->
<entryPoint>["sh", "-c", "mkdir -p /tmp /app/temp /root/nacos/naming/public &amp;&amp; chmod -R 777 /tmp /app/temp /root/nacos &amp;&amp; java -Djava.io.tmpdir=/app/temp -Dnacos.naming.cache.dir=/root/nacos/naming -jar -Xms256m -Xmx512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseContainerSupport -XX:MaxRAMPercentage=60.0 -XX:+UseSerialGC -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -Dspring.profiles.active=${spring.profile} -Duser.timezone=Asia/Shanghai /${project.build.finalName}.jar"]
</entryPoint>
<!--推送镜像仓库校验安全证书,无安全证书无法推送-->
<dockerCertPath>${docker.ca}</dockerCertPath>
<!-- 添加额外的Dockerfile指令来清理不必要的文件 -->
<runs>
<run>rm -rf /root/.m2 &amp;&amp; rm -rf /tmp/* &amp;&amp; rm -rf /var/cache/*</run>