Compare commits
69 Commits
31a28b418d
...
f124a08377
| Author | SHA1 | Date | |
|---|---|---|---|
| f124a08377 | |||
| 6ca0af397b | |||
| ed10a03442 | |||
| 6c479e744a | |||
| ad7f89cb42 | |||
| b04f2d095d | |||
| b06826593a | |||
| a43df002dc | |||
| 2ecfce1f2f | |||
| e9c42bdb99 | |||
| af9cce0db1 | |||
| 645ad45d08 | |||
| 41dbafffab | |||
| 5e45474807 | |||
| c3414925a0 | |||
| 615d478b4c | |||
| cb74d80aab | |||
| 0152518315 | |||
| c8a85d5119 | |||
| 31160d4806 | |||
| d59096f6d3 | |||
| 10fa6fd739 | |||
| accd09409d | |||
| eaf1a87db8 | |||
| d4a7c4b3fb | |||
| 62eb843629 | |||
| ef36db0b44 | |||
| 2d612ecc7a | |||
| 2a2ec2f9b3 | |||
| c587ad5532 | |||
| d2cdc097f8 | |||
| ee31d1f75d | |||
| 80607c8642 | |||
| ce974276a8 | |||
| da259b15e9 | |||
| d1ef71669e | |||
| 44794636f7 | |||
| 19756ee7e3 | |||
| 2e7612ac93 | |||
| 38a1ce0c66 | |||
| 415bbfd4ba | |||
| cfb8714410 | |||
| 070c1fc625 | |||
| eeb7f05706 | |||
| 2661277bd2 | |||
| db33dd255e | |||
| d2289f2d12 | |||
| 507b65838c | |||
| bb0eb917c1 | |||
| f3cf329f19 | |||
| 3287be123e | |||
| 57ccf4f599 | |||
| 3c19ee7064 | |||
| 3ee9eaee8d | |||
| 30bf1446d4 | |||
| 45a2f7a07d | |||
| 1aea5d0281 | |||
| 38b674e05a | |||
| 0c15b2c034 | |||
| 4814ca202d | |||
| 2ab4495920 | |||
| ea0afa5bdb | |||
| 3a4a8d7772 | |||
| 12fbd15a66 | |||
| d711a70e34 | |||
| b9d4d8744f | |||
| 5c6bf1bb3f | |||
| ef4db7a0bf | |||
| fa80c2fca3 |
@ -99,7 +99,7 @@ logging:
|
||||
suisung:
|
||||
mall:
|
||||
account:
|
||||
mapper: debug
|
||||
mapper: info
|
||||
sun:
|
||||
mail: error
|
||||
baomidou: error
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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<Boolean, LklSeparateWithTotalAmountDTO> 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> Boolean表示是否成功,LklSeparateWithTotalAmountDTO为分账结果
|
||||
* @return SeparateResult 包含成功状态、分账结果和错误信息的包装类
|
||||
*/
|
||||
public Pair<Boolean, LklSeparateWithTotalAmountDTO> calculateSeparateAmount() {
|
||||
public SeparateResult calculateSeparateAmount() {
|
||||
try {
|
||||
// 清空之前的错误信息
|
||||
this.errMsg = null;
|
||||
|
||||
// 参数校验
|
||||
validateInputs();
|
||||
Pair<Boolean, String> validateResult = validateInputs();
|
||||
if (!validateResult.getFirst()) {
|
||||
String errorMsg = "分账计算参数异常: " + validateResult.getSecond();
|
||||
log.error(errorMsg);
|
||||
this.errMsg = errorMsg;
|
||||
return SeparateResult.failure(errorMsg);
|
||||
}
|
||||
|
||||
// 1. 计算拉卡拉分账金额和可分账金额
|
||||
calculateLklAmountAndCanSeparateAmount();
|
||||
Pair<Boolean, String> lklResult = calculateLklAmountAndCanSeparateAmount();
|
||||
if (!lklResult.getFirst()) {
|
||||
String errorMsg = "分账计算参数异常: " + lklResult.getSecond();
|
||||
log.error(errorMsg);
|
||||
this.errMsg = errorMsg;
|
||||
return SeparateResult.failure(errorMsg);
|
||||
}
|
||||
|
||||
// 2. 确定实际可分账金额
|
||||
int actualCanSeparateAmount = determineActualCanSeparateAmount();
|
||||
Pair<Boolean, String> 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<Boolean, String> ratioResult = calculateDefaultRatios();
|
||||
if (!ratioResult.getFirst()) {
|
||||
String errorMsg = "分账计算参数异常: " + ratioResult.getSecond();
|
||||
log.error(errorMsg);
|
||||
this.errMsg = errorMsg;
|
||||
return SeparateResult.failure(errorMsg);
|
||||
}
|
||||
|
||||
// 4. 根据优先级顺序计算各参与方分账金额
|
||||
calculateAmountsInPriorityOrder(actualCanSeparateAmount);
|
||||
Pair<Boolean, String> amountResult = calculateAmountsInPriorityOrder(actualCanSeparateAmount);
|
||||
if (!amountResult.getFirst()) {
|
||||
String errorMsg = "分账计算参数异常: " + amountResult.getSecond();
|
||||
log.error(errorMsg);
|
||||
this.errMsg = errorMsg;
|
||||
return SeparateResult.failure(errorMsg);
|
||||
}
|
||||
|
||||
// 5. 校验分账金额总和不能超过总金额
|
||||
validateSeparateAmountTotal();
|
||||
Pair<Boolean, String> totalResult = validateSeparateAmountTotal();
|
||||
if (!totalResult.getFirst()) {
|
||||
String errorMsg = "分账计算参数异常: " + totalResult.getSecond();
|
||||
log.error(errorMsg);
|
||||
this.errMsg = errorMsg;
|
||||
return SeparateResult.failure(errorMsg);
|
||||
}
|
||||
|
||||
// 6. 计算商家实际分账比例
|
||||
calculateActualMchRatio();
|
||||
Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void validateInputs() {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void calculateLklAmountAndCanSeparateAmount() {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private int determineActualCanSeparateAmount() {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void calculateDefaultRatios() {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void calculateAmountsInPriorityOrder(int actualCanSeparateAmount) {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void calculateActualMchRatio() {
|
||||
private Pair<Boolean, String> 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> Boolean表示是否成功,String为错误信息
|
||||
*/
|
||||
private void validateSeparateAmountTotal() {
|
||||
int totalAmount = 0;
|
||||
private Pair<Boolean, String> 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 : "");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<String, String> findTimeInterSection(List<Map<String, String>> timeList) {
|
||||
public static Pair<String, String> findTimeInterSection(List<Pair<String, String>> 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<String, String> timeMap : timeList) {
|
||||
if (timeMap == null || !timeMap.containsKey("startTimeStr") || !timeMap.containsKey("endTimeStr")) {
|
||||
for (Pair<String, String> 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<String, String> 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<Map<String, String>> timeList1 = new ArrayList<>();
|
||||
Map<String, String> range1 = new HashMap<>();
|
||||
range1.put("startTimeStr", "06:00");
|
||||
range1.put("endTimeStr", "17:00");
|
||||
timeList1.add(range1);
|
||||
List<Pair<String, String>> timeList1 = new ArrayList<>();
|
||||
timeList1.add(Pair.of("06:00", "17:00"));
|
||||
timeList1.add(Pair.of("10:00", "18:00"));
|
||||
|
||||
Map<String, String> range2 = new HashMap<>();
|
||||
range2.put("startTimeStr", "06:00");
|
||||
range2.put("endTimeStr", "17:00");
|
||||
timeList1.add(range2);
|
||||
|
||||
Map<String, String> intersection1 = findTimeInterSection(timeList1);
|
||||
Pair<String, String> intersection1 = findTimeInterSection(timeList1);
|
||||
System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
|
||||
|
||||
// 测试无交集情况
|
||||
List<Map<String, String>> timeList2 = new ArrayList<>();
|
||||
Map<String, String> range3 = new HashMap<>();
|
||||
range3.put("startTimeStr", "09:00");
|
||||
range3.put("endTimeStr", "12:00");
|
||||
timeList2.add(range3);
|
||||
// 测试无交集情况
|
||||
List<Pair<String, String>> timeList2 = new ArrayList<>();
|
||||
timeList2.add(Pair.of("09:00", "12:00"));
|
||||
timeList2.add(Pair.of("13:00", "17:00"));
|
||||
|
||||
Map<String, String> range4 = new HashMap<>();
|
||||
range4.put("startTimeStr", "13:00");
|
||||
range4.put("endTimeStr", "17:00");
|
||||
timeList2.add(range4);
|
||||
Pair<String, String> intersection2 = findTimeInterSection(timeList2);
|
||||
System.out.println("交集结果2: " + intersection2); // 应该是null
|
||||
|
||||
Map<String, String> intersection2 = findTimeInterSection(timeList2);
|
||||
System.out.println("交集结果2: " + intersection2); // 应该是空Map
|
||||
|
||||
// 测试空列表
|
||||
Map<String, String> intersection3 = findTimeInterSection(null);
|
||||
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是空Map
|
||||
// 测试空列表
|
||||
Pair<String, String> intersection3 = findTimeInterSection(null);
|
||||
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* 分布式锁工具类
|
||||
*
|
||||
* <p>使用示例:</p>
|
||||
* <pre>
|
||||
* {@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();
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
@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<String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -26,7 +26,7 @@ logging:
|
||||
level:
|
||||
com:
|
||||
sun:
|
||||
mail: error
|
||||
mail: info
|
||||
baomidou: error
|
||||
alibaba:
|
||||
nacos:
|
||||
|
||||
@ -100,7 +100,7 @@ logging:
|
||||
suisung:
|
||||
mall:
|
||||
pay:
|
||||
mapper: debug
|
||||
mapper: info
|
||||
sun:
|
||||
mail: error
|
||||
baomidou: error
|
||||
|
||||
@ -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())) {
|
||||
|
||||
@ -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<ShopActivityCu
|
||||
*/
|
||||
Pair<Boolean, String> canDoOrderCutPriceActivity(Integer ac_id, Integer order_user_id);
|
||||
|
||||
|
||||
/**
|
||||
* 检查砍价活动是否过期和库存是否足够
|
||||
*
|
||||
* @param shopStoreActivityBase 砍价活动基础信息
|
||||
* @return Pair<Boolean, String> 检查结果,Boolean表示是否通过检查,String表示错误信息
|
||||
*/
|
||||
Pair<Boolean, String> 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();
|
||||
}
|
||||
|
||||
@ -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<ShopActivit
|
||||
return toMobileResult(cutpricePage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 砍价活动详情
|
||||
* 砍价活动详情(注:除了获取活动详情信息,还创建砍价订单记录和查询砍价历史历史)
|
||||
*
|
||||
* @return
|
||||
* @return 砍价活动详情信息
|
||||
*/
|
||||
@Override
|
||||
public Map getCutPriceActivity() {
|
||||
// 获取请求参数
|
||||
Integer activity_id = getParameter("activity_id", 0);
|
||||
Integer participant_id = getParameter("participant_id", 0);
|
||||
Integer user_id = getParameter("user_id", 0);
|
||||
|
||||
// 参数校验
|
||||
if (activity_id == null || activity_id <= 0) {
|
||||
throw new ApiException(I18nUtil._("活动ID无效!"));
|
||||
}
|
||||
if (user_id == null || user_id <= 0) {
|
||||
throw new ApiException(I18nUtil._("用户ID无效!"));
|
||||
}
|
||||
|
||||
// 获取活动基础信息
|
||||
ShopStoreActivityBase activityBase = shopStoreActivityBaseService.get(activity_id);
|
||||
if (activityBase == null) {
|
||||
throw new ApiException(I18nUtil._("未找到活动信息!"));
|
||||
}
|
||||
|
||||
Map activity_row = Convert.toMap(String.class, Object.class, activityBase);
|
||||
|
||||
// if (!shopStoreActivityBaseService.verifyActivity(activity_row)) {
|
||||
// throw new ApiException(I18nUtil._("该活动不存在或已结束!"));
|
||||
// }
|
||||
|
||||
// 获取当前用户信息
|
||||
UserDto user = getCurrentUser();
|
||||
if (CheckUtil.isEmpty(participant_id) && user != null) {
|
||||
if (CheckUtil.isEmpty(participant_id) && user != null && user.getId() > 0) {
|
||||
participant_id = user.getId();
|
||||
}
|
||||
|
||||
// 查询用户是否已参与该砍价活动
|
||||
QueryWrapper<ShopActivityCutprice> 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<Boolean, String> 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<ShopActivityCutpriceHistory> 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<ShopActivit
|
||||
|
||||
BigDecimal subtractPrice = NumberUtil.sub(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
|
||||
|
||||
// 能否立即出手 1-可以;2-不可以
|
||||
// 设置购买状态和提示信息
|
||||
activity_row.put("can_buy_now", canBuyNow ? CommonConstant.Enable : CommonConstant.Disable2);
|
||||
activity_row.put("cannot_buy_now_reason",
|
||||
canBuyNow ? "恭喜您,商品可以立即出手了。" :
|
||||
@ -340,56 +382,121 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
|
||||
return activity_row;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 砍价
|
||||
* 自己或朋友砍价
|
||||
*
|
||||
* @param ac_id 活动 Id
|
||||
* @param user_id 砍价用户
|
||||
* @return
|
||||
* @return CommonResult 砍价结果
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public CommonResult doCutPrice(Integer ac_id, Integer user_id) {
|
||||
DateTime today = DateUtil.beginOfDay(new Date());
|
||||
Integer num = accountBaseConfigService.getConfig("user_cutprice_num", 0);
|
||||
Integer cut_num = Convert.toInt(redisService.hGet("cutprice-" + today, user_id.toString()), 0);
|
||||
ShopActivityCutprice cutprice_row = get(ac_id);
|
||||
if (!cutprice_row.getUser_id().equals(user_id)) {
|
||||
// 参数校验
|
||||
if (ac_id == null || ac_id <= 0) {
|
||||
return CommonResult.failed(I18nUtil._("活动ID无效!"));
|
||||
}
|
||||
if (user_id == null || user_id <= 0) {
|
||||
return CommonResult.failed(I18nUtil._("用户ID无效!"));
|
||||
}
|
||||
|
||||
// 获取砍价记录
|
||||
ShopActivityCutprice shopActivityCutprice = get(ac_id);
|
||||
if (shopActivityCutprice == null) {
|
||||
return CommonResult.failed(I18nUtil._("抱歉,砍价记录已失效!"));
|
||||
}
|
||||
|
||||
Integer activity_id = shopActivityCutprice.getActivity_id();
|
||||
// 检查活动状态是否正常
|
||||
ShopStoreActivityBase shopStoreActivityBase = shopStoreActivityBaseService.get(shopActivityCutprice.getActivity_id());
|
||||
if (shopStoreActivityBase == null) {
|
||||
// 活动不存在,更新砍价订单状态为已取消
|
||||
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
|
||||
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已失效!"));
|
||||
}
|
||||
|
||||
// 活动状态不是正常状态
|
||||
if (!ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_NORMAL)) {
|
||||
// 如果活动已结束或已关闭,更新砍价订单状态为已取消
|
||||
if (ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_FINISHED)
|
||||
|| ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_CLOSED)) {
|
||||
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
|
||||
}
|
||||
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已失效!"));
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
Long expired_at = shopActivityCutprice.getExpired_at();
|
||||
|
||||
// 检查砍价订单是否过期
|
||||
if (CheckUtil.isNotEmpty(expired_at) && expired_at < now.getTime()) {
|
||||
// 砍价订单已过期,更新状态为已过期
|
||||
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
|
||||
return CommonResult.failed(I18nUtil._("砍价已过期,下次早点来!"));
|
||||
}
|
||||
|
||||
// 检查活动是否已结束(活动时间已过)
|
||||
if (!shopStoreActivityBaseService.isActivityTimeValid(shopStoreActivityBase, now)) {
|
||||
// 活动已结束,更新砍价订单状态为已过期
|
||||
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
|
||||
return CommonResult.failed(I18nUtil._("砍价已过期,下次早点来!"));
|
||||
}
|
||||
|
||||
// 检查是否是帮别人砍价且次数已用完
|
||||
if (!shopActivityCutprice.getUser_id().equals(user_id)) {
|
||||
DateTime today = DateUtil.beginOfDay(new Date());
|
||||
Integer num = accountBaseConfigService.getConfig("user_cutprice_num", 0);
|
||||
Integer cut_num = Convert.toInt(redisService.hGet("cutprice-" + today, user_id.toString()), 0);
|
||||
|
||||
if (num > 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<ShopActivityCutpriceHistory> 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<ShopActivit
|
||||
ach_data.setAch_datetime(new Date());
|
||||
ach_data.setAc_id(ac_id);
|
||||
|
||||
// 保存砍价历史记录
|
||||
if (!shopActivityCutpriceHistoryService.saveOrUpdate(ach_data)) {
|
||||
throw new ApiException(I18nUtil._("修改砍价历史失败!"));
|
||||
throw new ApiException(I18nUtil._("保存砍价历史失败!"));
|
||||
}
|
||||
|
||||
cutprice_row.setAc_sale_price(NumberUtil.sub(ac_sale_price, cut_price));
|
||||
cutprice_row.setAc_num(cutprice_row.getAc_num() + 1);
|
||||
if (!edit(cutprice_row)) {
|
||||
throw new ApiException(I18nUtil._("修改金额失败!"));
|
||||
// 更新砍价信息
|
||||
shopActivityCutprice.setAc_sale_price(NumberUtil.sub(ac_sale_price, cut_price));
|
||||
shopActivityCutprice.setAc_num(shopActivityCutprice.getAc_num() + 1);
|
||||
if (!edit(shopActivityCutprice)) {
|
||||
throw new ApiException(I18nUtil._("更新砍价信息失败!"));
|
||||
}
|
||||
|
||||
if (!cutprice_row.getUser_id().equals(user_id)) {
|
||||
// 更新帮砍次数(如果不是自己砍自己的话)
|
||||
if (!shopActivityCutprice.getUser_id().equals(user_id)) {
|
||||
DateTime today = DateUtil.beginOfDay(new Date());
|
||||
Integer cut_num = Convert.toInt(redisService.hGet("cutprice-" + today, user_id.toString()), 0);
|
||||
redisService.hSet("cutprice-" + today, user_id.toString(), cut_num + 1, 24 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
// 根据最新砍价信息(最后一次砍价成功之后,达到最低砍价价格),更新砍价订单状态
|
||||
if (NumberUtil.isGreaterOrEqual(shopActivityCutprice.getAc_sale_price(), ac_mix_limit_price)) {
|
||||
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_CutFinished);
|
||||
}
|
||||
|
||||
return CommonResult.success(ach_data);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 砍价活动是否符合立即下单了?
|
||||
*
|
||||
@ -448,7 +566,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
|
||||
}
|
||||
|
||||
// 2. 检查活动是否在有效期内
|
||||
if (!shopStoreActivityBaseService.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime())) {
|
||||
if (!shopStoreActivityBaseService.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime(), new Date())) {
|
||||
return Pair.of(false, I18nUtil._("该活动已过期,请检查。"));
|
||||
}
|
||||
|
||||
@ -492,5 +610,177 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查砍价活动是否过期和库存是否足够
|
||||
*
|
||||
* @param shopStoreActivityBase 砍价活动基础信息
|
||||
* @return Pair<Boolean, String> 检查结果,Boolean表示是否通过检查,String表示错误信息
|
||||
*/
|
||||
@Override
|
||||
public Pair<Boolean, String> 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<ShopActivityCutprice> 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<ShopActivityCutprice> 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 砍价活动结束后,定时修改砍价订单状态 定时任务(包括多个状态业务变更)
|
||||
* <p>
|
||||
* 砍价订单状态:1-砍价已完成下单;2-砍价未下单已取消;3-砍价助力进行中;4-砍价过期失效;6-砍价助力已完成待下单;
|
||||
* <p>
|
||||
* 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<ShopActivityCutprice> 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<NOW()",
|
||||
CommonConstant.CutPrice_Order_State_ING);
|
||||
successCount++;
|
||||
} else {
|
||||
log.debug("更新操作3未匹配到任何记录");
|
||||
}
|
||||
|
||||
// 处理状态6(砍价助力已完成待下单) -> 状态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<NOW()",
|
||||
CommonConstant.CutPrice_Order_State_CutFinished);
|
||||
successCount++;
|
||||
} else {
|
||||
log.debug("更新操作4未匹配到任何记录");
|
||||
}
|
||||
|
||||
log.info("定时任务更新砍价订单状态完成,总共成功执行{}次更新操作", successCount);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("定时任务更新砍价订单状态时发生异常,已成功执行{}次更新操作", successCount, e);
|
||||
}
|
||||
|
||||
return successCount;
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,32 +169,44 @@ public class ShopBaseConfigServiceImpl extends BaseServiceImpl<ShopBaseConfigMap
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setHeader("Cache-Control", "no-cache");
|
||||
response.setDateHeader("Expires", 0);
|
||||
// 产生验证码图片的。图片的宽是116,高是36,验证码的长度是4,干扰线的条数是20
|
||||
|
||||
// 参数校验
|
||||
String verify_token = getParameter("verify_token", String.class);
|
||||
if (StrUtil.isBlank(verify_token)) {
|
||||
logger.warn("验证码缺少 verify_token 参数");
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// 产生验证码图片
|
||||
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(120, 40, 4, 20);
|
||||
|
||||
// 拼接时间戳
|
||||
String verify_token = getParameter("verify_token", String.class);
|
||||
|
||||
//获取验证码图片中的字符串
|
||||
// 生成Redis键
|
||||
String code = RedisConstant.Verifycode_NameSpace + verify_token + lineCaptcha.getCode();
|
||||
redisService.set(code, "", 60); // 有效期一分钟
|
||||
|
||||
//获取到response的响应流。
|
||||
// 写入Redis并检查结果
|
||||
redisService.set(code, "", 60);
|
||||
|
||||
// 输出图片
|
||||
BufferedImage image = lineCaptcha.getImage();
|
||||
OutputStream os = null;
|
||||
try {
|
||||
os = response.getOutputStream();
|
||||
ImageIO.write(image, "png", os);
|
||||
os.flush();
|
||||
} catch (IOException e) {
|
||||
logger.error("获取验证码响应异常!" + e.getMessage(), e);
|
||||
logger.error("获取验证码响应异常: {}", e.getMessage(), e);
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
} finally {
|
||||
try {
|
||||
assert os != null;
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("获取验证码响应异常!" + e.getMessage(), e);
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
logger.error("关闭输出流异常:{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.suisung.mall.common.api.StateCode;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreActivityBase;
|
||||
import com.suisung.mall.shop.activity.service.ShopActivityCutpriceService;
|
||||
import com.suisung.mall.shop.config.SpringUtil;
|
||||
import com.suisung.mall.shop.store.service.ShopStoreActivityBaseService;
|
||||
import org.quartz.JobExecutionContext;
|
||||
@ -27,6 +28,7 @@ public class UpdateActivityStatusJob extends QuartzJobBean {
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
Logger logger = LoggerFactory.getLogger(UpdateActivityStatusJob.class);
|
||||
ShopStoreActivityBaseService shopStoreActivityBaseService = SpringUtil.getBean(ShopStoreActivityBaseService.class);
|
||||
ShopActivityCutpriceService shopActivityCutpriceService = SpringUtil.getBean(ShopActivityCutpriceService.class);
|
||||
TransactionTemplate transactionTemplate = SpringUtil.getBean(TransactionTemplate.class);
|
||||
|
||||
int page = 1;
|
||||
@ -63,5 +65,8 @@ public class UpdateActivityStatusJob extends QuartzJobBean {
|
||||
page++;
|
||||
}
|
||||
|
||||
// 更新砍价订单的过期状态
|
||||
shopActivityCutpriceService.autoUpdateCutPriceStateJob();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,7 +92,9 @@ public class LakalaController extends BaseControllerImpl {
|
||||
|
||||
// return lklLedgerReceiverService.selectAgentAndPlatformByMchId(36L);
|
||||
|
||||
return lakalaApiService.ewalletWithDrawNotify(null, paramsJSON.getStr("a"), paramsJSON.getStr("b"));
|
||||
// return lakalaApiService.ewalletWithDrawNotify(null, paramsJSON.getStr("a"), paramsJSON.getStr("b"));
|
||||
|
||||
return lakalaApiService.tradeQuery(paramsJSON.getInt("storeId"), paramsJSON.getStr("orderId"));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "批量发送推送消息 - 测试案例", notes = "批量发送推送消息 - 测试案例")
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.suisung.mall.common.modules.lakala.LklReceiveNotifyLog;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
|
||||
@Repository
|
||||
public interface LklReceiveNotifyLogMapper extends BaseMapper<LklReceiveNotifyLog> {
|
||||
|
||||
}
|
||||
@ -176,7 +176,7 @@ public interface LakalaApiService {
|
||||
* @return
|
||||
*/
|
||||
CommonResult getBankCardBin(String bankCardNo);
|
||||
|
||||
|
||||
/**
|
||||
* 根据商户号、交易号和收货流水号执行订单分账操作
|
||||
* <p>
|
||||
@ -376,7 +376,7 @@ public interface LakalaApiService {
|
||||
* @param refCanSeparateAmt 参考可分金额(单位:分) 可选参数
|
||||
* @return Pair<Boolean, LklSeparateDTO> 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象
|
||||
*/
|
||||
Pair<Boolean, LklSeparateWithTotalAmountDTO> calculateAndEvaluateSharingParams(Integer orderPayAmount,
|
||||
LklSeparateWithTotalAmountDTO.SeparateResult calculateAndEvaluateSharingParams(Integer orderPayAmount,
|
||||
Integer shippingFeeInner,
|
||||
BigDecimal mchSplitRatioRaw,
|
||||
BigDecimal platSplitRatio,
|
||||
|
||||
@ -31,7 +31,7 @@ public interface LklOrderDrawService extends IBaseService<LklOrderDraw> {
|
||||
* @param merOrderNo 商户订单号
|
||||
* @return
|
||||
*/
|
||||
LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo);
|
||||
LklOrderDraw getByMercIdAndMerOrderNo(String mercId, String merOrderNo);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@ -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<LklReceiveNotifyLog> {
|
||||
|
||||
/**
|
||||
* 新增或更新拉卡拉确认收货通知日志记录
|
||||
*
|
||||
* @param lklReceiveNotifyJSON 拉卡拉通知的JSON数据
|
||||
* @return 处理结果,true表示成功,false表示失败
|
||||
*/
|
||||
Boolean addOrUpdate(String lklReceiveNotifyJSON);
|
||||
|
||||
/**
|
||||
* 根据订单号获取拉卡拉确认收货通知日志记录
|
||||
*
|
||||
* @param orderId 商户订单号
|
||||
* @return 拉卡拉确认收货通知日志记录,如果不存在则返回null
|
||||
*/
|
||||
LklReceiveNotifyLog getByOrderId(String orderId);
|
||||
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
|
||||
/**
|
||||
* 发货类交易确认收货通知处理, 微信通知拉卡拉,拉卡拉通知我们系统,已经完成确认收货
|
||||
* 重要接口(直接影响到分账执行的):发货类交易确认收货通知处理, 微信通知拉卡拉,拉卡拉通知我们系统,已经完成确认收货
|
||||
* <p>
|
||||
* 参考文档:https://o.lakala.com/#/home/document/detail?id=1003
|
||||
* </p>
|
||||
@ -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<Boolean, String> 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<Boolean, LklSeparateWithTotalAmountDTO> 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<Boolean, LklSeparateDTO> 分账参数评估结果,第一个元素表示是否成功,第二个元素为分账参数对象
|
||||
*/
|
||||
@Override
|
||||
public Pair<Boolean, LklSeparateWithTotalAmountDTO> 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<Boolean, LklSeparateWithTotalAmountDTO> 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("分账参数有误,分账估算失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ public class LklOrderDrawServiceImpl extends BaseServiceImpl<LklOrderDrawMapper,
|
||||
* @return 拉卡拉订单分账记录,未找到返回null
|
||||
*/
|
||||
@Override
|
||||
public LklOrderDraw getByByMercIdAndMerOrderNo(String mercId, String merOrderNo) {
|
||||
public LklOrderDraw getByMercIdAndMerOrderNo(String mercId, String merOrderNo) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(mercId) && StrUtil.isBlank(merOrderNo)) {
|
||||
log.warn("[LklOrderDraw] 查询记录参数校验失败,商户号和商户订单号均为空");
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
package com.suisung.mall.shop.lakala.service.impl;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
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.suisung.mall.common.modules.lakala.LklReceiveNotifyLog;
|
||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
import com.suisung.mall.shop.lakala.mapper.LklReceiveNotifyLogMapper;
|
||||
import com.suisung.mall.shop.lakala.service.LklReceiveNotifyLogService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LklReceiveNotifyLogServiceImpl extends BaseServiceImpl<LklReceiveNotifyLogMapper, LklReceiveNotifyLog> 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<LklReceiveNotifyLog>().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<LklReceiveNotifyLog> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<BookingArgDTO> list = shopOrderInfoService.genBookingOrderArgList(store_ids);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
@ -616,4 +616,6 @@ public interface ShopOrderBaseService extends IBaseService<ShopOrderBase> {
|
||||
* @return
|
||||
*/
|
||||
Boolean updateOrderTime(String orderId, Date orderTime);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -5323,7 +5323,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
ShopProductItem shopProductItem = shopProductItemService.get(item_id);
|
||||
if (shopProductItem != null) {
|
||||
logger.info("无法获取订单中的商品,请检查!");//商品存在才执行库存扣减
|
||||
// throw new ApiException(I18nUtil._("无法获取订单中的商品,请检查!"));
|
||||
// throw new ApiException(I18nUtil._("无法获取订单中的商品,请检查!"));
|
||||
Integer item_quantity_frozen = shopProductItem.getItem_quantity_frozen();
|
||||
int quantity_frozen = item_quantity_frozen - order_item_quantity;
|
||||
shopProductItem.setItem_quantity_frozen(quantity_frozen > 0 ? quantity_frozen : 0);
|
||||
@ -6325,9 +6325,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
throw new ApiException(I18nUtil._("店铺关闭中,不可以下单!"));
|
||||
}
|
||||
|
||||
// 判断店铺是否打烊?打烊不能下单
|
||||
// 判断店铺是否已歇业?不能下单
|
||||
Pair<Integer, String> 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<ShopOrderBaseMappe
|
||||
BigDecimal storeSplitRatio = shopStoreBaseService.getStoreSplitRatio(store_id, false);
|
||||
|
||||
// 计算平台和代理商的分账金额
|
||||
Pair<Boolean, LklSeparateWithTotalAmountDTO> 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<ShopOrderBaseMappe
|
||||
null, null, null);
|
||||
|
||||
// 计算平台费
|
||||
if (calcResult != null && calcResult.getFirst() && calcResult.getSecond() != null) {
|
||||
if (calcResult != null && calcResult.getIsSuccess() && calcResult.getData() != null) {
|
||||
try {
|
||||
LklSeparateWithTotalAmountDTO lklSeparateDTO = calcResult.getSecond();
|
||||
LklSeparateWithTotalAmountDTO lklSeparateDTO = calcResult.getData();
|
||||
// 确保分账金额不为负数
|
||||
BigDecimal totalSeparateAmount = BigDecimal.valueOf(lklSeparateDTO.getCanSeparateAmount())
|
||||
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
|
||||
@ -9365,4 +9365,5 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ import com.suisung.mall.common.modules.order.ShopOrderStateLog;
|
||||
import com.suisung.mall.common.modules.pay.PayPlantformResource;
|
||||
import com.suisung.mall.common.modules.plantform.ShopPlantformFeedback;
|
||||
import com.suisung.mall.common.modules.product.ShopProductComment;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreInfo;
|
||||
import com.suisung.mall.common.pojo.dto.BookingArgDTO;
|
||||
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
||||
@ -59,6 +60,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
@ -852,84 +854,6 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
return this.count(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查订单预约参数是否合法
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param bookingState 预约配送状态
|
||||
* @param bookingBeginTimeStr 预约开始时间
|
||||
* @param bookingEndTimeStr 预约截止时间
|
||||
* @return 验证结果Pair,第一个元素表示是否通过验证,第二个元素为提示信息
|
||||
*/
|
||||
@Override
|
||||
public Pair<Boolean, String> checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTimeStr, String bookingEndTimeStr) {
|
||||
// 1. 必填参数检查
|
||||
if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(bookingState)) {
|
||||
return Pair.of(false, "[预约单校验] 缺少必要参数");
|
||||
}
|
||||
|
||||
// 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<ShopOrderInfoMappe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查订单预约参数是否合法
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param bookingState 预约配送状态
|
||||
* @param bookingBeginTimeStr 预约开始时间
|
||||
* @param bookingEndTimeStr 预约截止时间
|
||||
* @return 验证结果Pair,第一个元素表示是否通过验证,第二个元素为提示信息
|
||||
*/
|
||||
@Override
|
||||
public Pair<Boolean, String> checkBookingOrderArgs(Integer storeId, Integer bookingState, String bookingBeginTimeStr, String bookingEndTimeStr) {
|
||||
// 1. 必填参数检查
|
||||
if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(bookingState)) {
|
||||
return Pair.of(false, "[预约单校验] 缺少必要参数");
|
||||
}
|
||||
|
||||
// 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<ShopOrderInfoMappe
|
||||
*/
|
||||
@Override
|
||||
public List<BookingArgDTO> genBookingOrderArgList(String storeIds) {
|
||||
// 初始化默认营业时间
|
||||
Map<String, String> timesMap = new HashMap<>();
|
||||
// 初始化营业时间对象
|
||||
Pair<String, String> storeBizTimeRange = null;
|
||||
|
||||
// 如果storeId不为空,则尝试获取店铺信息
|
||||
if (StrUtil.isNotBlank(storeIds)) {
|
||||
List<Map<String, String>> timesMapList = selStoreBizTimeMapList(storeIds);
|
||||
if (!CollUtil.isEmpty(timesMapList)) {
|
||||
timesMap = DateTimeUtils.findTimeInterSection(timesMapList);
|
||||
// 根据一个或多个店铺id获取有效店铺的有效营业时间段
|
||||
List<Pair<String, String>> storeBizTimeRangesList = selectMulStoreBizTimeRanges(storeIds);
|
||||
if (!CollUtil.isEmpty(storeBizTimeRangesList)) {
|
||||
// 获取多个店铺的营业时间段的一个交集
|
||||
storeBizTimeRange = DateTimeUtils.findTimeInterSection(storeBizTimeRangesList);
|
||||
}
|
||||
}
|
||||
|
||||
List<BookingArgDTO> 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<BookingArgDTO> 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<ShopOrderInfoMappe
|
||||
}
|
||||
|
||||
// 设置date_title
|
||||
if (i == 0) {
|
||||
bookingArgDTO.setDate_title("今天(" + weekStr + ")");
|
||||
} else if (i == 1) {
|
||||
bookingArgDTO.setDate_title("明天(" + weekStr + ")");
|
||||
String dateTitle;
|
||||
if (i == 0 && latestBizOpeningDate == null) {
|
||||
dateTitle = isTodayAvailable ? "今天" : "明天";
|
||||
} else if (i == 1 && latestBizOpeningDate == null) {
|
||||
dateTitle = isTodayAvailable ? "明天" : "后天";
|
||||
} else {
|
||||
bookingArgDTO.setDate_title(displayDateStr + "(" + weekStr + ")");
|
||||
dateTitle = displayDateStr;
|
||||
}
|
||||
|
||||
bookingArgDTO.setDate_title(dateTitle + "(" + weekStr + ")");
|
||||
bookingArgDTO.setDate_str(displayDateStr);
|
||||
bookingArgDTO.setDate(dateStr);
|
||||
|
||||
// 生成时间项
|
||||
String startTimeStr = storeBizTimeRange.getFirst();
|
||||
String endTimeStr = storeBizTimeRange.getSecond();
|
||||
bookingArgDTO.setWorking_hours(String.format("%s-%s", startTimeStr, endTimeStr));
|
||||
|
||||
// 生成时间槽
|
||||
List<BookingArgDTO.BookingArgItem> items = new ArrayList<>();
|
||||
|
||||
// 如果是今天,始终添加"立即送出"选项
|
||||
if (i == 0) {
|
||||
BookingArgDTO.BookingArgItem immediateItem = new BookingArgDTO.BookingArgItem();
|
||||
immediateItem.setTime_title("立即送出");
|
||||
immediateItem.setBooking_at(0L);
|
||||
immediateItem.setBooking_state(1);
|
||||
immediateItem.setBooking_begin_time("");
|
||||
immediateItem.setBooking_end_time("");
|
||||
items.add(immediateItem);
|
||||
// 参数校验
|
||||
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<BookingArgDTO.BookingArgItem> timeSlots = generateTimeSlots(dateStr, startTimeStr, endTimeStr, i == 0);
|
||||
if (i == 0) {
|
||||
// 对于今天,移除除"立即送出"外的所有时间槽
|
||||
items.addAll(timeSlots.stream().filter(item -> item.getBooking_state() != 1).collect(Collectors.toList()));
|
||||
} else {
|
||||
// 对于其他日期,添加所有时间槽
|
||||
items.addAll(timeSlots);
|
||||
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<BookingArgDTO.BookingArgItem> 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<BookingArgDTO.BookingArgItem> 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<map{startTimeStr, endTimeStr}> 列表list
|
||||
* 根据一个或多个店铺id获取有效店铺的有效营业时间段
|
||||
* <p>
|
||||
* 根据 storeIds(一个或多个 storeid,如 34,23,43,23,),先对id去重,
|
||||
* 再获取多个店铺的营业时间 List<map{startTimeStr, endTimeStr}> 列表list
|
||||
* 启动如果遇到某个店铺是开业(活动)筹备中的,获取他的开始营业的的日期和营业时间段组合出特有营业时间段
|
||||
*
|
||||
* @param storeIds 以逗号分隔的店铺ID字符串
|
||||
* @return 包含店铺营业时间信息的列表,每个元素为包含opening_hours和close_hours的Map
|
||||
*/
|
||||
private List<Map<String, String>> selStoreBizTimeMapList(String storeIds) {
|
||||
private List<Pair<String, String>> selectMulStoreBizTimeRanges(String storeIds) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(storeIds)) {
|
||||
return Collections.emptyList();
|
||||
@ -1246,10 +1262,21 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 3. 批量获取店铺信息
|
||||
// 3. 先获取符合条件的 store_id 列表,关联shop_store_base表,要求store_is_open=1(开启)且store_biz_state!=2(非关闭状态)
|
||||
QueryWrapper<ShopStoreBase> 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<Serializable> validStoreIds = shopStoreBaseService.findKey(storeBaseQueryWrapper);
|
||||
|
||||
if (validStoreIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
QueryWrapper<ShopStoreInfo> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("store_opening_hours", "store_close_hours"); // 只查询必要字段
|
||||
queryWrapper.in("store_id", storeIds);
|
||||
queryWrapper.in("store_id", validStoreIds);
|
||||
|
||||
List<ShopStoreInfo> shopStoreInfos = shopStoreInfoService.find(queryWrapper);
|
||||
|
||||
@ -1257,9 +1284,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
return shopStoreInfos.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(storeInfo -> {
|
||||
Map<String, String> timeSlot = new HashMap<>();
|
||||
timeSlot.put("startTimeStr", storeInfo.getStore_opening_hours());
|
||||
timeSlot.put("endTimeStr", storeInfo.getStore_close_hours());
|
||||
Pair<String, String> timeSlot = Pair.of(storeInfo.getStore_opening_hours(), storeInfo.getStore_close_hours());
|
||||
return timeSlot;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
@ -478,13 +478,13 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
|
||||
BigDecimal item_unit_points = Convert.toBigDecimal(productItemMap.get("item_unit_points"));
|
||||
BigDecimal item_unit_price = Convert.toBigDecimal(productItemMap.get("item_unit_price"));
|
||||
Integer item_quantity = Convert.toInt(productItemMap.get("item_quantity"));
|
||||
String is_open_automatic=Convert.toStr(productItemMap.get("is_open_automatic"),DicEnum.YESORNO_0.getCode());
|
||||
if(is_open_automatic.equals(DicEnum.YESORNO_1.getCode())){
|
||||
Integer automatic=Convert.toInt(productItemMap.get("automatic"),0);
|
||||
if(automatic<item_quantity){
|
||||
String is_open_automatic = Convert.toStr(productItemMap.get("is_open_automatic"), DicEnum.YESORNO_0.getCode());
|
||||
if (is_open_automatic.equals(DicEnum.YESORNO_1.getCode())) {
|
||||
Integer automatic = Convert.toInt(productItemMap.get("automatic"), 0);
|
||||
if (automatic < item_quantity) {
|
||||
throw new ApiException("次日补全不能小于库存");
|
||||
}
|
||||
if(automatic<1){
|
||||
if (automatic < 1) {
|
||||
throw new ApiException("次日补全必须大于0");
|
||||
}
|
||||
item.setAutomatic(automatic);
|
||||
@ -737,9 +737,9 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
|
||||
String product_image_old = "";
|
||||
if (productId == null) {
|
||||
// 生成商品ID:product_id
|
||||
List<Long> productIds= shopNumberSeqService.batchCreateNextNo("product_id",1);
|
||||
productId=productIds.get(0);
|
||||
// productId = shopNumberSeqService.createNextNo("product_id");
|
||||
List<Long> 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<ShopProductBaseM
|
||||
field_row.setProduct_item_seq_id(product_item_seq_id);
|
||||
|
||||
//这儿应该提前生成编号使用
|
||||
List<Long> itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1);
|
||||
item_id=itemIds.get(0);
|
||||
// item_id = shopNumberSeqService.createNextNo("item_id");
|
||||
List<Long> 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<ShopProductBaseM
|
||||
boolean $flag = false;
|
||||
List $rs_row = new ArrayList();
|
||||
ShopStoreBase $store_row = shopStoreBaseService.get(store_id);
|
||||
List<Long> productIds= shopNumberSeqService.batchCreateNextNo("product_id",1);
|
||||
List<Long> 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<ShopProductBaseM
|
||||
field_row.setProduct_item_seq_id(product_item_seq_id);
|
||||
|
||||
//这儿应该提前生成编号使用
|
||||
List<Long> itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1);
|
||||
item_id=itemIds.get(0);
|
||||
List<Long> 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<ShopProductBaseM
|
||||
|
||||
Integer product_sale_num = Convert.toInt(product_index_row.get("product_sale_num"));
|
||||
|
||||
if (product_base_row != null) {
|
||||
// 获取店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中;
|
||||
Pair<Integer, String> 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<ShopProductBaseM
|
||||
shopProductItemSeq.setProduct_item_seq_id(product_item_seq_id);
|
||||
|
||||
//这儿应该提前生成编号使用
|
||||
// item_id = shopNumberSeqService.createNextNo("item_id");
|
||||
List<Long> itemIds= shopNumberSeqService.batchCreateNextNo("item_id",1);
|
||||
item_id=itemIds.get(0);
|
||||
// item_id = shopNumberSeqService.createNextNo("item_id");
|
||||
List<Long> 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<ShopProductBaseM
|
||||
// shopProductIndexList.get(i).setProduct_unit_points(BigDecimal.ZERO);
|
||||
shopProductIndexList.get(i).setProduct_unit_price_max(base.getProduct_market_price());
|
||||
shopProductIndexList.get(i).setProduct_unit_sp(Convert.toBigDecimal(base.getProduct_unit_sp()));
|
||||
// shopProductIndexList.get(i).setProduct_sale_time(base.getProduct_sale_time().getTime());
|
||||
// shopProductIndexList.get(i).setProduct_sale_time(base.getProduct_sale_time().getTime());
|
||||
shopProductIndexList.get(i).setProduct_verify_id(base.getProduct_verify_id());
|
||||
shopProductIndexList.get(i).setProduct_state_id(base.getProduct_state_id());
|
||||
shopProductIndexList.get(i).setProduct_src_id(existId);
|
||||
@ -5521,8 +5527,8 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
|
||||
// 1. 批量新增
|
||||
if (CollUtil.isNotEmpty(newProducts)) {
|
||||
// 4. 批量生成新商品的ID
|
||||
List<Long> newIds=new ArrayList<>();
|
||||
synchronized (this){
|
||||
List<Long> 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<ShopProductBaseM
|
||||
List<ShopProductInfo> newShopProductInfoList) {
|
||||
List<Serializable> itemIds = new ArrayList<>();
|
||||
if (CollUtil.isEmpty(items)) return itemIds;
|
||||
List<Long> generatedIds =new ArrayList<>();
|
||||
synchronized (this){
|
||||
List<Long> generatedIds = new ArrayList<>();
|
||||
synchronized (this) {
|
||||
generatedIds = shopNumberSeqService.batchCreateNextNo("item_id", items.size());
|
||||
}
|
||||
// Map<String,String> cacheMap=new HashMap<>();
|
||||
@ -6469,99 +6475,99 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
|
||||
|
||||
@Override
|
||||
public List<ShopProductBase> 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<ShopProductIndex> 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<ShopProductIndex> 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<Integer> product_ids= Collections.singletonList(Math.toIntExact(productId));
|
||||
data.put("baseInfo", getProduct(product_ids));
|
||||
List<Map> baseInfo = (List<Map>) data.get("baseInfo");
|
||||
Long productId = shopProductIndexFind.getProduct_id();
|
||||
List<Integer> product_ids = Collections.singletonList(Math.toIntExact(productId));
|
||||
data.put("baseInfo", getProduct(product_ids));
|
||||
List<Map> baseInfo = (List<Map>) 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<ShopProductItem> itemQueryWrapper = new QueryWrapper<>();
|
||||
itemQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductItem> productItems = shopProductItemService.find(itemQueryWrapper);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(productItems)) {
|
||||
for (Map item : baseInfo) {
|
||||
List<ShopProductItem> 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<ShopProductItem> 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<ShopProductImage> imageQueryWrapper = new QueryWrapper<>();
|
||||
imageQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductImage> shopProductImages = shopProductImageService.find(imageQueryWrapper);
|
||||
// shop_product_assist_index
|
||||
QueryWrapper<ShopProductAssistIndex> indexQueryWrapper = new QueryWrapper<>();
|
||||
indexQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductAssistIndex> assistIndexList = assistIndexService.find(indexQueryWrapper);
|
||||
|
||||
HashMap<String, Object> 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<ShopProductItem> itemQueryWrapper = new QueryWrapper<>();
|
||||
itemQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductItem> productItems = shopProductItemService.find(itemQueryWrapper);
|
||||
|
||||
if (CollectionUtil.isNotEmpty(productItems)) {
|
||||
for (Map item : baseInfo) {
|
||||
List<ShopProductItem> 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<ShopProductItem> 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<ShopProductImage> imageQueryWrapper = new QueryWrapper<>();
|
||||
imageQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductImage> shopProductImages = shopProductImageService.find(imageQueryWrapper);
|
||||
// shop_product_assist_index
|
||||
QueryWrapper<ShopProductAssistIndex> indexQueryWrapper = new QueryWrapper<>();
|
||||
indexQueryWrapper.eq("product_id", productId);
|
||||
List<ShopProductAssistIndex> assistIndexList = assistIndexService.find(indexQueryWrapper);
|
||||
|
||||
HashMap<String, Object> 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;
|
||||
}
|
||||
|
||||
@ -487,7 +487,7 @@ public class ShopProductItemServiceImpl extends BaseServiceImpl<ShopProductItemM
|
||||
data.put("product_analytics", analytics_row);
|
||||
|
||||
// 营业时间段,直接影响到 营业状态字段
|
||||
// store_biz_state 店铺营业状态:1-营业中;2-已打烊;
|
||||
// store_biz_state 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中;
|
||||
baseMap.put("store_biz_state", shopStoreBaseService.getStoreBizState(shopStoreBase, shopStoreInfo));
|
||||
|
||||
data.put("store_info", baseMap);
|
||||
@ -2283,7 +2283,7 @@ public class ShopProductItemServiceImpl extends BaseServiceImpl<ShopProductItemM
|
||||
queryWrapper.or(q -> 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<ShopProductItem> shopProductItems = this.list(queryWrapper);
|
||||
// Map map=shopProductItems.stream().collect(Collectors.toMap(ShopProductItem::getProduct_id,shopProductItem->shopProductItem.getMergedItemId()
|
||||
// +"_"+shopProductItem.getMergedUnitPrices()));
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -63,11 +63,21 @@ public interface ShopStoreActivityBaseService extends IBaseService<ShopStoreActi
|
||||
/**
|
||||
* 验证活动时间是否有效
|
||||
*
|
||||
* @param starTime 活动开始时间
|
||||
* @param endTime 活动结束时间
|
||||
* @param shopStoreActivityBase
|
||||
* @param checkTime
|
||||
* @return
|
||||
*/
|
||||
boolean isActivityTimeValid(ShopStoreActivityBase shopStoreActivityBase, Date checkTime);
|
||||
|
||||
/**
|
||||
* 验证活动时间是否有效
|
||||
*
|
||||
* @param starTime 活动开始时间
|
||||
* @param endTime 活动结束时间
|
||||
* @param checkTime 待验证时间
|
||||
* @return 时间是否有效
|
||||
*/
|
||||
boolean isActivityTimeValid(Date starTime, Date endTime);
|
||||
boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime);
|
||||
|
||||
Map listsMarketing();
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import org.springframework.data.util.Pair;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -204,7 +205,7 @@ public interface ShopStoreBaseService extends IBaseService<ShopStoreBase> {
|
||||
* 修改店铺的营业状态
|
||||
*
|
||||
* @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<ShopStoreBase> {
|
||||
*
|
||||
* @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<ShopStoreBase> {
|
||||
*/
|
||||
Pair<Integer, String> getStoreBizState(Integer storeId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据一个或多个店铺Id,获取开业(活动)筹备中店铺的营业日期最晚的那个日期
|
||||
*
|
||||
* @param storeIds 店铺ID列表,多个ID用英文逗号分隔
|
||||
* @return 最晚的营业开始日期,格式为yyyy-MM-dd
|
||||
*/
|
||||
Date getLatestBizOpeningDate(String storeIds);
|
||||
|
||||
// Page<ShopStoreBase> getMobileStoreList(Integer page, Integer rows);
|
||||
|
||||
}
|
||||
|
||||
@ -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<ShopStoreA
|
||||
@Autowired
|
||||
private ShopStoreActivityItemService shopStoreActivityItemService;
|
||||
@Autowired
|
||||
private ShopBaseActivityTypeService baseActivityTypeService;
|
||||
@Autowired
|
||||
private ShopStoreBaseService shopStoreBaseService;
|
||||
@Autowired
|
||||
private ShopProductIndexService shopProductIndexService;
|
||||
@ -116,8 +113,6 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
@Autowired
|
||||
private ShopProductImageService shopProductImageService;
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
@Autowired
|
||||
private ShopActivityMarketingHistoryService shopActivityMarketingHistoryService;
|
||||
@Autowired
|
||||
private UserInfoService userInfoService;
|
||||
@ -2082,18 +2077,41 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
/**
|
||||
* 验证活动时间是否有效
|
||||
*
|
||||
* @param starTime 活动开始时间
|
||||
* @param endTime 活动结束时间
|
||||
* @param shopStoreActivityBase
|
||||
* @param checkTime
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean isActivityTimeValid(ShopStoreActivityBase shopStoreActivityBase, Date checkTime) {
|
||||
if (shopStoreActivityBase == null || checkTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Date starTime = shopStoreActivityBase.getActivity_starttime();
|
||||
Date endTime = shopStoreActivityBase.getActivity_endtime();
|
||||
|
||||
return isActivityTimeValid(starTime, endTime, checkTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证活动时间是否有效
|
||||
*
|
||||
* @param starTime 活动开始时间
|
||||
* @param endTime 活动结束时间
|
||||
* @param checkTime 待验证时间
|
||||
* @return 时间是否有效
|
||||
*/
|
||||
@Override
|
||||
public boolean isActivityTimeValid(Date starTime, Date endTime) {
|
||||
public boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime) {
|
||||
if (starTime == null || endTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Date now = new Date();
|
||||
return !now.before(starTime) && !now.after(endTime);
|
||||
if (checkTime == null) {
|
||||
checkTime = new Date();
|
||||
}
|
||||
|
||||
return !checkTime.before(starTime) && !checkTime.after(endTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -3658,7 +3676,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
shopStoreActivityBase.setActivity_rule(Convert.toStr(activity_rule));
|
||||
|
||||
} else if (base.getActivity_type_id().equals(StateCode.ACTIVITY_TYPE_CUTPRICE)) {
|
||||
|
||||
// 砍价活动
|
||||
activity_rule.put("cut_down_fixed_price", Convert.toBigDecimal(getParameter("cut_down_fixed_price")));// 固定砍价价格
|
||||
activity_rule.put("cut_down_max_price", Convert.toBigDecimal(getParameter("cut_down_max_price")));// 砍价最高范围
|
||||
activity_rule.put("cut_down_min_price", Convert.toBigDecimal(getParameter("cut_down_min_price"))); // 砍价最低范围
|
||||
@ -3668,10 +3686,23 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
activity_rule.put("product_item_name", getParameter("product_item_name"));// 产品名称
|
||||
activity_rule.put("item_id", getParameter("item_id"));// 产品ID
|
||||
activity_rule.put("activity_intro", getParameter("activity_intro"));
|
||||
|
||||
ShopProductItem item_row = shopProductItemService.get(Convert.toInt(activity_rule.get("item_id")));
|
||||
ShopProductBase product_row = shopProductBaseService.get(item_row.getProduct_id());
|
||||
activity_rule.put("product_image", product_row.getProduct_image());
|
||||
activity_rule.put("item_unit_price", item_row.getItem_unit_price());
|
||||
|
||||
// cut_hour - 砍价有效期(小时)
|
||||
Integer cut_hour = getParameter("cut_hour", 0);
|
||||
if (cut_hour > 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<ShopStoreA
|
||||
data.put("activity_rule", activity_rule);
|
||||
|
||||
if (ObjectUtil.equal(activity_type_id, StateCode.ACTIVITY_TYPE_VOUCHER)) {
|
||||
//店铺优惠券
|
||||
String voucher_image = getParameter("voucher_image", "");
|
||||
BigDecimal needed = getParameter("voucher_points_needed", BigDecimal.ZERO);
|
||||
BigDecimal subtotal = getParameter("subtotal", BigDecimal.ZERO);
|
||||
@ -5331,25 +5363,26 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
String voucher_startdate = getParameter("voucher_startdate");
|
||||
|
||||
if (!CheckUtil.isDate(voucher_startdate)) {
|
||||
throw new ApiException(I18nUtil._("请输入有效的生效日期"));
|
||||
throw new ApiException(I18nUtil._("请输入有效的开始日期"));
|
||||
}
|
||||
String voucher_enddate = getParameter("voucher_enddate");
|
||||
|
||||
if (!CheckUtil.isDate(voucher_enddate)) {
|
||||
throw new ApiException(I18nUtil._("请输入有效的失效日期"));
|
||||
throw new ApiException(I18nUtil._("请输入有效的截止日期"));
|
||||
}
|
||||
|
||||
Map requirement = new HashMap();
|
||||
Map points = new HashMap();
|
||||
Map buy = new HashMap();
|
||||
|
||||
points.put("needed", needed);
|
||||
buy.put("subtotal", subtotal);
|
||||
|
||||
requirement.put("points", points);
|
||||
requirement.put("buy", buy);
|
||||
activity_rule.put("requirement", requirement);
|
||||
|
||||
points.put("needed", needed);
|
||||
|
||||
buy.put("subtotal", subtotal);
|
||||
|
||||
activity_rule.put("requirement", requirement);
|
||||
activity_rule.put("voucher_image", voucher_image);
|
||||
activity_rule.put("voucher_price", voucher_price);
|
||||
activity_rule.put("voucher_start_date", voucher_startdate);
|
||||
@ -5368,6 +5401,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
buy.put("buy", buy);
|
||||
data.put("requirement", requirement);
|
||||
} else if (ObjectUtil.equal(activity_type_id, StateCode.ACTIVITY_TYPE_MARKETING)) {
|
||||
//市场活动
|
||||
String start_join_time = getParameter("start_join_time");
|
||||
String end_join_time = getParameter("end_join_time");
|
||||
String activity_address = getParameter("activity_address");
|
||||
@ -5394,6 +5428,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
activity_rule.put("activity_detail_intro", activity_detail_intro); // 活动详细规则
|
||||
activity_rule.put("activity_intro", activity_intro); // 活动介绍
|
||||
} else if (ObjectUtil.equal(activity_type_id, StateCode.ACTIVITY_TYPE_LOTTERY)) {
|
||||
//幸运大抽奖
|
||||
String lottery_subtitle = getParameter("lottery_subtitle");
|
||||
Integer lottery_type = getParameter("lottery_type", Integer.class);
|
||||
String lottery_image = getParameter("lottery_image");
|
||||
@ -5517,6 +5552,14 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
|
||||
|
||||
activity_rule.put("product_image", product_row.getProduct_image());
|
||||
activity_rule.put("item_unit_price", item_row.getItem_unit_price());
|
||||
|
||||
// cut_hour - 砍价有效期(小时)
|
||||
Integer cut_hour = getParameter("cut_hour", 72);
|
||||
// product_count - 参与活动商品总数(个)
|
||||
Integer product_count = getParameter("product_count", 3);
|
||||
activity_rule.put("product_count", product_count); // 参与活动的商品总数(个)
|
||||
activity_rule.put("cut_hour", cut_hour); // 砍价的有效期(小时)
|
||||
|
||||
} else if (ObjectUtil.equal(activity_type_id, StateCode.ACTIVITY_TYPE_GIFTBAG)) {
|
||||
//a+b组合套餐
|
||||
String activity_bag_category = getParameter("activity_bag_category");//礼包分类
|
||||
|
||||
@ -312,8 +312,12 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
row.put("item_id", Convert.toList(Long.class, row.get("item_id")));
|
||||
row.put("store_latitude", Convert.toDouble(row.get("store_latitude")));
|
||||
row.put("store_longitude", Convert.toDouble(row.get("store_longitude")));
|
||||
}
|
||||
|
||||
// store_biz_state 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中;
|
||||
Pair<Integer, String> 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<ShopStoreBaseMappe
|
||||
store_id = Convert.toInt(user.getStore_id());
|
||||
}
|
||||
|
||||
if (ObjectUtil.isEmpty(store_id)) {
|
||||
if (CheckUtil.isEmpty(store_id)) {
|
||||
logger.warn("店铺Id:{} 空值,无法获取店铺数据!", store_id);
|
||||
return new HashMap();
|
||||
}
|
||||
@ -2370,9 +2374,16 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
base.setSubsite_id(subsite_id);
|
||||
}
|
||||
|
||||
// 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中;
|
||||
Integer storeBizState = Convert.toInt(getParameter("store_biz_state"));
|
||||
if (storeBizState != null && storeBizState <= 2 && storeBizState >= 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<ShopStoreBaseMappe
|
||||
}
|
||||
}
|
||||
|
||||
// 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中;
|
||||
Integer storeBizState = Convert.toInt(getParameter("store_biz_state"));
|
||||
if (storeBizState != null && storeBizState <= 2 && storeBizState >= 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<ShopStoreBaseMappe
|
||||
*
|
||||
* @param shopStoreBase 店铺基础信息
|
||||
* @param shopStoreInfo 店铺详细信息
|
||||
* @return 店铺营业状态:1-营业中;2-已打烊;
|
||||
* @return 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中;
|
||||
*/
|
||||
@Override
|
||||
public Integer getStoreBizState(ShopStoreBase shopStoreBase, ShopStoreInfo shopStoreInfo) {
|
||||
// 参数校验
|
||||
if (shopStoreBase == null || shopStoreInfo == null) {
|
||||
log.warn("店铺基础信息或详细信息为空,无法确定营业状态");
|
||||
return CommonConstant.Disable2;
|
||||
log.warn("店铺信息为空,未知营业状态");
|
||||
return CommonConstant.Store_Biz_State_Closed;
|
||||
}
|
||||
|
||||
try {
|
||||
@ -4217,21 +4235,25 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
// 检查店铺是否营业中且营业时间已设置
|
||||
if (CommonConstant.Enable.equals(storeBizState) && !StrUtil.hasBlank(openingHours, closingHours)) {
|
||||
// 检查当前时间是否在营业时间内
|
||||
if (!DateTimeUtils.isCurrentTimeInRange(openingHours, closingHours)) {
|
||||
// 不在营业时间内,返回已打烊状态
|
||||
return CommonConstant.Disable2;
|
||||
if (DateTimeUtils.isCurrentTimeInRange(openingHours, closingHours) != 0) {
|
||||
// 12-开业打烊中(但在营业时间外)
|
||||
return CommonConstant.Store_Biz_State_Opening2;
|
||||
}
|
||||
|
||||
return CommonConstant.Enable;
|
||||
return CommonConstant.Store_Biz_State_Opening;
|
||||
}
|
||||
|
||||
// 返回原始营业状态
|
||||
Integer resultState = storeBizState != null ? storeBizState : CommonConstant.Store_Biz_State_Closed;
|
||||
log.debug("返回店铺营业状态,storeId: {}, state: {}", shopStoreBase.getStore_id(), resultState);
|
||||
|
||||
// 返回原始营业状态
|
||||
return storeBizState;
|
||||
} catch (Exception e) {
|
||||
// 处理异常,避免影响主流程
|
||||
log.error("检查店铺营业状态时发生异常,storeId: {}",
|
||||
shopStoreBase != null ? shopStoreBase.getStore_id() : "unknown", e);
|
||||
return CommonConstant.Disable2;
|
||||
return CommonConstant.Store_Biz_State_Closed;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4239,21 +4261,21 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
* 根据店铺ID获取营业状态
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @return 营业状态:1-营业中;2-已打烊;
|
||||
* @return 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中;
|
||||
*/
|
||||
@Override
|
||||
public Pair<Integer, String> 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<ShopStoreBaseMappe
|
||||
String closingHours = storeBizTimeInfo.getStore_close_hours();
|
||||
|
||||
// 检查店铺是否营业中且营业时间已设置
|
||||
if (CommonConstant.Enable.equals(storeBizState)
|
||||
if (CommonConstant.Store_Biz_State_Opening.equals(storeBizState)
|
||||
&& StrUtil.isNotBlank(openingHours)
|
||||
&& StrUtil.isNotBlank(closingHours)) {
|
||||
// 检查当前时间是否在营业时间内
|
||||
if (!DateTimeUtils.isCurrentTimeInRange(openingHours, closingHours)) {
|
||||
if (DateTimeUtils.isCurrentTimeInRange(openingHours, closingHours) != 0) {
|
||||
// 不在营业时间内,返回已打烊状态
|
||||
log.debug("店铺当前不在营业时间内,storeId: {}, openingHours: {}, closingHours: {}",
|
||||
storeId, openingHours, closingHours);
|
||||
return Pair.of(CommonConstant.Disable2, String.format("%s营业时间段%s-%s", storeBizTimeInfo.getStore_name(),
|
||||
return Pair.of(CommonConstant.Store_Biz_State_Opening2, String.format("%s营业时间%s-%s", storeBizTimeInfo.getStore_name(),
|
||||
openingHours, closingHours));
|
||||
}
|
||||
|
||||
return Pair.of(CommonConstant.Enable, "");
|
||||
return Pair.of(CommonConstant.Store_Biz_State_Opening, "");
|
||||
}
|
||||
|
||||
// 返回原始营业状态(处理null情况)
|
||||
Integer resultState = storeBizState != null ? storeBizState : CommonConstant.Disable2;
|
||||
// 返回原始营业状态
|
||||
Integer resultState = storeBizState != null ? storeBizState : CommonConstant.Store_Biz_State_Closed;
|
||||
log.debug("返回店铺营业状态,storeId: {}, state: {}", storeId, resultState);
|
||||
if (resultState == CommonConstant.Disable2) {
|
||||
return Pair.of(resultState, String.format("%s打烊中", storeBizTimeInfo.getStore_name()));
|
||||
if (CommonConstant.Store_Biz_State_Closed.equals(resultState)) {
|
||||
return Pair.of(resultState, String.format("%s歇业中", storeBizTimeInfo.getStore_name()));
|
||||
}
|
||||
|
||||
// 返回原始营业状态
|
||||
return Pair.of(resultState, "");
|
||||
|
||||
} catch (Exception e) {
|
||||
// 处理异常,避免影响主流程
|
||||
log.error("检查店铺营业状态发生异常,storeId: {}", storeId, e);
|
||||
return Pair.of(CommonConstant.Disable2, "无法获取店铺营业状态");
|
||||
return Pair.of(CommonConstant.Store_Biz_State_Closed, "无法获取店铺营业状态");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新店铺分账比例
|
||||
*
|
||||
@ -4334,6 +4357,54 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据一个或多个店铺Id,获取开业(活动)筹备中店铺的营业日期最晚的那个日期
|
||||
*
|
||||
* @param storeIds 店铺ID列表,多个ID用英文逗号分隔
|
||||
* @return 最晚的营业开始日期,格式为yyyy-MM-dd
|
||||
*/
|
||||
@Override
|
||||
public Date getLatestBizOpeningDate(String storeIds) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(storeIds)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 解析并去重店铺ID
|
||||
List<Integer> uniqueStoreIds = Arrays.stream(storeIds.split(","))
|
||||
.map(String::trim)
|
||||
.filter(StrUtil::isNotBlank)
|
||||
.map(Integer::valueOf)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 2. 如果没有有效的店铺ID,返回null
|
||||
if (uniqueStoreIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. 查询开业筹备中店铺的最晚营业开始日期
|
||||
QueryWrapper<ShopStoreBase> 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<ShopStoreBase> getMobileStoreList(Integer page, Integer rows) {
|
||||
// QueryWrapper<ShopStoreBase> queryWrapper=new QueryWrapper<>();
|
||||
|
||||
@ -1052,9 +1052,10 @@ public class ShopUserCartServiceImpl extends BaseServiceImpl<ShopUserCartMapper,
|
||||
|
||||
// 店铺Id
|
||||
Integer storeId = Convert.toInt(product_row.get("store_id"));
|
||||
// 判断店铺是否打烊?打烊不能放入购物车
|
||||
|
||||
// 判断店铺是否歇业(停业)?歇业不能放入购物车
|
||||
Pair<Integer, String> 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() + ",无法加购商品。"));
|
||||
}
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ logging:
|
||||
com:
|
||||
suisung:
|
||||
mall:
|
||||
shop: debug
|
||||
shop: info
|
||||
sun:
|
||||
mail: error
|
||||
baomidou: error
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.suisung.mall.shop.lakala.mapper.LklReceiveNotifyLogMapper">
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
*
|
||||
</sql>
|
||||
</mapper>
|
||||
@ -3,10 +3,6 @@
|
||||
<mapper namespace="com.suisung.mall.shop.store.mapper.ShopStoreActivityBaseMapper">
|
||||
|
||||
<!-- 通用查询结果列 -->
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
</sql>
|
||||
<sql id="Base_Column_List">*</sql>
|
||||
|
||||
</mapper>
|
||||
|
||||
@ -5,8 +5,8 @@
|
||||
<sql id="Base_Column_List">
|
||||
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
|
||||
|
||||
@ -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();
|
||||
|
||||
File diff suppressed because one or more lines are too long
40
pom.xml
40
pom.xml
@ -308,6 +308,7 @@
|
||||
<docker.host>https://114.132.210.208:2375</docker.host>
|
||||
<docker.registry>10.1.8.3:5000</docker.registry>
|
||||
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
|
||||
<docker.remove_old_image>false</docker.remove_old_image>
|
||||
<!-- nacos配置 -->
|
||||
<nacos.server.address>114.132.210.208:8848</nacos.server.address>
|
||||
<nacos.namespace>public</nacos.namespace>
|
||||
@ -366,6 +367,7 @@
|
||||
<docker.host>https://114.132.210.208:2375</docker.host>
|
||||
<docker.registry>10.1.8.3:5000</docker.registry>
|
||||
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
|
||||
<docker.remove_old_image>false</docker.remove_old_image>
|
||||
<!-- nacos配置 -->
|
||||
<nacos.server.address>114.132.210.208:8848</nacos.server.address>
|
||||
<nacos.namespace>public</nacos.namespace>
|
||||
@ -418,6 +420,7 @@
|
||||
<docker.host>https://114.132.210.208:2375</docker.host>
|
||||
<docker.registry>10.1.8.3:5000</docker.registry>
|
||||
<docker.ca>/Users/panjunjie/code/docker_registry_ca_dev</docker.ca>
|
||||
<docker.remove_old_image>false</docker.remove_old_image>
|
||||
<!-- nacos配置 -->
|
||||
<nacos.server.address>10.1.8.3:8848</nacos.server.address>
|
||||
<nacos.namespace>public</nacos.namespace>
|
||||
@ -470,6 +473,7 @@
|
||||
<docker.host>https://159.75.249.163:2275</docker.host>
|
||||
<docker.registry>172.16.0.11:5000</docker.registry>
|
||||
<docker.ca>/Users/panjunjie/code/docker_registry_ca_prod</docker.ca>
|
||||
<docker.remove_old_image>true</docker.remove_old_image>
|
||||
<!-- nacos配置 -->
|
||||
<nacos.server.address>172.16.0.11:8848</nacos.server.address>
|
||||
<nacos.namespace>public</nacos.namespace>
|
||||
@ -546,6 +550,20 @@
|
||||
<version>${docker.maven.plugin.version}</version>
|
||||
<!--如果想在项目打包时构建镜像添加-->
|
||||
<executions>
|
||||
<!-- 先删除已存在的镜像 -->
|
||||
<execution>
|
||||
<id>remove-old-image</id>
|
||||
<phase>clean</phase>
|
||||
<goals>
|
||||
<goal>removeImage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<imageName>${docker.registry}/mall/${project.artifactId}:${project.version}</imageName>
|
||||
</configuration>
|
||||
<!-- 通过profile属性控制是否执行 -->
|
||||
<inherited>${docker.remove_old_image}</inherited>
|
||||
</execution>
|
||||
|
||||
<execution>
|
||||
<id>build-image</id>
|
||||
<phase>package</phase>
|
||||
@ -556,18 +574,13 @@
|
||||
</executions>
|
||||
<configuration>
|
||||
<!--定义镜像名称-->
|
||||
<!-- <imageName>mall/${project.artifactId}:${project.version}</imageName>-->
|
||||
<!-- <!– Docker 远程管理地址–>-->
|
||||
<!-- <dockerHost>${docker.host}</dockerHost>-->
|
||||
|
||||
<imageName>${docker.registry}/mall/${project.artifactId}:${project.version}</imageName>
|
||||
<!-- Docker 远程管理地址-->
|
||||
<dockerHost>${docker.host}</dockerHost>
|
||||
|
||||
<!--推送镜像仓库校验安全证书,无安全证书无法推送-->
|
||||
<dockerCertPath>${docker.ca}</dockerCertPath>
|
||||
<!-- 打包镜像到 docker registry 中心添加认证配置(账号密码在 maven 配置) -->
|
||||
<pushImage>true</pushImage>
|
||||
<!--推送镜像仓库校验安全证书,无安全证书无法推送-->
|
||||
<dockerCertPath>${docker.ca}</dockerCertPath>
|
||||
|
||||
<!--定义基础镜像-->
|
||||
<!-- <baseImage>java:8</baseImage>-->
|
||||
@ -576,14 +589,21 @@
|
||||
<!-- 打包镜像到 docker registry 中心添加认证配置(账号密码在 maven 配置) -->
|
||||
<serverId>docker-registry</serverId>
|
||||
|
||||
<!-- 强制覆盖配置 -->
|
||||
<forceTags>true</forceTags>
|
||||
<imageTags>
|
||||
<imageTag>${project.version}</imageTag>
|
||||
<!-- <imageTag>latest</imageTag>-->
|
||||
</imageTags>
|
||||
|
||||
<!--定义容器启动命令,注意不能换行-->
|
||||
<entryPoint>["sh", "-c", "mkdir -p /tmp /app/temp /root/nacos/naming/public && chmod -R 777 /tmp /app/temp /root/nacos && java -Djava.io.tmpdir=/app/temp -Dnacos.naming.cache.dir=/root/nacos/naming -jar -Xms256m -Xmx512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseContainerSupport -XX:MaxRAMPercentage=60.0 -XX:+UseSerialGC -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -Dspring.profiles.active=${spring.profile} -Duser.timezone=Asia/Shanghai /${project.build.finalName}.jar"]
|
||||
</entryPoint>
|
||||
|
||||
<!-- 添加额外的Dockerfile指令来清理不必要的文件 -->
|
||||
<runs>
|
||||
<run>rm -rf /root/.m2 && rm -rf /tmp/* && rm -rf /var/cache/*</run>
|
||||
</runs>
|
||||
<!-- <runs>-->
|
||||
<!-- <run>rm -rf /root/.m2 && rm -rf /tmp/* && rm -rf /var/cache/*</run>-->
|
||||
<!-- </runs>-->
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user