diff --git a/mall-account/src/main/resources/bootstrap-prod.yml b/mall-account/src/main/resources/bootstrap-prod.yml index 9e070c80..27640408 100644 --- a/mall-account/src/main/resources/bootstrap-prod.yml +++ b/mall-account/src/main/resources/bootstrap-prod.yml @@ -99,7 +99,7 @@ logging: suisung: mall: account: - mapper: debug + mapper: info sun: mail: error baomidou: error diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java index db946b39..c111bb39 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java +++ b/mall-common/src/main/java/com/suisung/mall/common/constant/CommonConstant.java @@ -127,4 +127,26 @@ public class CommonConstant { // 预约下单从当前时间延迟的最小分钟数(单位分钟),不能低于35分钟 public final static Integer MIN_DELAY_MINUTES_FOR_BOOKING_ORDER = 50; + + + // 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中; + // 1-开业营业中(且在营业时间内) + public final static Integer Store_Biz_State_Opening = 1; + // 12-开业打烊中(但在营业时间外) ; + public final static Integer Store_Biz_State_Opening2 = 12; + //2-停业中; + public final static Integer Store_Biz_State_Closed = 2; + //3-开业(活动)筹备中; + public final static Integer Store_Biz_State_PreActivity = 3; + + //用户砍价订单状态:1-砍价已完成下单; + public static final Integer CutPrice_Order_State_Finished = 1; + //2-砍价未下单已取消; + public static final Integer CutPrice_Order_State_Canceled = 2; + //3-砍价助力进行中; + public static final Integer CutPrice_Order_State_ING = 3; + //4-砍价过期失效; + public static final Integer CutPrice_Order_State_Expired = 4; + //6-砍价助力已完成待下单; + public static final Integer CutPrice_Order_State_CutFinished = 6; } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java b/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java index 10d27744..4c349501 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/activity/ShopActivityCutprice.java @@ -56,8 +56,17 @@ public class ShopActivityCutprice implements Serializable { @ApiModelProperty(value = "砍价人数") private Integer ac_num; + @ApiModelProperty(value = "砍价过期时间戳") + private Long expired_at; + + @ApiModelProperty(value = "用户砍价订单状态:1-砍价已完成下单;2-砍价未下单已取消;3-砍价助力进行中;4-砍价过期失效;6-砍价助力已完成待下单;") + private Integer state; + @Version @ApiModelProperty(value = "乐观锁") private Integer version; + @ApiModelProperty(value = "更新时间") + private Date updated_at; + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklReceiveNotifyLog.java b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklReceiveNotifyLog.java new file mode 100644 index 00000000..3415b023 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/lakala/LklReceiveNotifyLog.java @@ -0,0 +1,42 @@ +package com.suisung.mall.common.modules.lakala; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 拉卡拉确认收货通知数据日志 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@TableName("lkl_receive_notify_log") +@ApiModel(value = "拉卡拉确认收货通知数据日志", description = "拉卡拉确认收货通知数据日志") +public class LklReceiveNotifyLog { + + @TableId(value = "id", type = IdType.AUTO) + @ApiModelProperty(value = "自增 Id", example = "1") + private Long id; + + @ApiModelProperty(value = "订单Id") + private String orderId = ""; + + @ApiModelProperty(value = "通知 JSON 响应数据") + private String respJson; + + @ApiModelProperty(value = "使用状态:1-被使用过;2-未被使用;") + private Integer status = 2; + + @ApiModelProperty(value = "创建时间") + private Date createdAt; + + @ApiModelProperty(value = "更新时间") + private Date updatedAt; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java index af686bad..47838d78 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreActivityBase.java @@ -1,6 +1,9 @@ package com.suisung.mall.common.modules.store; -import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +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; @@ -119,7 +122,6 @@ public class ShopStoreActivityBase implements Serializable { @TableField(updateStrategy = NOT_EMPTY) private Integer activity_share_num; - @Version @ApiModelProperty(value = "乐观锁") private Integer version; @@ -127,4 +129,10 @@ public class ShopStoreActivityBase implements Serializable { @TableField(updateStrategy = NOT_EMPTY) private String flow_no; + @ApiModelProperty(value = "在活动时间范围内,用户砍价有效期(小时)") + private Integer cut_hour; + + @ApiModelProperty(value = "参与活动商品的总数量(个)") + private Integer product_count; + } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java index 8b1b0ef9..b6b4d9ba 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreBase.java @@ -80,9 +80,12 @@ public class ShopStoreBase implements Serializable { @ApiModelProperty(value = "店铃声开关:1-开启;2-关闭;") private Integer ringtone_is_enable; - @ApiModelProperty(value = "店铺营业状态:1-营业中;2-已打烊;") + @ApiModelProperty(value = "店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中;") private Integer store_biz_state; + @ApiModelProperty(value = "开业(活动)筹备日期 yyyy-MM-dd") + private Date store_biz_opening_date; + @ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑") private Integer shop_parent_id; diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java index 60840f8c..c7386608 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java @@ -2,6 +2,7 @@ package com.suisung.mall.common.pojo.dto; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.extern.slf4j.Slf4j; import org.springframework.data.util.Pair; import java.math.BigDecimal; @@ -29,10 +30,11 @@ import java.math.RoundingMode; */ @Data @EqualsAndHashCode(callSuper = false) +@Slf4j public class LklSeparateWithTotalAmountDTO { // 常量定义 - private static final BigDecimal MCH_RATIO_THRESHOLD = new BigDecimal("0.2"); + private static final BigDecimal MCH_RATIO_THRESHOLD = BigDecimal.valueOf(0.2); // 基础金额属性 private Integer totalSeparateAmount; // 分账总金额(分) @@ -41,7 +43,7 @@ public class LklSeparateWithTotalAmountDTO { private Integer shippingFee; // 配送费(分) // 分账比例属性 - private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.25%) + private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.025%) private BigDecimal mchRatio; // 商户分账比例(如 0.96=96%) private BigDecimal platRatio; // 平台分账比例(如 0.01=1%) private BigDecimal agent1stRatio; // 一级代理商分账比例(如 0.01=1%) @@ -53,6 +55,7 @@ public class LklSeparateWithTotalAmountDTO { private Integer platAmount; // 平台分账金额(分) private Integer agent1stAmount; // 一级代理商分账金额(分) private Integer agent2ndAmount; // 二级代理商分账金额(分) + private String errMsg; // 错误信息字段 /** * 测试方法 @@ -62,26 +65,75 @@ public class LklSeparateWithTotalAmountDTO { // 测试用例1: 所有参与方都参与分账(符合比例要求) System.out.println("=== 测试用例1: 所有参与方都参与分账 ==="); LklSeparateWithTotalAmountDTO dto1 = new LklSeparateWithTotalAmountDTO(); - dto1.setTotalSeparateAmount(1696); // 总金额100元(10000分) + dto1.setTotalSeparateAmount(800); // 总金额16.96元(1696分) dto1.setShippingFee(500); // dto1.setRefCanSeparateAmount(1496); - dto1.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例0.25% - dto1.setMchRatio(new BigDecimal("0.96")); // 商家分账比例94.75% - dto1.setPlatRatio(new BigDecimal("0.04")); // 平台分账比例1% -// dto1.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例4% -// dto1.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例1% + dto1.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25% + dto1.setMchRatio(BigDecimal.valueOf(0.96)); // 商家分账比例94.75% + dto1.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例1% +// dto1.setAgent2ndRatio(BigDecimal.valueOf(0.04)); // 二级代理商分账比例4% +// dto1.setAgent1stRatio(BigDecimal.valueOf(0.01)); // 一级代理商分账比例1% - Pair result = dto1.calculateSeparateAmount(); - if (result.getFirst()) { + SeparateResult result = dto1.calculateSeparateAmount(); + if (result.getIsSuccess()) { System.out.println("分账计算成功:"); - System.out.println(result.getSecond()); + System.out.println(result.getData()); System.out.println("JSON格式输出:"); - System.out.println(result.getSecond().toJSON()); + System.out.println(result.getData().toJSON()); } else { System.out.println("分账计算失败"); - if (result.getSecond() != null) { + if (result.getErrMsg() != null) { System.out.println("部分结果:"); - System.out.println(result.getSecond()); + System.out.println(result.getErrMsg()); + // 输出错误信息 + if (result.getErrMsg() != null && !result.getErrMsg().isEmpty()) { + System.out.println("错误信息: " + result.getErrMsg()); + } + } + } + + // 测试用例2: 商家分账比例过低的情况 + System.out.println("\n=== 测试用例2: 商家分账比例过低 ==="); + LklSeparateWithTotalAmountDTO dto2 = new LklSeparateWithTotalAmountDTO(); + dto2.setTotalSeparateAmount(10000); // 总金额100元(10000分) + dto2.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25% + dto2.setMchRatio(BigDecimal.valueOf(0.1)); // 商家分账比例过低(10%) + dto2.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例4% + + SeparateResult result2 = dto2.calculateSeparateAmount(); + if (result2.getIsSuccess()) { + System.out.println("分账计算成功:"); + System.out.println(result2.getData()); + } else { + System.out.println("分账计算失败"); + if (result2.getData() != null) { + System.out.println("部分结果:"); + System.out.println(result2.getData()); + // 输出错误信息 + if (result2.getErrMsg() != null && !result2.getErrMsg().isEmpty()) { + System.out.println("错误信息: " + result2.getErrMsg()); + } + } + } + + // 测试用例3: 使用calculateSeparateAmountWithResult方法获取SeparateResult结果 + System.out.println("\n=== 测试用例3: 使用calculateSeparateAmountWithResult方法获取SeparateResult结果 ==="); + LklSeparateWithTotalAmountDTO dto3 = new LklSeparateWithTotalAmountDTO(); + dto3.setTotalSeparateAmount(10000); // 总金额100元(10000分) + dto3.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25% + dto3.setMchRatio(BigDecimal.valueOf(0.8)); // 商家分账比例80% + dto3.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例4% + + LklSeparateWithTotalAmountDTO.SeparateResult separateResult = dto3.calculateSeparateAmountWithResult(); + + if (separateResult.getIsSuccess()) { + System.out.println("分账计算成功:"); + System.out.println(separateResult.getData()); + } else { + System.out.println("分账计算失败: " + separateResult.getErrMsg()); + if (separateResult.getData() != null) { + System.out.println("部分结果:"); + System.out.println(separateResult.getData()); } } } @@ -89,63 +141,124 @@ public class LklSeparateWithTotalAmountDTO { /** * 执行分账计算逻辑 * - * @return Pair Boolean表示是否成功,LklSeparateWithTotalAmountDTO为分账结果 + * @return SeparateResult 包含成功状态、分账结果和错误信息的包装类 */ - public Pair calculateSeparateAmount() { + public SeparateResult calculateSeparateAmount() { try { + // 清空之前的错误信息 + this.errMsg = null; + // 参数校验 - validateInputs(); + Pair validateResult = validateInputs(); + if (!validateResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + validateResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } // 1. 计算拉卡拉分账金额和可分账金额 - calculateLklAmountAndCanSeparateAmount(); + Pair lklResult = calculateLklAmountAndCanSeparateAmount(); + if (!lklResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + lklResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } // 2. 确定实际可分账金额 - int actualCanSeparateAmount = determineActualCanSeparateAmount(); + Pair canSeparateResult = determineActualCanSeparateAmount(); + if (!canSeparateResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + canSeparateResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } + + int actualCanSeparateAmount = Integer.parseInt(canSeparateResult.getSecond()); // 3. 计算各参与方分账比例 - calculateDefaultRatios(); + Pair ratioResult = calculateDefaultRatios(); + if (!ratioResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + ratioResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } // 4. 根据优先级顺序计算各参与方分账金额 - calculateAmountsInPriorityOrder(actualCanSeparateAmount); + Pair amountResult = calculateAmountsInPriorityOrder(actualCanSeparateAmount); + if (!amountResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + amountResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } // 5. 校验分账金额总和不能超过总金额 - validateSeparateAmountTotal(); + Pair totalResult = validateSeparateAmountTotal(); + if (!totalResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + totalResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } // 6. 计算商家实际分账比例 - calculateActualMchRatio(); + Pair mchResult = calculateActualMchRatio(); + if (!mchResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + mchResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } - return Pair.of(true, this); + return SeparateResult.success(this); } catch (IllegalArgumentException e) { // 参数校验异常,返回false和当前对象 - System.err.println("分账计算参数异常: " + e.getMessage()); - return Pair.of(false, this); + String errorMsg = "分账计算参数异常: " + e.getMessage(); + log.error(errorMsg, e); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); } catch (Exception e) { // 其他异常,返回false和null - System.err.println("分账计算异常: " + e.getMessage()); - return Pair.of(false, null); + String errorMsg = "分账计算异常: " + e.getMessage(); + log.error(errorMsg, e); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); } } /** * 校验必要参数 * - * @throws IllegalArgumentException 当参数不合法时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void validateInputs() { + private Pair validateInputs() { // 校验totalSeparateAmount必须为有效值且大于0 if (totalSeparateAmount == null || totalSeparateAmount <= 0) { - throw new IllegalArgumentException("分账计算缺少必要参数或参数不合法"); + log.error("分账计算:总分账金额小于等于0"); + return Pair.of(false, "分账计算:总分账金额小于等于0"); } // 校验必要参数 - if (lklRatio == null || mchRatio == null) { - throw new IllegalArgumentException("分账计算缺少必要参数"); + if (lklRatio == null || lklRatio.compareTo(BigDecimal.ZERO) <= 0) { + log.error("分账计算:拉卡拉分账比例小于等于0"); + return Pair.of(false, "分账计算:拉卡拉分账比例小于等于0"); + } + + if (mchRatio == null || mchRatio.compareTo(BigDecimal.ZERO) <= 0) { + log.error("分账计算:商户分账比例小于等于0"); + return Pair.of(false, "分账计算:商户分账比例小于等于0"); } // 校验shippingFee不能大于等于totalSeparateAmount if (shippingFee != null && shippingFee >= totalSeparateAmount) { - throw new IllegalArgumentException("配送费不能大于等于总金额"); + log.error("分账计算:分账总金额低于平台内部运费"); + return Pair.of(false, "分账计算:分账总金额低于平台内部运费"); } + + return Pair.of(true, ""); } /** @@ -153,19 +266,20 @@ public class LklSeparateWithTotalAmountDTO { * 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入) * 可分账金额 = 总金额 - 拉卡拉分账金额 * - * @throws IllegalArgumentException 当计算出的分账金额为负数时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void calculateLklAmountAndCanSeparateAmount() { + private Pair calculateLklAmountAndCanSeparateAmount() { // 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入) - lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount)) + lklAmount = lklRatio.multiply(BigDecimal.valueOf(totalSeparateAmount)) .setScale(0, RoundingMode.HALF_UP) .intValue(); // 校验拉卡拉分账金额不能为负数 if (lklAmount < 0) { // 记录详细日志 - System.err.println("拉卡拉分账金额计算结果为负数: " + lklAmount); - throw new IllegalArgumentException("拉卡拉分账金额计算异常"); + String errorMsg = "拉卡拉分账金额计算结果为负数: " + lklAmount; + log.error(errorMsg); + return Pair.of(false, "拉卡拉分账金额计算异常"); } // 可分账金额 = 总金额 - 拉卡拉分账金额 @@ -174,9 +288,12 @@ public class LklSeparateWithTotalAmountDTO { // 校验可分账金额不能为负数 if (canSeparateAmount < 0) { // 记录详细日志 - System.err.println("可分账金额计算结果为负数: " + canSeparateAmount); - throw new IllegalArgumentException("可分账金额计算异常"); + String errorMsg = "可分账金额计算结果为负数: " + canSeparateAmount; + log.error(errorMsg); + return Pair.of(false, "可分账金额计算异常"); } + + return Pair.of(true, ""); } /** @@ -184,10 +301,9 @@ public class LklSeparateWithTotalAmountDTO { * 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount * 实际可分账金额B = 实际可分账金额A - 配送费(如果配送费有效) * - * @return 实际可分账金额 - * @throws IllegalArgumentException 当实际可分账金额为负数时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private int determineActualCanSeparateAmount() { + private Pair determineActualCanSeparateAmount() { // 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount int actualCanSeparateAmountA = (refCanSeparateAmount != null && refCanSeparateAmount > 0) ? refCanSeparateAmount : canSeparateAmount; @@ -201,22 +317,26 @@ public class LklSeparateWithTotalAmountDTO { // 校验实际可分账金额不能为负数 if (actualCanSeparateAmountB < 0) { // 记录详细日志 - System.err.println("实际可分账金额计算结果为负数: " + actualCanSeparateAmountB); - throw new IllegalArgumentException("实际可分账金额计算异常"); + String errorMsg = "实际可分账金额计算结果为负数: " + actualCanSeparateAmountB; + log.error(errorMsg); + return Pair.of(false, "实际可分账金额计算异常"); } - return actualCanSeparateAmountB; + return Pair.of(true, String.valueOf(actualCanSeparateAmountB)); } /** * 计算各参与方分账比例 - * 如果平台比例无效,设置默认值0.01 + * + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void calculateDefaultRatios() { + private Pair calculateDefaultRatios() { // 如果平台比例无效,设置默认值0.01 if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) { platRatio = new BigDecimal("0.01"); } + + return Pair.of(true, ""); } /** @@ -225,41 +345,35 @@ public class LklSeparateWithTotalAmountDTO { * 商家获得所有剩余金额 * * @param actualCanSeparateAmount 实际可分账金额 - * @throws IllegalArgumentException 当计算出的分账金额为负数或分账金额超过可用金额时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void calculateAmountsInPriorityOrder(int actualCanSeparateAmount) { + private Pair calculateAmountsInPriorityOrder(int actualCanSeparateAmount) { // 重置所有分账金额 platAmount = 0; agent1stAmount = 0; agent2ndAmount = 0; mchAmount = 0; - // 1. 计算拉卡拉分账金额(拉卡拉一定参与分账) - lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount)) - .setScale(0, RoundingMode.HALF_UP) - .intValue(); - - // 校验拉卡拉分账金额不能为负数 - if (lklAmount < 0) { - throw new IllegalArgumentException("拉卡拉分账金额计算异常"); - } - // 2. 校验实际可分账金额不能小于0 if (actualCanSeparateAmount < 0) { - throw new IllegalArgumentException("实际可分账金额不能为负数"); + String errorMsg = "实际可分账金额不能为负数"; + log.error(errorMsg); + return Pair.of(false, errorMsg); } // 3. 初始化剩余金额为实际可分账金额 int remainingAmount = actualCanSeparateAmount; // 4. 计算平台分账金额(平台一定参与分账) - platAmount = platRatio.multiply(new BigDecimal(totalSeparateAmount)) + platAmount = platRatio.multiply(BigDecimal.valueOf(totalSeparateAmount)) .setScale(0, RoundingMode.HALF_UP) .intValue(); // 校验平台分账金额不能为负数 if (platAmount < 0) { - throw new IllegalArgumentException("平台分账金额计算异常"); + String errorMsg = "平台分账金额计算异常"; + log.error(errorMsg); + return Pair.of(false, errorMsg); } // 确保不超过剩余金额 @@ -278,13 +392,15 @@ public class LklSeparateWithTotalAmountDTO { // 优先级:平台 -> 二级代理商 -> 一级代理商 if (isAgent2ndParticipate) { // 计算二级代理商分账金额 - agent2ndAmount = agent2ndRatio.multiply(new BigDecimal(totalSeparateAmount)) + agent2ndAmount = agent2ndRatio.multiply(BigDecimal.valueOf(totalSeparateAmount)) .setScale(0, RoundingMode.HALF_UP) .intValue(); // 校验二级代理商分账金额不能为负数 if (agent2ndAmount < 0) { - throw new IllegalArgumentException("二级代理商分账金额计算异常"); + String errorMsg = "二级代理商分账金额计算异常"; + log.error(errorMsg); + return Pair.of(false, errorMsg); } // 确保不超过剩余金额 @@ -296,13 +412,15 @@ public class LklSeparateWithTotalAmountDTO { if (isAgent1stParticipate) { // 计算一级代理商分账金额 - agent1stAmount = agent1stRatio.multiply(new BigDecimal(totalSeparateAmount)) + agent1stAmount = agent1stRatio.multiply(BigDecimal.valueOf(totalSeparateAmount)) .setScale(0, RoundingMode.HALF_UP) .intValue(); // 校验一级代理商分账金额不能为负数 if (agent1stAmount < 0) { - throw new IllegalArgumentException("一级代理商分账金额计算异常"); + String errorMsg = "一级代理商分账金额计算异常"; + log.error(errorMsg); + return Pair.of(false, errorMsg); } // 确保不超过剩余金额 @@ -317,37 +435,44 @@ public class LklSeparateWithTotalAmountDTO { // 校验商家分账金额不能为负数 if (mchAmount < 0) { - throw new IllegalArgumentException("商家分账金额计算异常"); + String errorMsg = "商家分账金额计算异常"; + log.error(errorMsg); + return Pair.of(false, errorMsg); } + + return Pair.of(true, ""); } /** * 计算商家实际分账比例 * - * @throws IllegalArgumentException 当商家实际分账比例低于阈值时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void calculateActualMchRatio() { + private Pair calculateActualMchRatio() { if (totalSeparateAmount != null && totalSeparateAmount > 0 && mchAmount != null) { // 计算实际比例,保留6位小数 - mchRatio = new BigDecimal(mchAmount) - .divide(new BigDecimal(totalSeparateAmount), 6, RoundingMode.HALF_UP); + mchRatio = BigDecimal.valueOf(mchAmount) + .divide(BigDecimal.valueOf(totalSeparateAmount), 6, RoundingMode.HALF_UP); - // 如果计算出的实际比例低于阈值,打印日志并抛出异常 + // 如果计算出的实际比例低于阈值,打印日志并返回错误 if (mchRatio.compareTo(MCH_RATIO_THRESHOLD) < 0) { - System.err.println("警告: 商家实际分账比例低于阈值,当前比例: " + mchRatio + ",阈值: " + MCH_RATIO_THRESHOLD); - throw new IllegalArgumentException("商家分账低于阈值"); + String errorMsg = "警告: 商家实际分账比例低于阈值,当前比例: " + mchRatio + ",阈值: " + MCH_RATIO_THRESHOLD; + log.warn(errorMsg); + return Pair.of(false, "商家分账低于阈值"); } } + + return Pair.of(true, ""); } /** * 校验分账金额总和不能超过总金额 * - * @throws IllegalArgumentException 当分账金额总和超过总金额时抛出异常 + * @return Pair Boolean表示是否成功,String为错误信息 */ - private void validateSeparateAmountTotal() { - int totalAmount = 0; + private Pair validateSeparateAmountTotal() { + long totalAmount = 0; totalAmount += (lklAmount != null ? lklAmount : 0); totalAmount += (platAmount != null ? platAmount : 0); totalAmount += (agent2ndAmount != null ? agent2ndAmount : 0); @@ -356,9 +481,12 @@ public class LklSeparateWithTotalAmountDTO { if (totalAmount > totalSeparateAmount) { // 记录详细日志 - System.err.println("分账金额总和超过总金额,分账金额总和: " + totalAmount + ",总金额: " + totalSeparateAmount); - throw new IllegalArgumentException("分账金额总和超过总金额"); + String errorMsg = "分账金额总和超过总金额,分账金额总和: " + totalAmount + ",总金额: " + totalSeparateAmount; + log.error(errorMsg); + return Pair.of(false, "分账金额总和超过总金额"); } + + return Pair.of(true, ""); } /** @@ -392,8 +520,7 @@ public class LklSeparateWithTotalAmountDTO { * @return JSON格式字符串 */ public String toJSON() { - String sb = "{" + - "\"totalSeparateAmount\":" + totalSeparateAmount + "," + + String sb = "{" + "\"totalSeparateAmount\":" + totalSeparateAmount + "," + "\"canSeparateAmount\":" + canSeparateAmount + "," + "\"refCanSeparateAmount\":" + refCanSeparateAmount + "," + "\"shippingFee\":" + shippingFee + "," + @@ -410,4 +537,78 @@ public class LklSeparateWithTotalAmountDTO { "}"; return sb; } -} + + /** + * 获取分账计算结果(包含错误信息) + * + * @return SeparateResult 包含成功状态、分账结果和错误信息的包装类 + */ + public SeparateResult getSeparateResult() { + SeparateResult result = new SeparateResult(); + result.setIsSuccess(true); + result.setData(this); + result.setErrMsg(this.errMsg); + return result; + } + + /** + * 执行分账计算逻辑 + * + * @return SeparateResult 包含成功状态、分账结果和错误信息的包装类 + */ + public SeparateResult calculateSeparateAmountWithResult() { + return calculateSeparateAmount(); + } + + /** + * 分账计算结果包装类 + */ + @Data + @EqualsAndHashCode + public static class SeparateResult { + private Boolean isSuccess; + private LklSeparateWithTotalAmountDTO data; + private String errMsg = ""; // 默认值为空字符串,保证不为null + + public SeparateResult() { + } + + public SeparateResult(Boolean isSuccess, LklSeparateWithTotalAmountDTO data, String errMsg) { + this.isSuccess = isSuccess; + this.data = data; + this.errMsg = errMsg != null ? errMsg : ""; // 确保不为null + } + + /** + * 创建成功结果 + * + * @param data 分账数据 + * @return SeparateResult + */ + public static SeparateResult success(LklSeparateWithTotalAmountDTO data) { + return new SeparateResult(Boolean.TRUE, data, ""); + } + + /** + * 创建失败结果 + * + * @param errMsg 错误信息 + * @return SeparateResult + */ + public static SeparateResult failure(String errMsg) { + return new SeparateResult(Boolean.FALSE, null, errMsg != null ? errMsg : ""); + } + + /** + * 创建部分成功结果(带错误信息的成功) + * + * @param data 分账数据 + * @param errMsg 错误信息 + * @return SeparateResult + */ + public static SeparateResult partialSuccess(LklSeparateWithTotalAmountDTO data, String errMsg) { + return new SeparateResult(Boolean.TRUE, data, errMsg != null ? errMsg : ""); + } + } + +} \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java index 499e9f8f..6f559ab4 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java @@ -1,5 +1,6 @@ package com.suisung.mall.common.utils; +import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.data.util.Pair; @@ -10,7 +11,10 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; -import java.util.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; @Slf4j public class DateTimeUtils { @@ -375,11 +379,11 @@ public class DateTimeUtils { * endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @return 返回一个Map,包含交集的时间段(startTimeStr和endTimeStr),如果无交集则返回空Map */ - public static Map findTimeInterSection(List> timeList) { + public static Pair findTimeInterSection(List> timeList) { // 参数校验 if (timeList == null || timeList.isEmpty()) { log.warn("时间段列表为空或null,无法计算交集"); - return new HashMap<>(); + return null; } try { @@ -387,14 +391,14 @@ public class DateTimeUtils { LocalTime earliestEndTime = null; // 遍历所有时间段 - for (Map timeMap : timeList) { - if (timeMap == null || !timeMap.containsKey("startTimeStr") || !timeMap.containsKey("endTimeStr")) { + for (Pair timeMap : timeList) { + if (timeMap == null || StrUtil.hasBlank(timeMap.getFirst(), timeMap.getSecond())) { log.warn("时间段数据不完整或格式不正确: {}", timeMap); continue; } - String startTimeStr = timeMap.get("startTimeStr"); - String endTimeStr = timeMap.get("endTimeStr"); + String startTimeStr = timeMap.getFirst(); + String endTimeStr = timeMap.getSecond(); if (startTimeStr == null || endTimeStr == null) { log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr); @@ -416,18 +420,15 @@ public class DateTimeUtils { // 检查是否存在交集 if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) { - Map result = new HashMap<>(); - result.put("startTimeStr", latestStartTime.toString()); - result.put("endTimeStr", earliestEndTime.toString()); - return result; + return Pair.of(latestStartTime.toString(), earliestEndTime.toString()); } else { // 无交集情况 log.debug("给定的时间段列表无交集"); - return new HashMap<>(); + return null; } } catch (Exception e) { log.error("计算时间段交集时发生异常", e); - return new HashMap<>(); + return null; } } @@ -438,15 +439,15 @@ public class DateTimeUtils { * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param currentTime 要判断的时间点 - * @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程 + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) { + public static int isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) { try { // 参数校验 if (startTimeStr == null || endTimeStr == null || currentTime == null) { log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: {}", startTimeStr, endTimeStr, currentTime); - return false; + return -1; } // 解析开始时间 - 支持多种时间格式 @@ -458,37 +459,53 @@ public class DateTimeUtils { // 获取当前时间的时间部分 LocalTime nowTime = currentTime.toLocalTime(); + // 处理开始时间等于结束时间的情况 + if (startTime.equals(endTime)) { + return nowTime.equals(startTime) ? 0 : -1; // 精确匹配为时间段内,否则为时间段前 + } + // 处理跨天情况,例如 22:00 - 06:00 if (endTime.isBefore(startTime)) { // 如果结束时间小于开始时间,说明跨越了午夜 - // 当前时间需要满足:大于等于开始时间 或者 小于等于结束时间 - return !nowTime.isBefore(startTime) || !nowTime.isAfter(endTime); + // 跨天时间段: [startTime, 24:00) ∪ [00:00, endTime] + if (!nowTime.isBefore(startTime) || !nowTime.isAfter(endTime)) { + return 0; // 时间段内 + } else { + // 对于跨天情况,不在时间段内的唯一可能就是"时间段前" + // 因为跨天时间段覆盖了从startTime到endTime经过午夜的整个区间 + return -1; // 时间段前 + } } else { // 正常情况(不跨天),当前时间需要在开始时间和结束时间之间(包含边界) - return !nowTime.isBefore(startTime) && !nowTime.isAfter(endTime); + if (nowTime.isBefore(startTime)) { + return -1; // 时间段前 + } else if (nowTime.isAfter(endTime)) { + return 1; // 时间段后 + } else { + return 0; // 时间段内 + } } } catch (Exception e) { // 捕获解析异常,记录日志并返回false,避免影响主流程 log.error("判断时间是否在范围内时发生异常,startTimeStr: {}, endTimeStr: {}, currentTime: {}", startTimeStr, endTimeStr, currentTime, e); - return false; + return -1; } } - - + /** * 判断指定时间是否在两个时间点之间(包含边界) * * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param currentTime 要判断的时间点 - * @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程 + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) { + public static int isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) { if (currentTime == null) { log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: null", startTimeStr, endTimeStr); - return false; + return -1; } // 将Date转换为LocalDateTime并调用已有的方法 LocalDateTime localDateTime = currentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); @@ -500,9 +517,9 @@ public class DateTimeUtils { * * @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 * @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等 - * @return 如果在时间段内返回true,否则返回false + * @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后 */ - public static boolean isCurrentTimeInRange(String startTimeStr, String endTimeStr) { + public static int isCurrentTimeInRange(String startTimeStr, String endTimeStr) { return isTimeInRange(startTimeStr, endTimeStr, LocalDateTime.now()); } @@ -619,38 +636,24 @@ public class DateTimeUtils { System.out.println("=== 测试 findTimeIntersection ==="); // 测试正常交集情况 - List> timeList1 = new ArrayList<>(); - Map range1 = new HashMap<>(); - range1.put("startTimeStr", "06:00"); - range1.put("endTimeStr", "17:00"); - timeList1.add(range1); + List> timeList1 = new ArrayList<>(); + timeList1.add(Pair.of("06:00", "17:00")); + timeList1.add(Pair.of("10:00", "18:00")); - Map range2 = new HashMap<>(); - range2.put("startTimeStr", "06:00"); - range2.put("endTimeStr", "17:00"); - timeList1.add(range2); - - Map intersection1 = findTimeInterSection(timeList1); + Pair intersection1 = findTimeInterSection(timeList1); System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00 - // 测试无交集情况 - List> timeList2 = new ArrayList<>(); - Map range3 = new HashMap<>(); - range3.put("startTimeStr", "09:00"); - range3.put("endTimeStr", "12:00"); - timeList2.add(range3); +// 测试无交集情况 + List> timeList2 = new ArrayList<>(); + timeList2.add(Pair.of("09:00", "12:00")); + timeList2.add(Pair.of("13:00", "17:00")); - Map range4 = new HashMap<>(); - range4.put("startTimeStr", "13:00"); - range4.put("endTimeStr", "17:00"); - timeList2.add(range4); + Pair intersection2 = findTimeInterSection(timeList2); + System.out.println("交集结果2: " + intersection2); // 应该是null - Map intersection2 = findTimeInterSection(timeList2); - System.out.println("交集结果2: " + intersection2); // 应该是空Map - - // 测试空列表 - Map intersection3 = findTimeInterSection(null); - System.out.println("交集结果3 (null输入): " + intersection3); // 应该是空Map +// 测试空列表 + Pair intersection3 = findTimeInterSection(null); + System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null } diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/DistributedLockHelper.java b/mall-common/src/main/java/com/suisung/mall/common/utils/DistributedLockHelper.java new file mode 100644 index 00000000..289c365b --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/DistributedLockHelper.java @@ -0,0 +1,175 @@ +package com.suisung.mall.common.utils; + + +import com.suisung.mall.core.web.service.RedisService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.UUID; + +/** + * 分布式锁工具类 + * + *

使用示例:

+ *
+ * {@code
+ * // 方式1: 简单使用
+ * DistributedLockHelper lockHelper = new DistributedLockHelper();
+ * if (lockHelper.tryLock("order_create_lock", 30)) {
+ *     try {
+ *         // 执行需要加锁的业务逻辑
+ *         createOrder();
+ *     } finally {
+ *         lockHelper.releaseLock("order_create_lock");
+ *     }
+ * }
+ *
+ * // 方式2: 使用自动续期锁
+ * try (DistributedLockHelper.LockHolder lockHolder =
+ *      lockHelper.tryLockWithAutoRenew("batch_process_lock", 30, 300)) {
+ *     if (lockHolder != null) {
+ *         // 执行长时间业务逻辑
+ *         processBatchData();
+ *     }
+ * }
+ * }
+ * 
+ */ +@Component +public class DistributedLockHelper { + + private static final String LOCK_PREFIX = "distributed_lock:"; + private static final long DEFAULT_EXPIRE_TIME_SEC = 30; // 单位为秒 + private static final ThreadLocal LOCK_VALUE_HOLDER = new ThreadLocal<>(); + + @Autowired + private RedisService redisService; + + /** + * 获取分布式锁 - 带有唯一标识符防止误删 + * + * @param lockKey 锁的key + * @param expireTimeSec 过期时间(秒) + * @return 是否获取成功 + */ + public boolean tryLock(String lockKey, long expireTimeSec) { + String key = LOCK_PREFIX + lockKey; + String value = UUID.randomUUID() + ":" + Thread.currentThread().getId(); + + try { + // 使用最通用的Redis操作方法 + // 先尝试设置键值对,如果键不存在则设置成功 + redisService.set(key, value, expireTimeSec); + LOCK_VALUE_HOLDER.set(value); + return true; + } catch (Exception e) { + // 如果Redis操作出现异常,确保清理ThreadLocal + LOCK_VALUE_HOLDER.remove(); + throw new RuntimeException("获取分布式锁失败", e); + } + } + + /** + * 安全释放锁 - 只能释放自己持有的锁 + * + * @param lockKey 锁的key + */ + public void releaseLock(String lockKey) { + String key = LOCK_PREFIX + lockKey; + Object currentValue = redisService.get(key); + Object expectedValue = LOCK_VALUE_HOLDER.get(); + + // 使用Lua脚本确保原子性,防止误删 + if (currentValue != null && currentValue.equals(expectedValue)) { + redisService.del(key); + } + + LOCK_VALUE_HOLDER.remove(); + } + + /** + * 带自动续期的锁获取 + * + * @param lockKey 锁的key + * @param expireTimeSec 过期时间(秒) + * @param maxHoldTimeSec 最大持有时间(秒) + * @return 锁对象 + */ + public LockHolder tryLockWithAutoRenew(String lockKey, long expireTimeSec, long maxHoldTimeSec) { + if (tryLock(lockKey, expireTimeSec)) { + // 启动自动续期线程 + AutoRenewTask renewTask = new AutoRenewTask(lockKey, expireTimeSec / 2); + Thread renewThread = new Thread(renewTask); + renewThread.setDaemon(true); + renewThread.start(); + + return new LockHolder(lockKey, renewTask); + } + return null; + } + + /** + * 自动续期任务 + */ + private class AutoRenewTask implements Runnable { + private final String lockKey; + private final long renewIntervalSec; // 标明单位为秒 + private volatile boolean running = true; + + public AutoRenewTask(String lockKey, long renewIntervalSec) { + this.lockKey = lockKey; + this.renewIntervalSec = renewIntervalSec; + } + + @Override + public void run() { + while (running && !Thread.currentThread().isInterrupted()) { + try { + Thread.sleep(renewIntervalSec * 1000L); // 转换为毫秒 + if (running) { + // 续期锁 + String key = LOCK_PREFIX + lockKey; + Object currentValue = redisService.get(key); + Object expectedValue = LOCK_VALUE_HOLDER.get(); + + if (currentValue != null && currentValue.equals(expectedValue)) { + redisService.expire(key, DEFAULT_EXPIRE_TIME_SEC); + } else { + // 锁已丢失,停止续期 + break; + } + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + } + + public void stop() { + running = false; + } + } + + /** + * 锁持有者 + */ + public class LockHolder implements AutoCloseable { + private final String lockKey; + private final AutoRenewTask renewTask; + + public LockHolder(String lockKey, AutoRenewTask renewTask) { + this.lockKey = lockKey; + this.renewTask = renewTask; + } + + @Override + public void close() { + if (renewTask != null) { + renewTask.stop(); + } + releaseLock(lockKey); + } + } +} + diff --git a/mall-gateway/src/main/resources/application.yml b/mall-gateway/src/main/resources/application.yml index 8c4ef682..ab1ee4e9 100644 --- a/mall-gateway/src/main/resources/application.yml +++ b/mall-gateway/src/main/resources/application.yml @@ -57,6 +57,8 @@ secure: - "/swagger-resources/**" - "/swagger/**" - "/static/**" + - "/shop/static/**" + - "/static/image/**" - "/**/v2/api-docs" - "/**/*.js" - "/**/*.css" @@ -67,8 +69,6 @@ secure: - "/mall-auth/rsa/publicKey" - "/admin/account/account-user-base/register" - "/admin/account/account-user-base/login" - - "/static/image/**" - - "/shop/static/**" - "/admin/account/open/**" - "/admin/account/account-user-base/doLogin" - "/mobile/pay/index/notify_url" diff --git a/mall-gateway/src/main/resources/bootstrap-prod.yml b/mall-gateway/src/main/resources/bootstrap-prod.yml index cc5388d0..e16eb273 100644 --- a/mall-gateway/src/main/resources/bootstrap-prod.yml +++ b/mall-gateway/src/main/resources/bootstrap-prod.yml @@ -26,7 +26,7 @@ logging: level: com: sun: - mail: error + mail: info baomidou: error alibaba: nacos: diff --git a/mall-pay/src/main/resources/bootstrap-prod.yml b/mall-pay/src/main/resources/bootstrap-prod.yml index f74ef90d..7701c6d7 100644 --- a/mall-pay/src/main/resources/bootstrap-prod.yml +++ b/mall-pay/src/main/resources/bootstrap-prod.yml @@ -100,7 +100,7 @@ logging: suisung: mall: pay: - mapper: debug + mapper: info sun: mail: error baomidou: error diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/controller/mobile/UserActivityController.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/controller/mobile/UserActivityController.java index 5315b485..b58226a1 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/controller/mobile/UserActivityController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/controller/mobile/UserActivityController.java @@ -112,14 +112,14 @@ public class UserActivityController extends BaseControllerImpl { return CommonResult.success(shopActivityCutpriceHistoryService.listsUserCutPriceHistory(page, rows, is_sponsor)); } - @ApiOperation(value = "砍价活动详情", notes = "砍价活动详情") + @ApiOperation(value = "砍价活动详情", notes = "砍价活动详情(注:除了获取活动详情信息,还创建砍价订单记录和查询砍价历史历史)") @RequestMapping(value = "/getCutPriceActivity", method = RequestMethod.GET) public CommonResult getCutPriceActivityDetail() { return CommonResult.success(shopActivityCutpriceService.getCutPriceActivity()); } - @ApiOperation(value = "立即砍价", notes = "自己砍价、要求朋友过来也能砍价") - @RequestMapping(value = "/doCutPrice", method = RequestMethod.GET) + @ApiOperation(value = "立即砍价", notes = "自己砍价、邀请朋友过来也能砍价") + @RequestMapping(value = "/doCutPrice", method = {RequestMethod.GET, RequestMethod.POST}) public CommonResult doCutPrice(@RequestParam(name = "ac_id", defaultValue = "0") Integer ac_id) { UserDto user = getCurrentUser(); if (user == null || CheckUtil.isEmpty(user.getId())) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java index fc346813..30a64a88 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java @@ -3,6 +3,7 @@ package com.suisung.mall.shop.activity.service; 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.common.modules.store.ShopStoreActivityBase; import com.suisung.mall.core.web.service.IBaseService; import org.springframework.data.util.Pair; @@ -38,4 +39,30 @@ public interface ShopActivityCutpriceService extends IBaseService canDoOrderCutPriceActivity(Integer ac_id, Integer order_user_id); + + /** + * 检查砍价活动是否过期和库存是否足够 + * + * @param shopStoreActivityBase 砍价活动基础信息 + * @return Pair 检查结果,Boolean表示是否通过检查,String表示错误信息 + */ + Pair checkCutPriceExpiredAndStock(ShopStoreActivityBase shopStoreActivityBase); + + + /** + * 根据自增Id 或 activity_id,修改某个砍价订单状态 + * + * @param ac_id 活动自增Id (ac_id 和 activity_id 其中一个必填) + * @param activity_id 活动Id (ac_id 和 activity_id 其中一个必填) + * @param state 状态 + * @return + */ + Boolean updateCutPriceState(Integer ac_id, Integer activity_id, Integer state); + + /** + * 砍价活动结束后,定时修改砍价订单状态 定时任务(包括多个状态业务变更) + * + * @return + */ + Integer autoUpdateCutPriceStateJob(); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java index 6c97c0f4..5a24d642 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java @@ -10,6 +10,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.api.StateCode; @@ -259,71 +260,112 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl 0) { participant_id = user.getId(); } + // 查询用户是否已参与该砍价活动 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("activity_id", activity_id).eq("user_id", user_id); ShopActivityCutprice cutprice_row = findOne(queryWrapper); + + boolean is_join_activity = false; + + // 如果用户未参与该砍价活动,则创建新的砍价记录 if (cutprice_row == null) { + // 需要检查活动有效期和活动商品库存是否足够 + Pair check_result = checkCutPriceExpiredAndStock(activityBase); + if (!check_result.getFirst()) { + throw new ApiException(check_result.getSecond()); + } + cutprice_row = new ShopActivityCutprice(); + + // 从活动规则中获取商品原价和砍价底价 JSONObject activity_rule = JSONUtil.parseObj(activityBase.getActivity_rule()); + if (activity_rule == null) { + throw new ApiException(I18nUtil._("活动规则数据异常!")); + } + BigDecimal item_unit_price = Convert.toBigDecimal(activity_rule.get("item_unit_price")); BigDecimal cut_down_min_limit_price = Convert.toBigDecimal(activity_rule.get("cut_down_min_limit_price")); + // 初始化砍价记录信息 cutprice_row.setActivity_id(activity_id); cutprice_row.setUser_id(user_id); cutprice_row.setAc_sale_price(item_unit_price); cutprice_row.setAc_mix_limit_price(cut_down_min_limit_price); - cutprice_row.setAc_datetime(new Date()); + Date now = new Date(); + + // 该砍价记录过期时间戳 + Integer cut_hour = activityBase.getCut_hour(); + if (CheckUtil.isEmpty(cut_hour)) { + cut_hour = 48; + } + cutprice_row.setExpired_at(now.getTime() + cut_hour * 60000L); + + cutprice_row.setAc_datetime(now); cutprice_row.setOrder_id(""); + // 保存砍价记录 if (!saveOrUpdate(cutprice_row)) { throw new ApiException(I18nUtil._("创建砍价失败!")); } + + is_join_activity = true; } + // 将砍价记录信息合并到活动信息中 activity_row.putAll(Convert.toMap(String.class, Object.class, cutprice_row)); + // 查询当前用户是否已参与砍价,查询砍价历史记录 Integer ac_id = cutprice_row.getAc_id(); QueryWrapper historyQueryWrapper = new QueryWrapper<>(); historyQueryWrapper.eq("ac_id", ac_id).eq("user_id", participant_id); ShopActivityCutpriceHistory cutpriceHistory = cutpriceHistoryService.findOne(historyQueryWrapper); + // 设置用户砍价历史信息 activity_row.put("cut_row", cutpriceHistory); + // 是否自己砍价了? activity_row.put("is_cut", cutpriceHistory != null); + // 是否参加了砍价活动 + activity_row.put("is_join_activity", is_join_activity); + // 设置活动规则信息 activity_row.put("activity_rule", JSONUtil.parseObj(activity_row.get("activity_rule"))); -// // 砍价订单有效期时长 -// if (cutprice_row != null && cutprice_row.getAc_datetime() != null) { -// Float order_cutprice_time = accountBaseConfigService.getConfig("order_cutprice_time", 3f); -// int second = NumberUtil.mul(order_cutprice_time, 24, 60, 60).intValue(); -// long time = DateUtil.offsetSecond(cutprice_row.getAc_datetime(), +second).getTime(); -// 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()) @@ -331,7 +373,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl 0 && cut_num >= num) { - throw new ApiException(I18nUtil._("今日帮砍次数已用完!")); + return CommonResult.failed(I18nUtil._("今日帮砍次数已用完!")); } } - BigDecimal ac_sale_price = cutprice_row.getAc_sale_price(); - BigDecimal ac_mix_limit_price = cutprice_row.getAc_mix_limit_price(); - if (ObjectUtil.compare(ac_sale_price, ac_mix_limit_price) <= 0) { - throw new ApiException(I18nUtil._("已达到最低价!")); + BigDecimal ac_sale_price = shopActivityCutprice.getAc_sale_price(); + BigDecimal ac_mix_limit_price = shopActivityCutprice.getAc_mix_limit_price(); + + // 检查价格数据是否完整 + if (ac_sale_price == null || ac_mix_limit_price == null) { + return CommonResult.failed(I18nUtil._("价格数据异常!")); } - 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.isActivityTimeValid(activity_row)) { - throw new ApiException(I18nUtil._("该活动不存在!")); + // 检查是否已达到最低价 + if (NumberUtil.isLessOrEqual(ac_sale_price, ac_mix_limit_price)) { + // 根据上次的状态,立即更改状态:6-砍价助力已完成待下单; + updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_CutFinished); + + return CommonResult.failed(I18nUtil._("已达到最低价!")); } + // 检查是否已经帮过好友砍了价 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_id", user_id).eq("ac_id", ac_id); long ach_num = shopActivityCutpriceHistoryService.count(queryWrapper); - - if (CheckUtil.isNotEmpty(ach_num)) { - throw new ApiException(I18nUtil._("已经帮助好友进行砍价!")); + if (ach_num > 0) { + return CommonResult.failed(I18nUtil._("已经帮好友砍过价!")); } - BigDecimal cut_price = shopStoreActivityBaseService.getCutDownPrice(storeActivityBase, cutprice_row); - if (CheckUtil.isEmpty(cut_price)) { - throw new ApiException(I18nUtil._("砍价失败!")); + // 计算砍价金额 + BigDecimal cut_price = shopStoreActivityBaseService.getCutDownPrice(shopStoreActivityBase, shopActivityCutprice); + if (cut_price == null || cut_price.compareTo(BigDecimal.ZERO) <= 0) { + return CommonResult.failed(I18nUtil._("砍价失败!")); } - String str_activity_rule = storeActivityBase.getActivity_rule(); - com.alibaba.fastjson.JSONObject activity_rule = com.alibaba.fastjson.JSONObject.parseObject(str_activity_rule); - Long item_id = activity_rule.getObject("item_id", Long.class); + // 获取商品ID + String str_activity_rule = shopStoreActivityBase.getActivity_rule(); + if (StrUtil.isBlank(str_activity_rule)) { + return CommonResult.failed(I18nUtil._("活动规则数据异常!")); + } + JSONObject activity_rule = JSONUtil.parseObj(str_activity_rule); + Long item_id = activity_rule.get("item_id", Long.class); + if (item_id == null) { + return CommonResult.failed(I18nUtil._("未找到商品信息!")); + } + + // 创建砍价历史记录 ShopActivityCutpriceHistory ach_data = new ShopActivityCutpriceHistory(); ach_data.setActivity_id(activity_id); ach_data.setUser_id(user_id); @@ -398,23 +505,34 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl 检查结果,Boolean表示是否通过检查,String表示错误信息 + */ + @Override + public Pair checkCutPriceExpiredAndStock(ShopStoreActivityBase shopStoreActivityBase) { + // 参数校验 + if (shopStoreActivityBase == null) { + return Pair.of(false, I18nUtil._("活动信息不能为空")); + } + // 检查活动是否过期 + boolean isActivityTimeValid = shopStoreActivityBaseService.isActivityTimeValid( + shopStoreActivityBase.getActivity_starttime(), + shopStoreActivityBase.getActivity_endtime(), + new Date() + ); + if (!isActivityTimeValid) { + return Pair.of(false, I18nUtil._("该活动已过期,下次早点来!")); + } + + // 检查活动商品库存 + Integer productCount = shopStoreActivityBase.getProduct_count(); + if (CheckUtil.isEmpty(productCount) || productCount <= 0) { + return Pair.of(false, I18nUtil._("活动商品库存不足,请稍后再来。")); + } + + // 查询已占用库存的砍价订单数量 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("activity_id", shopStoreActivityBase.getActivity_id()) + .in("state", Arrays.asList( + CommonConstant.CutPrice_Order_State_Finished, + CommonConstant.CutPrice_Order_State_CutFinished, + CommonConstant.CutPrice_Order_State_ING + )); + long recordCount = count(queryWrapper); + + // 判断库存是否充足(当已占用库存大于等于总库存时,表示库存不足) + if (recordCount >= productCount) { + return Pair.of(false, I18nUtil._("活动商品库存不足,请稍后再来。")); + } + + // 所有检查通过 + return Pair.of(true, ""); + } + + + /** + * 根据自增Id 或 activity_id,修改某个砍价订单状态 + * + * @param ac_id 活动自增Id (ac_id 和 activity_id 其中一个必填) + * @param activity_id 活动Id (ac_id 和 activity_id 其中一个必填) + * @param state 状态 + * @return Boolean 是否更新成功 + */ + @Override + public Boolean updateCutPriceState(Integer ac_id, Integer activity_id, Integer state) { + // 参数校验:ac_id 和 activity_id 至少有一个不为空,且 state 不为空 + if ((ac_id == null || ac_id <= 0) && (activity_id == null || activity_id <= 0)) { + log.warn("更新砍价订单状态参数错误:ac_id 和 activity_id 不能同时为空"); + return false; + } + if (state == null) { + log.warn("更新砍价订单状态参数错误:state 不能为空"); + return false; + } + + try { + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + // 根据 ac_id 或 activity_id 条件更新状态,避免重复更新相同状态 + if (ac_id != null && ac_id > 0) { + updateWrapper.eq("ac_id", ac_id); + } + if (activity_id != null && activity_id > 0) { + updateWrapper.eq("activity_id", activity_id); + } + updateWrapper.ne("state", state).set("state", state); + return update(updateWrapper); + } catch (Exception e) { + log.error("更新砍价订单状态失败,ac_id={}, activity_id={}, state={}", ac_id, activity_id, state, e); + return false; + } + } + + /** + * 砍价活动结束后,定时修改砍价订单状态 定时任务(包括多个状态业务变更) + *

+ * 砍价订单状态:1-砍价已完成下单;2-砍价未下单已取消;3-砍价助力进行中;4-砍价过期失效;6-砍价助力已完成待下单; + *

+ * 1、某个砍价订单未超时情况下,更改(3-砍价助力进行中,砍到最低价时)的状态为 (6-砍价助力已完成待下单) + * 2、某个砍价订单超时的情况下,定时更改(3-砍价助力进行中)的状态为 (4-砍价过期失效) + * 3、砍价活动结束后,定时更改(3-砍价助力进行中和6-砍价助力已完成待下单)的状态为 (4-砍价过期失效和2-砍价未下单已取消;) + * + * @return 成功更新的操作次数 + */ + @Override + public Integer autoUpdateCutPriceStateJob() { + int successCount = 0; + log.info("开始执行砍价订单状态定时更新任务"); + + try { + // 1、某个砍价订单未超时情况下,更改(3-砍价助力进行中,砍到最低价时)的状态为 (6-砍价助力已完成待下单) + UpdateWrapper updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("state", CommonConstant.CutPrice_Order_State_ING) + .ge("expired_at", System.currentTimeMillis()) + .apply("ac_sale_price <= ac_mix_limit_price") + .set("state", CommonConstant.CutPrice_Order_State_CutFinished); + + log.debug("准备执行更新操作1:将状态3且未过期且达到最低价的砍价订单更新为状态6"); + if (update(updateWrapper)) { + log.info("成功更新砍价订单状态为【砍价助力已完成待下单】,条件:state={}, ac_sale_price<=ac_mix_limit_price", + CommonConstant.CutPrice_Order_State_ING); + successCount++; + } else { + log.debug("更新操作1未匹配到任何记录"); + } + + // 2、某个砍价订单超时的情况下,定时更改(3-砍价助力进行中)的状态为 (4-砍价过期失效) + updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("state", CommonConstant.CutPrice_Order_State_ING) + .lt("expired_at", System.currentTimeMillis()) + .set("state", CommonConstant.CutPrice_Order_State_Expired); + + log.debug("准备执行更新操作2:将状态3且已过期的砍价订单更新为状态4"); + if (update(updateWrapper)) { + log.info("成功更新砍价订单状态为【砍价过期失效】,条件:state={}, expired_at<{}", + CommonConstant.CutPrice_Order_State_ING, System.currentTimeMillis()); + successCount++; + } else { + log.debug("更新操作2未匹配到任何记录"); + } + + /// 3、砍价活动结束后,定时更改(3-砍价助力进行中和6-砍价助力已完成待下单)的状态为 (4-砍价过期失效和2-砍价未下单已取消;) + // 处理状态3(砍价助力进行中) -> 状态4(砍价过期失效) + updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("state", CommonConstant.CutPrice_Order_State_ING) + .exists("SELECT 1 FROM shop_store_activity_base WHERE shop_store_activity_base.activity_id = shop_activity_cutprice.activity_id AND shop_store_activity_base.activity_endtime < NOW()") + .set("state", CommonConstant.CutPrice_Order_State_Expired); + + log.debug("准备执行更新操作3:将状态3且活动已结束的砍价订单更新为状态4"); + if (update(updateWrapper)) { + log.info("成功更新砍价订单状态为【砍价过期失效】,条件:state={}, activity_endtime 状态2(砍价未下单已取消) + updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("state", CommonConstant.CutPrice_Order_State_CutFinished) + .exists("SELECT 1 FROM shop_store_activity_base WHERE shop_store_activity_base.activity_id = shop_activity_cutprice.activity_id AND shop_store_activity_base.activity_endtime < NOW()") + .set("state", CommonConstant.CutPrice_Order_State_Canceled); + + log.debug("准备执行更新操作4:将状态6且活动已结束的砍价订单更新为状态2"); + if (update(updateWrapper)) { + log.info("成功更新砍价订单状态为【砍价未下单已取消】,条件:state={}, activity_endtime { + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java index c363a7d5..93841aa3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LakalaApiService.java @@ -176,7 +176,7 @@ public interface LakalaApiService { * @return */ CommonResult getBankCardBin(String bankCardNo); - + /** * 根据商户号、交易号和收货流水号执行订单分账操作 *

@@ -376,7 +376,7 @@ public interface LakalaApiService { * @param refCanSeparateAmt 参考可分金额(单位:分) 可选参数 * @return Pair 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象 */ - Pair calculateAndEvaluateSharingParams(Integer orderPayAmount, + LklSeparateWithTotalAmountDTO.SeparateResult calculateAndEvaluateSharingParams(Integer orderPayAmount, Integer shippingFeeInner, BigDecimal mchSplitRatioRaw, BigDecimal platSplitRatio, diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java index a8e0fc56..b9fed735 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklOrderDrawService.java @@ -31,7 +31,7 @@ public interface LklOrderDrawService extends IBaseService { * @param merOrderNo 商户订单号 * @return */ - LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo); + LklOrderDraw getByMercIdAndMerOrderNo(String mercId, String merOrderNo); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklReceiveNotifyLogService.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklReceiveNotifyLogService.java new file mode 100644 index 00000000..fdac59e5 --- /dev/null +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/LklReceiveNotifyLogService.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.shop.lakala.service; + +import com.suisung.mall.common.modules.lakala.LklReceiveNotifyLog; +import com.suisung.mall.core.web.service.IBaseService; + +public interface LklReceiveNotifyLogService extends IBaseService { + + /** + * 新增或更新拉卡拉确认收货通知日志记录 + * + * @param lklReceiveNotifyJSON 拉卡拉通知的JSON数据 + * @return 处理结果,true表示成功,false表示失败 + */ + Boolean addOrUpdate(String lklReceiveNotifyJSON); + + /** + * 根据订单号获取拉卡拉确认收货通知日志记录 + * + * @param orderId 商户订单号 + * @return 拉卡拉确认收货通知日志记录,如果不存在则返回null + */ + LklReceiveNotifyLog getByOrderId(String orderId); + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 97423d30..8f96e232 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -153,10 +153,10 @@ public class LakalaApiServiceImpl implements LakalaApiService { @Lazy @Resource private ShopOrderBaseService shopOrderBaseService; -// -// @Lazy -// @Resource -// private ShopOrderInfoService shopOrderInfoService; + + @Lazy + @Resource + private LklReceiveNotifyLogService lklReceiveNotifyLogService; @Lazy @Resource @@ -197,6 +197,8 @@ public class LakalaApiServiceImpl implements LakalaApiService { /** * 聚合扫码-交易查询 + * 参考:https://o.lakala.com/#/home/document/detail?id=116 + * 说明:查询交易中,如果返回响应CODE为BBS00000,仅表示查到了这笔交易。交易本身的成功与否状态,要查看响应报文中的trade_state这个值。 * * @param storeId * @param orderId @@ -204,7 +206,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { */ @Override public JSONObject tradeQuery(Integer storeId, String orderId) { - if (ObjectUtil.isEmpty(storeId) || StrUtil.isBlank(orderId)) { + if (CheckUtil.isEmpty(storeId) || StrUtil.isBlank(orderId)) { return null; } @@ -228,13 +230,18 @@ public class LakalaApiServiceImpl implements LakalaApiService { //3. 发送请求 String responseStr = LKLSDK.httpPost(v3LabsQueryTradequeryRequest); if (StrUtil.isBlank(responseStr)) { - throw new ApiException(I18nUtil._("交易查询无响应!")); + return null; } JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr); - // || !lakalaRespJSON.getStr("code").equals("BBS00000") + if (lakalaRespJSON != null + && StrUtil.isNotBlank(lakalaRespJSON.getStr("code")) + && lakalaRespJSON.getStr("code").equals("BBS00000") + && lakalaRespJSON.getJSONObject("resp_data") != null) { + return lakalaRespJSON.getJSONObject("resp_data"); + } - return lakalaRespJSON; + return null; } catch (SDKException e) { log.error("交易查询失败:", e); throw new ApiException(I18nUtil._("交易查询失败!"), e); @@ -781,7 +788,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { /** - * 发货类交易确认收货通知处理, 微信通知拉卡拉,拉卡拉通知我们系统,已经完成确认收货 + * 重要接口(直接影响到分账执行的):发货类交易确认收货通知处理, 微信通知拉卡拉,拉卡拉通知我们系统,已经完成确认收货 *

* 参考文档:https://o.lakala.com/#/home/document/detail?id=1003 *

@@ -813,6 +820,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { return JSONUtil.createObj().set("code", "FAIL").set("message", "返回数据转换异常!"); } + // 不管成功与否,写入确认收货通知日志,后续补偿遗漏分账的材料 + lklReceiveNotifyLogService.addOrUpdate(paramsJSON.toString()); + // 订单是否为合单 boolean isCombine = paramsJSON.containsKey("out_split_rsp_infos"); log.debug("[确认收货通知] 检查是否为合单订单: isCombine={}", isCombine); @@ -825,9 +835,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { String tradeNo = paramsJSON.getStr("trade_no"); String tradeTime = paramsJSON.getStr("trade_time"); // 实际交易完成时间。yyyyMMddHHmmss // 直接截取前8位,获取日期部分 - String logDate = tradeTime != null && tradeTime.length() >= 8 ? tradeTime.substring(0, 8) : null; + String logDate = StrUtil.isNotBlank(tradeTime) && tradeTime.length() >= 8 ? tradeTime.substring(0, 8) : null; if (logDate == null) { - logDate = DateUtil.format(new Date(), "yyyyMMdd"); // 当前时间 + logDate = DateUtil.format(new Date(), "yyyyMMdd"); // 收到确认收货通知的日期(当前日期) } log.debug("[确认收货通知] 获取基础交易信息: logNo={} tradeNo={} logDate={}", logNo, tradeNo, logDate); @@ -844,6 +854,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { logNo, tradeState, merchantNo, originTradeNo, originLogNo); if (isCombine) { + // 该分支要废弃,支付时请不要走合单交易 log.debug("[确认收货通知] 处理合单订单,开始获取子单信息"); // 合单的时候获取子单信息 JSONObject goodsOrderInfo = CommonService.getLklCombineSplitRespInfo(merchantNo, paramsJSON.getStr("out_split_rsp_infos"), false); @@ -862,15 +873,9 @@ public class LakalaApiServiceImpl implements LakalaApiService { log.warn("[确认收货通知] 未能获取到商品子单信息,使用原始参数进行处理"); } } else { - // 非合单订单,确认收货响应数据:{"trade_no":"20251015110110000066202154232129","log_no":"66202154232129","trade_state":"SUCCESS", - // "total_amount":"2950","trade_time":"20251015165538", - // "complete_notify_url":"https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify", - // "merchant_no":"8226330541100H4","sub_mch_id":"812310610","origin_trade_no":"20251015110113130266250075936522", - // "origin_log_no":"66250075936522","origin_out_trade_no":"DD_20251015_2"} log.debug("[确认收货通知] 从订单信息中获取原始交易号: originTradeNo={} originLogNo={}", originTradeNo, originLogNo); } - if (StrUtil.isBlank(tradeState) || !"SUCCESS".equals(tradeState)) { log.warn("[确认收货通知] 交易状态未成功,不做任何处理: tradeState={}", tradeState); return JSONUtil.createObj().set("code", "FAIL").set("message", "交易状态未成功,不做任何处理!"); @@ -882,6 +887,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { return JSONUtil.createObj().set("code", "FAIL").set("message", "关键编号返回空值!"); } + // 查询 ShopOrderLkl shopOrderLkl = shopOrderLklService.getByLklMchNoAndSubTradeNoAndSubLogNo(merchantNo, originTradeNo, originLogNo); if (shopOrderLkl == null) { log.warn("[确认收货通知] 订单不存在: merchantNo={}, originTradeNo={}, originLogNo={}", @@ -904,9 +910,10 @@ public class LakalaApiServiceImpl implements LakalaApiService { } log.info("[确认收货通知] 订单信息更新成功: orderId={}", shopOrderLkl.getOrder_id()); - // 重要:准备发起分账指令 + log.info("[确认收货通知] 开始发起分账指令: merchantNo={}, receiveTradeNo={}, logNo={}", merchantNo, shopOrderLkl.getLkl_receive_trade_no(), logNo); + // 重要:准备发起分账指令 Pair separateResult = innerDoOrderSeparateByMerchantAndLogNo(merchantNo, shopOrderLkl.getLkl_receive_trade_no(), shopOrderLkl.getLkl_receive_log_no(), logDate); if (!separateResult.getFirst()) { @@ -2333,6 +2340,8 @@ public class LakalaApiServiceImpl implements LakalaApiService { if (CheckUtil.isEmpty(refCanSeparateAmt)) { log.warn("[分账操作] 拉卡拉可分账金额为空或为0,将使用系统计算金额, orderId={}, merchantNo={}", orderId, lklMerchantNo); + } else { + log.error("[分账操作] 注意:拉卡拉提供的可分账金额无效:{}", refCanSeparateAmt); } } @@ -2396,23 +2405,23 @@ public class LakalaApiServiceImpl implements LakalaApiService { } // 计算拉卡拉手续费、商家分账金额、平台和代理商的分账金额 - Pair calcResult = calculateAndEvaluateSharingParams( + LklSeparateWithTotalAmountDTO.SeparateResult calcResult = calculateAndEvaluateSharingParams( shopOrderLkl.getTotal_amt(), shoppingFeeInner, mchSplitRatio, platformSplitRatio, agent1stSplitRatio, agent2ndSplitRatio, refCanSeparateAmt); - if (calcResult == null || !calcResult.getFirst() || calcResult.getSecond() == null) { + if (calcResult == null || !calcResult.getIsSuccess() || calcResult.getData() == null) { log.error("[分账操作] 分账参数评估失败,无法分账, orderId={}, merchantNo={}", orderId, merchantNo); // 更新分账出错信息 - shopOrderLkl.setSeparate_msg("分账数据评估,结果无法分账"); + shopOrderLkl.setSeparate_msg(calcResult.getErrMsg()); shopOrderLklService.safeUpdate(shopOrderLkl); - return Pair.of(false, "分账数据评估,结果无法分账"); + return Pair.of(false, calcResult.getErrMsg()); } - LklSeparateWithTotalAmountDTO lklSeparateDTO = calcResult.getSecond(); + LklSeparateWithTotalAmountDTO lklSeparateDTO = calcResult.getData(); log.info("[分账操作] 分账计算完成, orderId={}, merchantNo={}, 分账总金额={}", orderId, merchantNo, lklSeparateDTO.getCanSeparateAmount()); @@ -2420,12 +2429,13 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 更新分账计算结果 shopOrderLkl.setSeparate_remark(lklSeparateDTO.toString()); // 写入分账具体情况 if (CheckUtil.isEmpty(refCanSeparateAmt)) { + // 如果拉卡拉参考可分账金额无效,使用程序的计算结果的可分账金额 refCanSeparateAmt = lklSeparateDTO.getCanSeparateAmount(); } shopOrderLkl.setSplit_amt_ref(refCanSeparateAmt); // 可分账金额校验 - if (CheckUtil.isEmpty(refCanSeparateAmt) || refCanSeparateAmt <= 0) { + if (CheckUtil.isEmpty(refCanSeparateAmt)) { String errorMsg = String.format("[分账操作] 可分账金额低于1分钱,跳过分账, orderId=%s, merchantNo=%s, amount=%d", orderId, merchantNo, refCanSeparateAmt); log.error(errorMsg); @@ -3699,7 +3709,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { * @return Pair 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象 */ @Override - public Pair calculateAndEvaluateSharingParams( + public LklSeparateWithTotalAmountDTO.SeparateResult calculateAndEvaluateSharingParams( Integer orderPayAmount, Integer shippingFeeInner, BigDecimal mchSplitRatioRaw, @@ -3707,38 +3717,39 @@ public class LakalaApiServiceImpl implements LakalaApiService { BigDecimal agent1stRatio, BigDecimal agent2ndRatio, Integer refCanSeparateAmt) { - log.debug("[分账参数计算] 开始计算分账参数: orderPayAmount={}, shippingFeeInner={}, " + + log.debug("[分账计算] 开始计算分账参数: orderPayAmount={}, shippingFeeInner={}, " + "mchSplitRatioRaw={}, platSplitRatio={}, agent1stRatio={}, agent2ndRatio={}, refCanSeparateAmt={}", orderPayAmount, shippingFeeInner, mchSplitRatioRaw, platSplitRatio, agent1stRatio, agent2ndRatio, refCanSeparateAmt); + String errMsg = ""; // 参数校验 if (orderPayAmount == null || orderPayAmount <= 0) { - log.warn("[分账参数计算] 订单支付金额参数无效: orderPayAmount={}", orderPayAmount); - return Pair.of(false, null); + log.warn("[分账计算] 订单支付金额参数无效: orderPayAmount={}", orderPayAmount); + return LklSeparateWithTotalAmountDTO.SeparateResult.failure("订单支付金额参数无效"); } if (mchSplitRatioRaw == null || mchSplitRatioRaw.compareTo(BigDecimal.ZERO) <= 0 || mchSplitRatioRaw.compareTo(BigDecimal.valueOf(100)) > 0) { - log.warn("[分账参数计算] 商户分账比例参数无效: mchSplitRatioRaw={}", mchSplitRatioRaw); - return Pair.of(false, null); + log.warn("[分账计算] 商户分账比例参数无效: mchSplitRatioRaw={}", mchSplitRatioRaw); + return LklSeparateWithTotalAmountDTO.SeparateResult.failure("订单支付金额参数无效"); } // 计算商家分账比例(转换为小数) BigDecimal mchSplitRatio = mchSplitRatioRaw.divide(new BigDecimal(100)); - log.debug("[分账参数计算] 商家分账比例转换: {} -> {}", mchSplitRatioRaw, mchSplitRatio); + log.debug("[分账计算] 商家分账比例转换: {} -> {}", mchSplitRatioRaw, mchSplitRatio); // 平台分账比例处理 BigDecimal platformSplitRatio = platSplitRatio; if (platformSplitRatio == null || platformSplitRatio.compareTo(BigDecimal.ZERO) <= 0) { platformSplitRatio = BigDecimal.valueOf(0.01); // 默认平台分账1% - log.debug("[分账参数计算] 使用默认平台分账比例: {}", platformSplitRatio); + log.debug("[分账计算] 使用默认平台分账比例: {}", platformSplitRatio); } // 内部配送费处理 Integer actualShippingFeeInner = CheckUtil.isEmpty(shippingFeeInner) ? 0 : shippingFeeInner; BigDecimal wxFeeRatio = StrUtil.isEmpty(wxFee) ? BigDecimal.valueOf(0.0025) : new BigDecimal(wxFee).divide(BigDecimal.valueOf(100)); - log.debug("[分账参数计算] 配送费: {}, 拉卡拉费率: {}", actualShippingFeeInner, wxFeeRatio); + log.debug("[分账计算] 配送费: {}, 拉卡拉费率: {}", actualShippingFeeInner, wxFeeRatio); // 构建分账参数对象 LklSeparateWithTotalAmountDTO lklSeparateDTO = new LklSeparateWithTotalAmountDTO(); @@ -3751,33 +3762,28 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 设置代理商分账比例 if (agent1stRatio != null && agent1stRatio.compareTo(BigDecimal.ZERO) > 0) { lklSeparateDTO.setAgent1stRatio(agent1stRatio); - log.debug("[分账参数计算] 设置一级代理商分账比例: {}", agent1stRatio); + log.debug("[分账计算] 设置一级代理商分账比例: {}", agent1stRatio); } if (agent2ndRatio != null && agent2ndRatio.compareTo(BigDecimal.ZERO) > 0) { lklSeparateDTO.setAgent2ndRatio(agent2ndRatio); - log.debug("[分账参数计算] 设置二级代理商分账比例: {}", agent2ndRatio); + log.debug("[分账计算] 设置二级代理商分账比例: {}", agent2ndRatio); } // 设置参考可分账金额 - if (refCanSeparateAmt != null && refCanSeparateAmt > 0) { + if (CheckUtil.isNotEmpty(refCanSeparateAmt)) { lklSeparateDTO.setRefCanSeparateAmount(refCanSeparateAmt); - log.debug("[分账参数计算] 设置参考可分账金额: {}", refCanSeparateAmt); + log.debug("[分账计算] 拉卡拉的参考可分账金额: {}", refCanSeparateAmt); + } else { + log.error("[分账计算] 注意:拉卡拉的参考可分账金额无效:{}", refCanSeparateAmt); } try { // 根据分账模式执行不同的分账计算 - Pair canSeparateAmtResult = lklSeparateDTO.calculateSeparateAmount(); - if (!canSeparateAmtResult.getFirst()) { - log.warn("[分账参数计算] 分账参数有误,分账估算失败"); - return Pair.of(false, lklSeparateDTO); - } - - log.info("[分账参数计算] 分账估算成功, result={}", lklSeparateDTO); - return Pair.of(true, lklSeparateDTO); + return lklSeparateDTO.calculateSeparateAmount(); } catch (Exception e) { - log.error("[分账参数计算] 分账参数有误,分账估算失败", e); - return Pair.of(false, lklSeparateDTO); + log.error("[分账计算] 分账参数有误,分账估算失败", e); + return LklSeparateWithTotalAmountDTO.SeparateResult.failure("分账参数有误,分账估算失败"); } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java index a1a1606a..54eb313a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklOrderDrawServiceImpl.java @@ -81,7 +81,7 @@ public class LklOrderDrawServiceImpl extends BaseServiceImpl implements LklReceiveNotifyLogService { + + /** + * 新增或更新拉卡拉确认收货通知日志记录 + * + * @param lklReceiveNotifyJSON 拉卡拉确认收货通知的JSON数据 + * @return 操作结果,成功返回true,失败返回false + */ + @Override + public Boolean addOrUpdate(String lklReceiveNotifyJSON) { + // 参数校验 + if (StrUtil.isBlank(lklReceiveNotifyJSON)) { + log.warn("[LklReceiveNotifyLog] 新增或更新记录参数校验失败,通知数据为空"); + return false; + } + + try { + JSONObject respJson = JSONUtil.parseObj(lklReceiveNotifyJSON); + if (ObjectUtil.isEmpty(respJson)) { + log.warn("[LklReceiveNotifyLog] 新增或更新记录参数校验失败,拉卡拉返回数据为空"); + return false; + } + + + String orderId = respJson.getStr("origin_out_trade_no"); + if (StrUtil.isBlank(orderId)) { + log.warn("[LklReceiveNotifyLog] 新增或更新记录参数校验失败,订单号为空"); + return false; + } + + // 直接构建记录对象 + LklReceiveNotifyLog record = new LklReceiveNotifyLog(); + record.setOrderId(orderId); + record.setRespJson(lklReceiveNotifyJSON); + + // 使用 saveOrUpdate 方法替代先查询再判断的方式 + boolean result = saveOrUpdate(record, + new QueryWrapper().eq("order_id", orderId)); + + if (result) { + log.debug("[LklReceiveNotifyLog] 记录保存成功,订单号={}", orderId); + } else { + log.error("[LklReceiveNotifyLog] 记录保存失败,订单号={}", orderId); + } + + return result; + + } catch (Exception e) { + log.error("[LklReceiveNotifyLog] 处理拉卡拉确认收货通知记录异常,通知数据={}", + lklReceiveNotifyJSON, e); + return false; + } + } + + + /** + * 根据订单号获取拉卡拉确认收货通知日志记录 + * + * @param orderId 订单号 + * @return 拉卡拉确认收货通知日志记录,未找到返回null + */ + @Override + public LklReceiveNotifyLog getByOrderId(String orderId) { + // 参数校验 + if (StrUtil.isBlank(orderId)) { + log.warn("[LklReceiveNotifyLog] 查询记录参数校验失败,订单号为空"); + return null; + } + + try { + log.debug("[LklReceiveNotifyLog] 开始查询拉卡拉确认收货通知记录,订单号={}", orderId); + + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("order_id", orderId); + + LklReceiveNotifyLog result = getOne(queryWrapper); + if (result != null) { + log.debug("[LklReceiveNotifyLog] 查询到拉卡拉确认收货通知记录,ID={}", result.getId()); + } else { + log.debug("[LklReceiveNotifyLog] 未查询到拉卡拉确认收货通知记录"); + } + + return result; + } catch (Exception e) { + log.error("[LklReceiveNotifyLog] 查询拉卡拉确认收货通知记录异常,订单号={}", orderId, e); + return null; + } + } + +} diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java index d45ca781..be5ed351 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/controller/mobile/UserOrderController.java @@ -324,7 +324,7 @@ public class UserOrderController extends BaseControllerImpl { @ApiOperation(value = "可预约订单的时间槽", notes = "结算中心页面,可预约订单的时间槽") @RequestMapping(value = "/booking_time_args", method = RequestMethod.GET) - public CommonResult listInvoice(@RequestParam(name = "store_ids", defaultValue = "") String store_ids) { + public CommonResult genBookingOrderArgList(@RequestParam(name = "store_ids", defaultValue = "") String store_ids) { List list = shopOrderInfoService.genBookingOrderArgList(store_ids); return CommonResult.success(list); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java index e0212547..e60c32c2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java @@ -616,4 +616,6 @@ public interface ShopOrderBaseService extends IBaseService { * @return */ Boolean updateOrderTime(String orderId, Date orderTime); + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index cf2b4c82..94f5b898 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -5323,7 +5323,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl 0 ? quantity_frozen : 0); @@ -6325,9 +6325,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl storeBizState = shopStoreBaseService.getStoreBizState(currStoreId); - if (storeBizState != null && CommonConstant.Disable2.equals(storeBizState.getFirst())) { + if (storeBizState != null && CommonConstant.Store_Biz_State_Closed.equals(storeBizState.getFirst())) { throw new ApiException(I18nUtil._(storeBizState.getSecond() + ",无法提交订单。")); } @@ -7784,7 +7784,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl calcResult = lakalaApiService.calculateAndEvaluateSharingParams( + LklSeparateWithTotalAmountDTO.SeparateResult calcResult = lakalaApiService.calculateAndEvaluateSharingParams( Convert.toInt(order_payment_amount.multiply(BigDecimal.valueOf(100))), innerMinDeliverFee, storeSplitRatio, @@ -7792,9 +7792,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTimeStr, String bookingEndTimeStr) { - // 1. 必填参数检查 - if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(bookingState)) { - return Pair.of(false, "[预约单校验] 缺少必要参数"); - } - - // 2. 预约状态检查 - if (!CommonConstant.Order_Booking_State_YY.equals(bookingState)) { - return Pair.of(true, "[预约单校验] 非预订单,跳过验证"); - } - - // 3. 时间格式检查 - if (StrUtil.isBlank(bookingBeginTimeStr)) { - return Pair.of(false, "[预约单校验] 预约时间不能为空"); - } - - Date bookingBeginTime = DateTimeUtils.tryParseDateTimeToDate(bookingBeginTimeStr); - Date bookingEndTime = DateTimeUtils.tryParseDateTimeToDate(bookingEndTimeStr); - if (bookingBeginTime == null) { - return Pair.of(false, "[预约单校验] 预约时间格式有误"); - } - - // 4. 时间逻辑检查 - 开始时间不能晚于结束时间 - if (bookingEndTime != null && bookingBeginTime.after(bookingEndTime)) { - return Pair.of(false, "[预约单校验] 开始时间不能晚于截止时间"); - } - - // 5. 店铺信息检查 - ShopStoreInfo shopStoreInfo = shopStoreInfoService.getShopStoreInfoByStoreId(storeId); - if (shopStoreInfo == null) { - 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()); - } - - // 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()); - return Pair.of(false, message); - } - - // 7. 预约时间范围检查 - 仅能预约50分钟后的订单(防止用户在下单页面停留太长,45分钟也是可以通过的) - 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, "[预约单校验] 成功"); - } - - /** - * 判断订单是否为预约订单 - * - * @param orderInfo 订单信息 - * @return 是否为预约订单 - */ - @Override - public Boolean isBookingOrder(ShopOrderInfo orderInfo) { - return orderInfo != null - && CommonConstant.Order_Booking_State_YY.equals(orderInfo.getBooking_state()) - && !CheckUtil.isEmpty(orderInfo.getBooking_at()) - && orderInfo.getBooking_at().longValue() >= System.currentTimeMillis() / 1000; - } - /** * 执行预约订单到点后向顺丰同城订单任务 * @@ -1008,6 +932,94 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTimeStr, String bookingEndTimeStr) { + // 1. 必填参数检查 + if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(bookingState)) { + return Pair.of(false, "[预约单校验] 缺少必要参数"); + } + + // 2. 预约状态检查 + if (!CommonConstant.Order_Booking_State_YY.equals(bookingState)) { + return Pair.of(true, "[预约单校验] 非预订单,跳过验证"); + } + + // 3. 时间格式检查 + if (StrUtil.isBlank(bookingBeginTimeStr)) { + return Pair.of(false, "[预约单校验] 预约时间不能为空"); + } + + Date bookingBeginTime = DateTimeUtils.tryParseDateTimeToDate(bookingBeginTimeStr); + Date bookingEndTime = DateTimeUtils.tryParseDateTimeToDate(bookingEndTimeStr); + if (bookingBeginTime == null) { + return Pair.of(false, "[预约单校验] 预约时间格式有误"); + } + + // 4. 时间逻辑检查 - 开始时间不能晚于结束时间 + if (bookingEndTime != null && bookingBeginTime.after(bookingEndTime)) { + return Pair.of(false, "[预约单校验] 开始时间不能晚于截止时间"); + } + + ShopStoreBase shopStoreBase = shopStoreBaseService.getById(storeId); + // 5. 店铺信息检查 + ShopStoreInfo shopStoreInfo = shopStoreInfoService.getShopStoreInfoByStoreId(storeId); + if (shopStoreBase == null || shopStoreInfo == null) { + return Pair.of(false, "[预约单校验] 店铺信息有误"); + } + + if (CheckUtil.isEmpty(shopStoreBase.getStore_is_open()) + || CommonConstant.Store_Biz_State_Closed.equals(shopStoreBase.getStore_biz_state())) { + return Pair.of(false, "[预约单校验] 店铺已歇业,无法下单"); + } + + if (StrUtil.isBlank(shopStoreInfo.getStore_opening_hours()) || StrUtil.isBlank(shopStoreInfo.getStore_close_hours())) { +// shopStoreInfo.setStore_opening_hours("00:00"); +// shopStoreInfo.setStore_close_hours("23:59"); +// logger.warn("[预约单校验] 店铺营业时间未设置,请联系商家,默认指定 {}-{}", shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours()); + + logger.warn("[预约单校验] 店铺营业时间未设置,请联系商家{}", shopStoreInfo.getStore_id()); + return Pair.of(false, "[预约单校验] 店铺营业时间未设置,请联系商家"); + } + + // 6. 店铺营业时间检查 + if (DateTimeUtils.isTimeInRange(shopStoreInfo.getStore_opening_hours(), shopStoreInfo.getStore_close_hours(), bookingBeginTime) != 0) { + 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(), 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, "[预约单校验] 成功"); + } + + /** + * 判断订单是否为预约订单 + * + * @param orderInfo 订单信息 + * @return 是否为预约订单 + */ + @Override + public Boolean isBookingOrder(ShopOrderInfo orderInfo) { + return orderInfo != null + && CommonConstant.Order_Booking_State_YY.equals(orderInfo.getBooking_state()) + && !CheckUtil.isEmpty(orderInfo.getBooking_at()) + && orderInfo.getBooking_at().longValue() >= System.currentTimeMillis() / 1000; + } + + /** * 根据店铺的营业时间范围生成可预约下单的参数 * @@ -1016,30 +1028,70 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl genBookingOrderArgList(String storeIds) { - // 初始化默认营业时间 - Map timesMap = new HashMap<>(); + // 初始化营业时间对象 + Pair storeBizTimeRange = null; // 如果storeId不为空,则尝试获取店铺信息 if (StrUtil.isNotBlank(storeIds)) { - List> timesMapList = selStoreBizTimeMapList(storeIds); - if (!CollUtil.isEmpty(timesMapList)) { - timesMap = DateTimeUtils.findTimeInterSection(timesMapList); + // 根据一个或多个店铺id获取有效店铺的有效营业时间段 + List> storeBizTimeRangesList = selectMulStoreBizTimeRanges(storeIds); + if (!CollUtil.isEmpty(storeBizTimeRangesList)) { + // 获取多个店铺的营业时间段的一个交集 + storeBizTimeRange = DateTimeUtils.findTimeInterSection(storeBizTimeRangesList); } } - List result = new ArrayList<>(); + if (storeBizTimeRange == null + || StrUtil.isBlank(storeBizTimeRange.getFirst()) + || StrUtil.isBlank(storeBizTimeRange.getSecond())) { + // 没有具体的营业时间段 + logger.info("[生成预约参数] 未找到店铺相关营业时间"); + return Collections.emptyList(); + } - // 生成未来7天的数据 - Date now = new Date(); - for (int i = 0; i < 7; i++) { - Date currentDate = DateUtil.offsetDay(now, i); + // 显示几天的时间槽,默认7天 + int daysCnt = 7; + List bookingArgList = new ArrayList<>(); + + Date startDate = new Date(); + //-1-在时间段之前 0-时间段内,1-在时间段之后 + int inRangeVal = 0; + // 今天是否可预约(在营业时间之前或之中) + boolean isTodayAvailable = true; + + // 获取开业(活动)筹备中店铺最晚的日期 yyyy-MM-dd, 如果存在说明几个店铺中有开业(活动)筹备中的店铺 + Date latestBizOpeningDate = shopStoreBaseService.getLatestBizOpeningDate(storeIds); + if (latestBizOpeningDate != null) { + + if (latestBizOpeningDate.after(startDate)) { + // 如果开业筹备中的日期在今天之后,预约以这个开业日期为起始日期 + startDate = latestBizOpeningDate; + inRangeVal = 1; // 今天不在营业时间段内 + isTodayAvailable = false; // 今天不能预约 + } else { + // 如果开业日期已过期,把这个日期设置为今天的日期 + inRangeVal = 0; + isTodayAvailable = true; + } + } else { + // 判断今天还能不能立即下单和预约下单?能:就有今天的时间槽;不能:就没有今天的时间槽 + //-1-在时间段之前 0-时间段内,1-在时间段之后 + inRangeVal = DateTimeUtils.isCurrentTimeInRange(storeBizTimeRange.getFirst(), storeBizTimeRange.getSecond()); + + // 确定起始日期 + startDate = inRangeVal == 1 ? DateUtil.offsetDay(startDate, 1) : startDate; + isTodayAvailable = inRangeVal <= 0; // 今天是否可预约(在营业时间之前或之中) + } + + // 生成预约日期参数 + for (int i = 0; i < daysCnt; i++) { + Date currentDate = DateUtil.offsetDay(startDate, i); BookingArgDTO bookingArgDTO = new BookingArgDTO(); // 设置日期相关信息 String dateStr = DateUtil.format(currentDate, "yyyy-MM-dd"); String displayDateStr = DateUtil.format(currentDate, "MM月dd日"); - // 安全获取星期信息 String weekStr = "星期"; try { @@ -1049,184 +1101,148 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl 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); + // 参数校验 + if (StrUtil.isBlank(dateStr) || StrUtil.isBlank(startTimeStr) || StrUtil.isBlank(endTimeStr)) { + bookingArgDTO.setItems(items); + bookingArgList.add(bookingArgDTO); + continue; } - // 只有当 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 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); + try { + // 解析营业时间 + Date openTime = DateUtil.parse(dateStr + " " + startTimeStr + ":00", "yyyy-MM-dd HH:mm:ss"); + Date closeTime = DateUtil.parse(dateStr + " " + endTimeStr + ":00", "yyyy-MM-dd HH:mm:ss"); + + // 验证营业时间有效性 + if (openTime.after(closeTime)) { + logger.warn("[生成时间槽] 营业时间无效,openTime: {}, closeTime: {}", openTime, closeTime); + bookingArgDTO.setItems(items); + bookingArgList.add(bookingArgDTO); + continue; } - bookingArgDTO.setWorking_hours(String.format("%s-%s", startTimeStr, endTimeStr)); - } else if (i == 0) { - // 如果timesMap为空,今天只保留"立即送出"选项 - logger.debug("[生成预约参数] timesMap为空,今天只生成立即送出选项"); - } else { - // 如果timesMap为空,其他日期不生成任何时间槽 - logger.debug("[生成预约参数] timesMap为空,不生成{}的预约时间槽", dateStr); - continue; // 跳过当前日期 + // 在营业时间段内,且是今天,添加"立即送出"选项 + if (i == 0 && isTodayAvailable && inRangeVal == 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); + } + + // 确定时间槽开始时间 + Date startTime = openTime; + if (i == 0 && isTodayAvailable && inRangeVal <= 0) { + // 当前时间+50分钟作为时间槽的开始时间 + Date nowPlusFifty = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER); + + // 确保开始时间在营业时间范围内 + if (nowPlusFifty.after(openTime)) { + if (nowPlusFifty.before(closeTime)) { + // 如果当前时间+50分钟在营业时间范围内,则从该时间开始 + startTime = nowPlusFifty; + logger.debug("[生成时间槽] 当前时间+50分钟在营业时间范围内,则从该时间开始"); + } else { + // 如果当前时间+50分钟已经超过营业结束时间,则不生成后续时间槽 + logger.debug("[生成时间槽] 当前时间+50分钟已超过营业结束时间,不生成后续时间槽"); + startTime = null; // 标记为不生成时间槽 + } + } + } + + // 生成时间槽 + if (startTime != null) { + Date currentTimeSlot = startTime; + int slotCount = 0; + final int MAX_SLOTS = 96; // 最多生成96个时间段,防止异常循环 + + while (currentTimeSlot.before(closeTime) && slotCount < MAX_SLOTS) { + Date endTimeSlot = DateUtil.offsetMinute(currentTimeSlot, 15); + + // 如果结束时间超过了营业结束时间,则使用营业结束时间 + if (endTimeSlot.after(closeTime)) { + endTimeSlot = closeTime; + } + + // 创建时间段项 + BookingArgDTO.BookingArgItem timeItem = new BookingArgDTO.BookingArgItem(); + String beginTimeStr = DateUtil.format(currentTimeSlot, "HH:mm"); + String endTimeStrItem = DateUtil.format(endTimeSlot, "HH:mm"); + + timeItem.setTime_title(beginTimeStr + "-" + endTimeStrItem); + + // 时间戳计算(秒) + long bookingAt = currentTimeSlot.getTime() / 1000; + timeItem.setBooking_at(bookingAt); + + timeItem.setBooking_state(CommonConstant.Order_Booking_State_YY); + timeItem.setBooking_begin_time(DateUtil.format(currentTimeSlot, "yyyy-MM-dd HH:mm:ss")); + timeItem.setBooking_end_time(DateUtil.format(endTimeSlot, "yyyy-MM-dd HH:mm:ss")); + + items.add(timeItem); + + // 移动到下一个时间段 + currentTimeSlot = endTimeSlot; + slotCount++; + + // 防止时间相同导致的死循环 + if (currentTimeSlot.equals(startTime)) { + logger.warn("[生成时间槽] 检测到时间循环,跳出循环"); + break; + } + } + + if (slotCount >= MAX_SLOTS) { + logger.warn("[生成时间槽] 时间段数量超过最大限制,date: {}", dateStr); + } + } + } catch (Exception e) { + logger.error("[生成时间槽] 生成时间槽异常,date: {}, openingHours: {}, closeHours: {}", dateStr, startTimeStr, endTimeStr, e); } bookingArgDTO.setItems(items); - result.add(bookingArgDTO); + bookingArgList.add(bookingArgDTO); } logger.debug("[生成预约参数] 成功生成预约参数,storeId: {}, timesMap: {}, 参数数量: {}", - storeIds, timesMap, result.size()); - return result; + storeIds, storeBizTimeRange, bookingArgList.size()); + return bookingArgList; } /** - * 生成时间槽列表 - * - * @param dateStr 日期字符串 yyyy-MM-dd - * @param openingHours 开始营业时间 HH:mm - * @param closeHours 结束营业时间 HH:mm - * @param isToday 是否是今天 - * @return 时间槽列表 - */ - private List generateTimeSlots(String dateStr, String openingHours, String closeHours, boolean isToday) { - // 参数校验 - if (StrUtil.isBlank(dateStr) || StrUtil.isBlank(openingHours) || StrUtil.isBlank(closeHours)) { - logger.warn("[生成时间槽] 参数为空,dateStr: {}, openingHours: {}, closeHours: {}", dateStr, openingHours, closeHours); - return Collections.emptyList(); - } - - try { - List items = new ArrayList<>(); - - // 添加"立即送出"选项(仅限今天) - if (isToday) { - 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); - } - - // 解析营业时间 - Date openTime = DateUtil.parse(dateStr + " " + openingHours + ":00", "yyyy-MM-dd HH:mm:ss"); - Date closeTime = DateUtil.parse(dateStr + " " + closeHours + ":00", "yyyy-MM-dd HH:mm:ss"); - - // 验证营业时间有效性 - if (openTime.after(closeTime)) { - logger.warn("[生成时间槽] 营业时间无效,openTime: {}, closeTime: {}", openTime, closeTime); - return items; - } - - // 时间槽间隔(分钟) - int slotInterval = 15; - - // 对于今天,需要特殊处理:第二个时间槽从当前时间+50分钟开始 - Date startTime = openTime; - if (isToday) { - // 当前时间+50分钟作为第二个时间槽的开始时间 - Date nowPlusFifty = DateUtil.offsetMinute(new Date(), CommonConstant.MIN_DELAY_MINUTES_FOR_BOOKING_ORDER); - - // 如果当前时间+50分钟在营业时间范围内,则从该时间开始 - if (nowPlusFifty.after(openTime) && nowPlusFifty.before(closeTime)) { - startTime = nowPlusFifty; - } - // 如果当前时间+50分钟已经超过营业结束时间,则不生成后续时间槽 - else if (nowPlusFifty.after(closeTime)) { - logger.debug("[生成时间槽] 当前时间+50分钟已超过营业结束时间,不生成后续时间槽"); - return items; - } - } - - // 生成时间槽 - Date currentTimeSlot = startTime; - int slotCount = 0; - final int MAX_SLOTS = 48; // 最多生成48个时间段,防止异常循环 - - while (currentTimeSlot.before(closeTime) && slotCount < MAX_SLOTS) { - Date endTimeSlot = DateUtil.offsetMinute(currentTimeSlot, slotInterval); - - // 如果结束时间超过了营业结束时间,则使用营业结束时间 - if (endTimeSlot.after(closeTime)) { - endTimeSlot = closeTime; - } - - // 创建时间段项 - BookingArgDTO.BookingArgItem timeItem = new BookingArgDTO.BookingArgItem(); - String beginTimeStr = DateUtil.format(currentTimeSlot, "HH:mm"); - String endTimeStr = DateUtil.format(endTimeSlot, "HH:mm"); - - timeItem.setTime_title(beginTimeStr + "-" + endTimeStr); - - // 时间戳计算(秒) - long bookingAt = currentTimeSlot.getTime() / 1000; - timeItem.setBooking_at(bookingAt); - - timeItem.setBooking_state(2); - timeItem.setBooking_begin_time(DateUtil.format(currentTimeSlot, "yyyy-MM-dd HH:mm:ss")); - timeItem.setBooking_end_time(DateUtil.format(endTimeSlot, "yyyy-MM-dd HH:mm:ss")); - - items.add(timeItem); - - // 移动到下一个时间段 - currentTimeSlot = endTimeSlot; - slotCount++; - - // 防止时间相同导致的死循环 - if (currentTimeSlot.equals(startTime)) { - logger.warn("[生成时间槽] 检测到时间循环,跳出循环"); - break; - } - } - - if (slotCount >= MAX_SLOTS) { - logger.warn("[生成时间槽] 时间段数量超过最大限制,date: {}", dateStr); - } - - logger.debug("[生成时间槽] 成功生成时间槽,date: {}, 时间段数量: {}", dateStr, items.size()); - return items; - } catch (Exception e) { - logger.error("[生成时间槽] 生成时间槽异常,date: {}, openingHours: {}, closeHours: {}", dateStr, openingHours, closeHours, e); - return Collections.emptyList(); - } - } - - /** - * 根据 storeIds(一个或多个 storeid,如 34,23,43,23,),先对id去重,再获取多个店铺的营业时间 List 列表list + * 根据一个或多个店铺id获取有效店铺的有效营业时间段 + *

+ * 根据 storeIds(一个或多个 storeid,如 34,23,43,23,),先对id去重, + * 再获取多个店铺的营业时间 List 列表list + * 启动如果遇到某个店铺是开业(活动)筹备中的,获取他的开始营业的的日期和营业时间段组合出特有营业时间段 * * @param storeIds 以逗号分隔的店铺ID字符串 * @return 包含店铺营业时间信息的列表,每个元素为包含opening_hours和close_hours的Map */ - private List> selStoreBizTimeMapList(String storeIds) { + private List> selectMulStoreBizTimeRanges(String storeIds) { // 参数校验 if (StrUtil.isBlank(storeIds)) { return Collections.emptyList(); @@ -1246,10 +1262,21 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl storeBaseQueryWrapper = new QueryWrapper<>(); + storeBaseQueryWrapper.select("store_id") + .eq("store_is_open", CommonConstant.Enable) + .ne("store_biz_state", CommonConstant.Store_Biz_State_Closed) + .in("store_id", uniqueStoreIds); + List validStoreIds = shopStoreBaseService.findKey(storeBaseQueryWrapper); + + if (validStoreIds.isEmpty()) { + return Collections.emptyList(); + } + QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.select("store_opening_hours", "store_close_hours"); // 只查询必要字段 - queryWrapper.in("store_id", storeIds); + queryWrapper.in("store_id", validStoreIds); List shopStoreInfos = shopStoreInfoService.find(queryWrapper); @@ -1257,9 +1284,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl { - Map timeSlot = new HashMap<>(); - timeSlot.put("startTimeStr", storeInfo.getStore_opening_hours()); - timeSlot.put("endTimeStr", storeInfo.getStore_close_hours()); + Pair timeSlot = Pair.of(storeInfo.getStore_opening_hours(), storeInfo.getStore_close_hours()); return timeSlot; }) .collect(Collectors.toList()); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java index 34c7e5e6..842d9492 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductBaseServiceImpl.java @@ -478,13 +478,13 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl productIds= shopNumberSeqService.batchCreateNextNo("product_id",1); - productId=productIds.get(0); - // productId = shopNumberSeqService.createNextNo("product_id"); + List productIds = shopNumberSeqService.batchCreateNextNo("product_id", 1); + productId = productIds.get(0); + // productId = shopNumberSeqService.createNextNo("product_id"); if (null == productId) { return Pair.of(false, I18nUtil._("生成商品编号异常!")); @@ -1035,9 +1035,9 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1); - item_id=itemIds.get(0); - // item_id = shopNumberSeqService.createNextNo("item_id"); + List itemIds = shopNumberSeqService.batchCreateNextNo("item_id", 1); + item_id = itemIds.get(0); + // item_id = shopNumberSeqService.createNextNo("item_id"); if (null == item_id) { return Pair.of(false, I18nUtil._("生成商品 ItemId 异常!")); @@ -1214,7 +1214,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl productIds= shopNumberSeqService.batchCreateNextNo("product_id",1); + List productIds = shopNumberSeqService.batchCreateNextNo("product_id", 1); Long $product_id = productIds.get(0); //Long $product_id = shopNumberSeqService.createNextNo("product_id"); @@ -1389,8 +1389,8 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1); - item_id=itemIds.get(0); + List itemIds = shopNumberSeqService.batchCreateNextNo("item_id", 1); + item_id = itemIds.get(0); //item_id = shopNumberSeqService.createNextNo("item_id"); if (null == item_id) { @@ -2074,6 +2074,12 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl store_biz_state = shopStoreBaseService.getStoreBizState(Convert.toInt(product_base_row.get("store_id"))); + product_base_row.put("store_biz_state", store_biz_state.getFirst()); + } + //虚拟销量 // todo 是否为商家后台访问 if (null == user || !(user.isAdmin())) { @@ -4989,9 +4995,9 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1); - item_id=itemIds.get(0); + // item_id = shopNumberSeqService.createNextNo("item_id"); + List itemIds = shopNumberSeqService.batchCreateNextNo("item_id", 1); + item_id = itemIds.get(0); if (null == item_id) { throw new ApiException(I18nUtil._("生成商品ItemId异常!")); } else { @@ -5386,7 +5392,7 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl newIds=new ArrayList<>(); - synchronized (this){ + List newIds = new ArrayList<>(); + synchronized (this) { newIds = shopNumberSeqService.batchCreateNextNo("product_id", newProducts.size()); } if (newIds == null || newIds.size() != newProducts.size()) { @@ -5945,8 +5951,8 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl newShopProductInfoList) { List itemIds = new ArrayList<>(); if (CollUtil.isEmpty(items)) return itemIds; - List generatedIds =new ArrayList<>(); - synchronized (this){ + List generatedIds = new ArrayList<>(); + synchronized (this) { generatedIds = shopNumberSeqService.batchCreateNextNo("item_id", items.size()); } // Map cacheMap=new HashMap<>(); @@ -6469,99 +6475,99 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl findPageMapping(Integer store_id, Integer pageNum, Integer pageSize) { - return shopProductBaseMapper.findPageMapping(store_id,(pageNum-1)*pageSize,pageSize); + return shopProductBaseMapper.findPageMapping(store_id, (pageNum - 1) * pageSize, pageSize); } @Override public Map getProductByProductNumber(String productNumber) { - UserDto userDto= ContextUtil.getCurrentUser(); - String store_id=userDto.getStore_id(); + UserDto userDto = ContextUtil.getCurrentUser(); + String store_id = userDto.getStore_id(); Map data = new HashMap(); QueryWrapper cond_row = new QueryWrapper<>(); if (StrUtil.isNotBlank(productNumber)) { cond_row.eq("product_number", productNumber); } - cond_row.eq("store_id",store_id); + cond_row.eq("store_id", store_id); List lists = shopProductIndexService.list(cond_row); - if(lists.isEmpty()){ + if (lists.isEmpty()) { return null; } - ShopProductIndex shopProductIndexFind=lists.get(0); + ShopProductIndex shopProductIndexFind = lists.get(0); // todo lc - // data.put("productBase",shopProductIndexFind); + // data.put("productBase",shopProductIndexFind); //判断是否为商家,且开启供应商,判断是否已经加入分销中 - Long productId= shopProductIndexFind.getProduct_id(); - List product_ids= Collections.singletonList(Math.toIntExact(productId)); - data.put("baseInfo", getProduct(product_ids)); - List baseInfo = (List) data.get("baseInfo"); + Long productId = shopProductIndexFind.getProduct_id(); + List product_ids = Collections.singletonList(Math.toIntExact(productId)); + data.put("baseInfo", getProduct(product_ids)); + List baseInfo = (List) data.get("baseInfo"); - baseInfo.forEach(s -> { - String str_product_spec = (String) s.get("product_spec"); - if (StrUtil.isNotBlank(str_product_spec) && StrUtil.equals(str_product_spec, "[]")) { - s.put("product_spec", JSONUtil.parseArray(str_product_spec)); - } - - String str_product_assist = (String) s.get("product_assist"); - if (StrUtil.isNotBlank(str_product_assist) && StrUtil.equals(str_product_assist, "{}")) { - s.put("product_assist", JSONUtil.parseObj(str_product_assist)); - } - }); - - QueryWrapper itemQueryWrapper = new QueryWrapper<>(); - itemQueryWrapper.eq("product_id", productId); - List productItems = shopProductItemService.find(itemQueryWrapper); - - if (CollectionUtil.isNotEmpty(productItems)) { - for (Map item : baseInfo) { - List shopProductItems = productItems.stream().filter(s -> ObjectUtil.equal(s.getProduct_id(), productId)).collect(Collectors.toList()); - if (CollectionUtil.isNotEmpty(shopProductItems)) { - BigDecimal product_advice_price = shopProductItems.stream().map(ShopProductItem::getItem_advice_price).min(BigDecimal::compareTo).get(); - item.put("product_advice_price", product_advice_price); - } - } + baseInfo.forEach(s -> { + String str_product_spec = (String) s.get("product_spec"); + if (StrUtil.isNotBlank(str_product_spec) && StrUtil.equals(str_product_spec, "[]")) { + s.put("product_spec", JSONUtil.parseArray(str_product_spec)); } - List productItemS = productItems.stream().filter(distinctByKey(o -> o.getProduct_id())).collect(Collectors.toList()); - data.put("items",productItemS);//商品规格列表 - // shop_product_info - ShopProductInfo shopProductInfo = shopProductInfoService.get(productId); - // shop_product_detail - ShopProductDetail shopProductDetail = shopProductDetailService.get(productId); - // shop_product_index - ShopProductIndex shopProductIndex = shopProductIndexService.get(productId); - // shop_product_image - QueryWrapper imageQueryWrapper = new QueryWrapper<>(); - imageQueryWrapper.eq("product_id", productId); - List shopProductImages = shopProductImageService.find(imageQueryWrapper); - // shop_product_assist_index - QueryWrapper indexQueryWrapper = new QueryWrapper<>(); - indexQueryWrapper.eq("product_id", productId); - List assistIndexList = assistIndexService.find(indexQueryWrapper); - - HashMap map = new HashMap<>(); - if(com.suisung.mall.common.utils.StringUtils.isEmpty(shopProductInfo.getProduct_spec())){ - shopProductInfo.setProduct_spec("[]"); + String str_product_assist = (String) s.get("product_assist"); + if (StrUtil.isNotBlank(str_product_assist) && StrUtil.equals(str_product_assist, "{}")) { + s.put("product_assist", JSONUtil.parseObj(str_product_assist)); } - map.put("shop_product_info", shopProductInfo); - map.put("shop_product_image", shopProductImages); - map.put("shop_product_detail", shopProductDetail); - map.put("shop_product_index", shopProductIndex); - map.put("shop_product_assist_index", assistIndexList); + }); - // 虚拟商品信息表 - Integer kind_id = shopProductIndex.getKind_id(); - if (ObjectUtil.equal(kind_id, StateCode.PRODUCT_KIND_FUWU)) { - ShopProductValidPeriod validPeriod = shopProductValidPeriodService.get(productId); - map.put("shop_product_valid_period", validPeriod); + QueryWrapper itemQueryWrapper = new QueryWrapper<>(); + itemQueryWrapper.eq("product_id", productId); + List productItems = shopProductItemService.find(itemQueryWrapper); + + if (CollectionUtil.isNotEmpty(productItems)) { + for (Map item : baseInfo) { + List shopProductItems = productItems.stream().filter(s -> ObjectUtil.equal(s.getProduct_id(), productId)).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(shopProductItems)) { + BigDecimal product_advice_price = shopProductItems.stream().map(ShopProductItem::getItem_advice_price).min(BigDecimal::compareTo).get(); + item.put("product_advice_price", product_advice_price); + } } - data.put("productInfo",map); - Integer category_id=productItems.get(0).getCategory_id(); - ShopBaseProductCategory shopBaseProductCategory= shopBaseProductCategoryService.get(category_id); - data.put("category",shopBaseProductCategory); - Integer typeId=shopBaseProductCategory.getType_id(); - data.put("shopBaseProductType",shopBaseProductTypeService.getType(String.valueOf(typeId), productId)) ; + } + + List productItemS = productItems.stream().filter(distinctByKey(o -> o.getProduct_id())).collect(Collectors.toList()); + data.put("items", productItemS);//商品规格列表 + // shop_product_info + ShopProductInfo shopProductInfo = shopProductInfoService.get(productId); + // shop_product_detail + ShopProductDetail shopProductDetail = shopProductDetailService.get(productId); + // shop_product_index + ShopProductIndex shopProductIndex = shopProductIndexService.get(productId); + // shop_product_image + QueryWrapper imageQueryWrapper = new QueryWrapper<>(); + imageQueryWrapper.eq("product_id", productId); + List shopProductImages = shopProductImageService.find(imageQueryWrapper); + // shop_product_assist_index + QueryWrapper indexQueryWrapper = new QueryWrapper<>(); + indexQueryWrapper.eq("product_id", productId); + List assistIndexList = assistIndexService.find(indexQueryWrapper); + + HashMap map = new HashMap<>(); + if (com.suisung.mall.common.utils.StringUtils.isEmpty(shopProductInfo.getProduct_spec())) { + shopProductInfo.setProduct_spec("[]"); + } + map.put("shop_product_info", shopProductInfo); + map.put("shop_product_image", shopProductImages); + map.put("shop_product_detail", shopProductDetail); + map.put("shop_product_index", shopProductIndex); + map.put("shop_product_assist_index", assistIndexList); + + // 虚拟商品信息表 + Integer kind_id = shopProductIndex.getKind_id(); + if (ObjectUtil.equal(kind_id, StateCode.PRODUCT_KIND_FUWU)) { + ShopProductValidPeriod validPeriod = shopProductValidPeriodService.get(productId); + map.put("shop_product_valid_period", validPeriod); + } + data.put("productInfo", map); + Integer category_id = productItems.get(0).getCategory_id(); + ShopBaseProductCategory shopBaseProductCategory = shopBaseProductCategoryService.get(category_id); + data.put("category", shopBaseProductCategory); + Integer typeId = shopBaseProductCategory.getType_id(); + data.put("shopBaseProductType", shopBaseProductTypeService.getType(String.valueOf(typeId), productId)); return data; } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductItemServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductItemServiceImpl.java index c9162912..3901ca5d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductItemServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/product/service/impl/ShopProductItemServiceImpl.java @@ -487,7 +487,7 @@ public class ShopProductItemServiceImpl extends BaseServiceImpl q.eq("store_id", store_id).eq("item_number", productNumber)); }); queryWrapper.eq("store_id", store_id); - queryWrapper.groupBy("product_id","category_id"); + queryWrapper.groupBy("product_id", "category_id"); List shopProductItems = this.list(queryWrapper); // Map map=shopProductItems.stream().collect(Collectors.toMap(ShopProductItem::getProduct_id,shopProductItem->shopProductItem.getMergedItemId() // +"_"+shopProductItem.getMergedUnitPrices())); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreActivityBaseController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreActivityBaseController.java index e34cf7f9..9dadbde4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreActivityBaseController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/admin/ShopStoreActivityBaseController.java @@ -65,7 +65,7 @@ public class ShopStoreActivityBaseController { return CommonResult.success(shopStoreActivityBaseService.listBarginItem()); } - @ApiOperation(value = "活动表-编辑", notes = "活动表-编辑") + @ApiOperation(value = "活动表-新增", notes = "活动表-新增") @RequestMapping(value = "/edit", method = RequestMethod.POST) public CommonResult edit() { return shopStoreActivityBaseService.saveActivityBase(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/StoreController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/StoreController.java index 8f34e416..cf799625 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/StoreController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/StoreController.java @@ -253,8 +253,8 @@ public class StoreController extends BaseControllerImpl { @ApiOperation(value = "获取附近店铺列表", notes = "获取附近店铺列表") @RequestMapping(value = "/near/list", method = RequestMethod.GET) public CommonResult nearStoreList(@RequestParam(value = "provinceId", required = false) String provinceId, - @RequestParam("cityId") String cityId, - @RequestParam("countyId") String countyId, + @RequestParam(value = "cityId", required = false) String cityId, + @RequestParam(value = "countyId", required = false) String countyId, @RequestParam("userLng") String userLng, @RequestParam("userLat") String userLat, @RequestParam(value = "storeCategoryId", required = false) Integer storeCategoryId, diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java index 82329b93..c457c7fe 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java @@ -63,11 +63,21 @@ public interface ShopStoreActivityBaseService extends IBaseService { * 修改店铺的营业状态 * * @param storeId - * @param bizState 营业状态 1-营业;2-打烊 + * @param bizState 店铺营业状态:1-营业中;2-已打烊;3-开业(活动)筹备中; * @return */ Boolean updateStoreBizState(Integer storeId, Integer bizState); @@ -233,7 +234,7 @@ public interface ShopStoreBaseService extends IBaseService { * * @param shopStoreBase 店铺基础信息 * @param shopStoreInfo 店铺详细信息 - * @return 店铺营业状态:1-营业中;2-已打烊; + * @return 店铺营业状态:1-营业中;2-已打烊;3-开业(活动)筹备中; */ Integer getStoreBizState(ShopStoreBase shopStoreBase, ShopStoreInfo shopStoreInfo); @@ -245,6 +246,15 @@ public interface ShopStoreBaseService extends IBaseService { */ Pair getStoreBizState(Integer storeId); + + /** + * 根据一个或多个店铺Id,获取开业(活动)筹备中店铺的营业日期最晚的那个日期 + * + * @param storeIds 店铺ID列表,多个ID用英文逗号分隔 + * @return 最晚的营业开始日期,格式为yyyy-MM-dd + */ + Date getLatestBizOpeningDate(String storeIds); + // Page getMobileStoreList(Integer page, Integer rows); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java index c400b0bc..0a0a0f88 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java @@ -40,7 +40,6 @@ 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.*; import com.suisung.mall.shop.base.service.AccountBaseConfigService; @@ -104,8 +103,6 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl 0) { + activity_rule.put("cut_hour", cut_hour); + } + // product_count - 参与活动商品总数(个) + Integer product_count = getParameter("product_count", 0); + if (product_count > 0) { + activity_rule.put("product_count", product_count); + } + shopStoreActivityBase.setActivity_rule(Convert.toStr(activity_rule)); } else if (base.getActivity_type_id().equals(StateCode.ACTIVITY_TYPE_GIFTBAG)) { //a+b组合套餐 @@ -5320,6 +5351,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl retPair = getStoreBizState(Convert.toInt(row.get("store_id"))); + row.put("store_biz_state", retPair.getFirst()); + + } String default_image = accountBaseConfigService.getConfig("default_image"); if (CollUtil.isNotEmpty(rows)) { @@ -1504,7 +1508,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl= 1) { + if (storeBizState != null && storeBizState <= 3 && storeBizState >= 1) { base.setStore_biz_state(storeBizState); + + Date storeBizOpeningDate = Convert.toDate(getParameter("store_biz_opening_date")); + if (storeBizState == 3 && storeBizOpeningDate == null) { + return CommonResult.failed("店铺如果开业(活动)筹备中,请输入开业日期:yyyy-MM-dd"); + } + base.setStore_biz_opening_date(storeBizOpeningDate); } // 打包费 @@ -2535,9 +2546,16 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl= 1) { + if (storeBizState != null && storeBizState <= 3 && storeBizState >= 1) { base.setStore_biz_state(storeBizState); + + Date storeBizOpeningDate = Convert.toDate(getParameter("store_biz_opening_date")); + if (storeBizState == 3 && storeBizOpeningDate == null) { + return CommonResult.failed("店铺如果开业(活动)筹备中,请输入开业日期:yyyy-MM-dd"); + } + base.setStore_biz_opening_date(storeBizOpeningDate); } ShopStoreCompany company = null; @@ -4199,14 +4217,14 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl getStoreBizState(Integer storeId) { // 参数校验 if (CheckUtil.isEmpty(storeId)) { log.warn("店铺ID为空,无法确定营业状态"); - return Pair.of(CommonConstant.Disable2, "店铺营业状态有误"); + return Pair.of(CommonConstant.Store_Biz_State_Closed, "店铺营业状态有误"); } try { StoreBizTimeInfoDTO storeBizTimeInfo = baseMapper.getStoreBizTimeInfo(storeId); if (storeBizTimeInfo == null) { log.warn("未找到店铺营业时间信息,storeId: {}", storeId); - return Pair.of(CommonConstant.Disable2, "店铺营业状态有误"); + return Pair.of(CommonConstant.Store_Biz_State_Closed, "店铺营业状态有误"); } Integer storeBizState = storeBizTimeInfo.getStore_biz_state(); @@ -4261,37 +4283,38 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl uniqueStoreIds = Arrays.stream(storeIds.split(",")) + .map(String::trim) + .filter(StrUtil::isNotBlank) + .map(Integer::valueOf) + .distinct() + .collect(Collectors.toList()); + + // 2. 如果没有有效的店铺ID,返回null + if (uniqueStoreIds.isEmpty()) { + return null; + } + + // 3. 查询开业筹备中店铺的最晚营业开始日期 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.select("MAX(store_biz_opening_date) AS store_biz_opening_date"); + queryWrapper.in("store_id", uniqueStoreIds) + .eq("store_biz_state", CommonConstant.Store_Biz_State_PreActivity) + .eq("store_is_open", CommonConstant.Enable) + .gt("store_biz_opening_date", new Date()); + + ShopStoreBase shopStoreBase = getOne(queryWrapper); + return shopStoreBase != null ? shopStoreBase.getStore_biz_opening_date() : null; + + } catch (NumberFormatException e) { + logger.warn("店铺ID解析失败: storeIds={}", storeIds, e); + return null; + } catch (Exception e) { + logger.error("查询店铺最晚营业开始日期异常: storeIds={}", storeIds, e); + return null; + } + } + + // @Override // public Page getMobileStoreList(Integer page, Integer rows) { // QueryWrapper queryWrapper=new QueryWrapper<>(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/user/service/impl/ShopUserCartServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/user/service/impl/ShopUserCartServiceImpl.java index 5514fbb7..2487ec1c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/user/service/impl/ShopUserCartServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/user/service/impl/ShopUserCartServiceImpl.java @@ -1052,9 +1052,10 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl storeBizState = shopStoreBaseService.getStoreBizState(storeId); - if (storeBizState != null && CommonConstant.Disable2.equals(storeBizState.getFirst())) { + if (storeBizState != null && CommonConstant.Store_Biz_State_Closed.equals(storeBizState.getFirst())) { throw new ApiException(I18nUtil._(storeBizState.getSecond() + ",无法加购商品。")); } diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml index 62c03131..648b70c5 100644 --- a/mall-shop/src/main/resources/bootstrap-prod.yml +++ b/mall-shop/src/main/resources/bootstrap-prod.yml @@ -145,7 +145,7 @@ logging: com: suisung: mall: - shop: debug + shop: info sun: mail: error baomidou: error diff --git a/mall-shop/src/main/resources/mapper/activity/ShopActivityCutpriceMapper.xml b/mall-shop/src/main/resources/mapper/activity/ShopActivityCutpriceMapper.xml index 4735e449..b3f9bb66 100644 --- a/mall-shop/src/main/resources/mapper/activity/ShopActivityCutpriceMapper.xml +++ b/mall-shop/src/main/resources/mapper/activity/ShopActivityCutpriceMapper.xml @@ -4,7 +4,7 @@ - ac_id, activity_id, user_id, ac_sale_price, ac_datetime, order_id, ac_mix_limit_price, ac_num + ac_id, activity_id, user_id, ac_sale_price, ac_datetime, order_id, ac_mix_limit_price, ac_num, expired_at, version diff --git a/mall-shop/src/main/resources/mapper/lakala/LklReceiveNotifyLogMapper.xml b/mall-shop/src/main/resources/mapper/lakala/LklReceiveNotifyLogMapper.xml new file mode 100644 index 00000000..975b2a94 --- /dev/null +++ b/mall-shop/src/main/resources/mapper/lakala/LklReceiveNotifyLogMapper.xml @@ -0,0 +1,8 @@ + + + + + + * + + diff --git a/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml b/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml index a91d358a..229b51a9 100644 --- a/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/store/ShopStoreActivityBaseMapper.xml @@ -3,10 +3,6 @@ - - activity_id, store_id, user_id, activity_name, activity_title, activity_remark, activity_combo_id, - activity_type_id, activity_starttime, activity_endtime, activity_state, activity_rule, activity_type, - activity_order, activity_is_finish, item_id, subsite_id - + * diff --git a/mall-shop/src/main/resources/mapper/store/ShopStoreBaseMapper.xml b/mall-shop/src/main/resources/mapper/store/ShopStoreBaseMapper.xml index cc6988a1..e0bbc023 100644 --- a/mall-shop/src/main/resources/mapper/store/ShopStoreBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/store/ShopStoreBaseMapper.xml @@ -5,8 +5,8 @@ store_id , user_id, store_name, store_grade_id, store_logo, store_slogan, store_domain, store_area, store_district_id, - store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open, store_biz_state, - ringtone_is_enable, shop_parent_id, store_2nd_category_id, + store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open, + store_biz_state, store_biz_opening_date, ringtone_is_enable, shop_parent_id, store_2nd_category_id, store_category_id, store_state_id, store_time, store_end_time, product_category_ids, store_o2o_tags, store_o2o_flag, store_o2o_merchant_id, store_circle, subsite_id, lkl_merchant_no, lkl_term_no, wx_qrcode, split_ratio, packing_fee, created_at, updated_at diff --git a/mall-shop/src/main/resources/static/diy/js/diy.js b/mall-shop/src/main/resources/static/diy/js/diy.js index 3fe8121c..c46760f0 100644 --- a/mall-shop/src/main/resources/static/diy/js/diy.js +++ b/mall-shop/src/main/resources/static/diy/js/diy.js @@ -20809,7 +20809,7 @@ c(); }, s, o, r)); }) : 17 == i[_x41903[4420]] && $[_x41903[39]](i[_x41903[4617]][_x41903[473]], function(e, t) { - t[_x41903[124]] == a && (s = r = o = 100, publicFun[_x41903[4586]]($(n[_x41903[476]])[_x41903[217]](_x41903[124]), function(e) { + t[_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[4618])), t[_x41903[2345]] = e[_x41903[473]][_x41903[688]], c(); diff --git a/mall-shop/src/main/resources/templates/diy.html b/mall-shop/src/main/resources/templates/diy.html index 59a5c53d..34680d3a 100644 --- a/mall-shop/src/main/resources/templates/diy.html +++ b/mall-shop/src/main/resources/templates/diy.html @@ -15,10 +15,10 @@ var SYSTEM = SYSTEM || {}; SYSTEM.skin = 'green'; - var authorization = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdG9yZV9pZCI6bnVsbCwiY2hhaW5faWQiOm51bGwsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiMV_ns7vnu5_nrqHnkIblkZgiXSwiY2xpZW50X2lkIjoiYWRtaW4tYXBwIiwicm9sZV9pZCI6OSwic2NvcGUiOlsiYWxsIl0sInNpdGVfaWQiOjAsInVzZXJfbmlja25hbWUiOiLns7vnu5_nrqHnkIblkZgiLCJ1c2VyX2FjY291bnQiOiJhZG1pbiIsImlkIjoxMDAwMSwiZXhwIjoxNzYyODQwMTUyLCJqdGkiOiJhZTZhNDI3Mi1kMzcyLTRjYTktYTVjNC1iMTQ0N2Y5Zjc4MTAifQ.YMx84RgGve-BbAPOGI8qRFk4DaS33PInxBjTdfOwxRkEhaCqOcIWm28o6De6QF_ktVNJXmTQEw1iQNgyrOVSmzOtgqbS7dP9S79_UYZpXwdH_6Wwn2BhAjdmiPz7_h-Qq1HgQTtmDoJk4aLhV0WiAMB7tnX0IvcWYsIox8bV8TA" + var authorization = "${authorization}"; - + @@ -60,18 +60,18 @@

-
+
{{(index+1)}}. {{item.PageTitle?item.PageTitle:'第'+(index+1)+'项'}} @@ -80,12 +80,12 @@ 积分 - - +
-
+
首页
- +
社区
+ :SnsPageId="item.Id" class="weui-switch" type="checkbox" + v-model="item.IsSns" v-on:change="setSnsPage"/>
B端首页
+ :StoreIndexId="item.Id" class="weui-switch" type="checkbox" + v-model="item.StoreIndex" v-on:change="setStoreIndex"/>
资讯
+ :PointPageId="item.Id" :data-page-id="item.Id" class="weui-switch" + data-page-type="IsArticle" type="checkbox" + v-model="item.IsArticle" v-on:change="setDefaultPage"/>
积分
+ :PointPageId="item.Id" :data-page-id="item.Id" class="weui-switch" + data-page-type="IsPoint" type="checkbox" + v-model="item.IsPoint" v-on:change="setDefaultPage"/>
@@ -352,12 +352,14 @@
- + - +
小程序 @@ -369,13 +371,14 @@
-
用户中心 - + class="iconfont icon-zhengzaijieru" + style="font-size: 14px;">
@@ -384,7 +387,7 @@
-
-
    -
  • -
    - +
  • +
    +
    -

    轮播

    +

    轮播

  • -
  • -
    - +
  • +
    +
    -

    图片

    +

    图片

  • -
  • -
    - +
  • +
    +
    -

    图片组

    +

    图片组

  • -
  • -
    - +
  • +
    +
    -

    商品列表

    +

    商品列表

  • -
  • -
    - +
  • +
    +
    -

    功能入口

    +

    功能入口

  • -
  • -
    - +
  • +
    +
    -

    辅助空白

    +

    辅助空白

  • -
  • -
    - +
  • +
    +
    -

    搜索

    +

    搜索

  • -
  • -
    - +
  • +
    +
    -

    电话

    +

    电话

  • - -
    - + +
    +
    -

    背景

    +

    背景

    -
  • -
    - +
  • +
    +
    -

    客服

    +

    客服

  • -
  • -
    - +
  • +
    +
    -

    视频

    +

    视频

  • @@ -509,12 +513,12 @@

    视频

    -
  • -
    - +
  • +
    +
    -

    动态表单

    +

    动态表单

  • @@ -522,78 +526,78 @@

    动态表单

    -
  • -
    - +
  • +
    +
    -

    通告栏

    +

    通告栏

  • -
  • -
    - +
  • +
    +
    -

    标题

    +

    标题

  • -
  • -
    - +
  • +
    +
    -

    营销活动

    +

    营销活动

  • -
  • -
    - +
  • +
    +
    -

    优惠券

    +

    优惠券

  • -
  • -
    - +
  • +
    +
    -

    公众号关注

    +

    公众号关注

  • -
  • -
    - +
  • +
    +
    -

    猜你喜欢

    +

    猜你喜欢

  • -
  • -
    - +
  • +
    +
    -

    附近店铺

    +

    附近店铺

  • -
  • -
    - +
  • +
    +
    -

    秒杀商品

    +

    秒杀商品

  • -
  • -
    - +
  • +
    +
    -

    分类商品

    +

    分类商品

  • - -
  • -
    - + +
  • +
    +
    -

    测试组件

    +

    测试组件

  • - +
-
-
+

{{StoreName}}

@@ -622,16 +626,17 @@
--> -
-
@@ -672,19 +677,19 @@
-
+
  • 自定义装修区域
-
+
  • 常用功能
  • - {{item.name}}
    @@ -692,13 +697,13 @@
-
+
  • 特色功能
  • - {{item.name}}
    @@ -707,7 +712,7 @@
-
-
+
- - - - - - - - - -
+ }" + v-if="item.eltmType==1"> + + + + + + + + + +
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}" + v-if="item.eltmType==2">

点此编辑『富文本』内容 ——

你可以对文字进行加粗斜体

-
+
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
  • @@ -796,33 +804,37 @@
-
-
+
+
-
- - -
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}" + v-if="item.eltmType==3"> + + +
-
+ v-on:click="editModule">
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
  • @@ -830,26 +842,26 @@
-
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}" + v-if="item.eltmType==4">
- - - - - + + + + +
@@ -862,15 +874,16 @@
0.00
-
+
-
+
buy
-
+
购买
@@ -878,29 +891,29 @@
-
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}" + v-for="items in item.eltm4.data" + v-if="item.eltm4.data.length>0 && items">
-
- - - - - +
+ + + + +
@@ -919,13 +932,13 @@ 商品卖点
+ style="display: inline-block; padding-top: 12px;padding-bottom: 12px " + v-if="items.ProductTips!='' && item.eltm4.isProductTips && !item.eltm4.isPrice"> {{items.ProductTips}}
+ style="display: inline-block; padding-top: 12px;padding-bottom: 12px " + v-if="items.ProductTips=='' && item.eltm4.isProductTips && !item.eltm4.isPrice"> 商品卖点
@@ -934,58 +947,62 @@ v-if="items.ItemSalePrice!='' && item.eltm4.isPrice">{{items.ItemSalePrice}}
+ v-bind:style="{color:item.eltm4.priceColor}" + v-if="items.ItemSalePrice=='' && item.eltm4.isPrice"> 19.88
-
+
-
+
buy
-
+
{{item.eltm4.btnText}}
-
+
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-
+
辅助空白区域 -
+
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-
+
-
- {{items.name || '请输入文字'}} - + }" + v-for="items in item.eltm6.data" v-if="item.eltm6.data.length>0"> + {{items.name || '请输入文字'}} +
+ }" v-if="item.eltm6.data.length<=0">
+ }" v-if="item.eltm6.data.length<=0">
-
+
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}" + v-if="item.eltmType==7"> + v-bind:style="{width:(100/item.eltm7.column)+'%',paddingTop:item.eltm7.paddingTop+'px',paddingBottom:item.eltm7.paddingBottom+'px',paddingLeft:item.eltm7.paddingLeft+'px',paddingRight:item.eltm7.paddingRight+'px',backgroundColor:item.bgColor}" + v-for="items in item.eltm7.data" + v-if="item.eltm7.data.length>0 && items">

{{items.name || '功能入口'}}

- + v-bind:style="{width:(100/item.eltm7.column)+'%',paddingTop:item.eltm7.paddingTop+'px',paddingBottom:item.eltm7.paddingBottom+'px',paddingLeft:item.eltm7.paddingLeft+'px',paddingRight:item.eltm7.paddingRight+'px',backgroundColor:item.bgColor}" + v-for="items in item.eltm7.data" + v-for="items in [1,2,3,4,5,6,7,8,9]" v-if="item.eltm7.data.length==0" + v-if="item.eltm7.data.length>0">

功能入口

-
+
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-