diff --git a/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java b/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java index ec853b84..b4613ac9 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java +++ b/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java @@ -15,9 +15,8 @@ public class StateCode { public static final int DELIVERY_TYPE_AIR_FREIGHT = 4; //货运(空运、水运、铁路运输、公路运输) public static final int DELIVERY_TYPE_SELF_PICK_UP = 5; // 自提(运费 0 元) public static final int DELIVERY_TYPE_EXP = 10; // 普通快递 - public static final int DELIVERY_TYPE_IN_STORE_SERVICE = 15; // 店铺配送 - public static final int DELIVERY_TYPE_SAME_CITY = 16;//顺丰同城配送 - public static final int DELIVERY_TYPE_STORE_BY_SELF = 17; // 商家(线下)自己配送 + public static final int DELIVERY_TYPE_IN_STORE_SERVICE = 15; // 店铺配送(运费 0 元) + public static final int DELIVERY_TYPE_SAME_CITY = 16;//同城配送 public static final Map DELIVERY_TYPE_MAP = new HashMap() { { @@ -28,7 +27,7 @@ public class StateCode { put(DELIVERY_TYPE_SELF_PICK_UP, "到店自提"); put(DELIVERY_TYPE_EXP, "普通快递"); put(DELIVERY_TYPE_IN_STORE_SERVICE, "店铺配送"); - put(DELIVERY_TYPE_SAME_CITY, "顺丰同城"); + put(DELIVERY_TYPE_SAME_CITY, "顺丰同城配送"); } }; diff --git a/mall-common/src/main/java/com/suisung/mall/common/constant/MqConstant.java b/mall-common/src/main/java/com/suisung/mall/common/constant/MqConstant.java index f0ff94fb..08b49df9 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/constant/MqConstant.java +++ b/mall-common/src/main/java/com/suisung/mall/common/constant/MqConstant.java @@ -14,6 +14,7 @@ public class MqConstant { public static final Integer FAILURE = 2; // 消息消费失败更改状态 public static final Integer DELIVERED = 3; // 消息重试消费成功更改状态 public static final Integer MAX_COUNT = 3; // 消息重新投递最大重试次数 + public static final String SHOP_EXCHANGE = "shop-event-exchange"; // SHOP服务交换机 public static final String ACCOUNT_EXCHANGE = "account-event-exchange"; // ACCOUNT服务交换机 public static final String PAY_EXCHANGE = "pay-event-exchange"; // PAY服务交换机 diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/library/LibraryProduct.java b/mall-common/src/main/java/com/suisung/mall/common/modules/library/LibraryProduct.java index a048a247..8824bbff 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/library/LibraryProduct.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/library/LibraryProduct.java @@ -29,6 +29,9 @@ public class LibraryProduct implements Serializable { @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String name; + @ApiModelProperty(value = "商品简称,作为匹配关键字", example = "小米12 Pro") + private String sname; + @ApiModelProperty(value = "商品标题", example = "小米12 Pro") @TableField(updateStrategy = FieldStrategy.NOT_EMPTY) private String title; @@ -66,7 +69,6 @@ public class LibraryProduct implements Serializable { @ApiModelProperty(value = "商品卖点", example = "绿色有机") private String selling_point; - @ApiModelProperty(value = "商品介绍", example = "商品介绍") private String intro; diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/page/ShopPageBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/page/ShopPageBase.java index f061060c..5075d865 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/page/ShopPageBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/page/ShopPageBase.java @@ -10,6 +10,7 @@ import lombok.EqualsAndHashCode; import lombok.experimental.Accessors; import java.io.Serializable; +import java.util.Date; /** *

@@ -135,5 +136,9 @@ public class ShopPageBase implements Serializable { @ApiModelProperty(value = "信息发布(BOOL):0-否;1-是") private Integer page_message; + @ApiModelProperty(value = "创建时间") + private Date created_at; + @ApiModelProperty(value = "最后更新时间") + private Date updated_at; } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreSameCityTransportBase.java b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreSameCityTransportBase.java index 00c097b6..8204ee6e 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreSameCityTransportBase.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/store/ShopStoreSameCityTransportBase.java @@ -43,6 +43,9 @@ public class ShopStoreSameCityTransportBase implements Serializable { @TableId(value = "transport_base_id", type = IdType.AUTO) private Long transport_base_id; + @ApiModelProperty(value = "平台配送费设置?1-是;2-否") + private Integer is_platform; + @ApiModelProperty(value = "店铺ID") private Long store_id; diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java index be215f5f..fe328f22 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java @@ -34,9 +34,9 @@ import java.math.RoundingMode; public class LklSeparateWithTotalAmountDTO { // 商家最低分账比例阈值 20% - private static final BigDecimal MCH_RATIO_THRESHOLD = BigDecimal.valueOf(0.2); + private static final BigDecimal MIN_MERCHANT_RATIO_THRESHOLD = BigDecimal.valueOf(0.2); // 默认平台比例 1% - private static final BigDecimal DEFAULT_PLAT_RATIO = BigDecimal.valueOf(0.01); + private static final BigDecimal DEFAULT_PLATFORM_RATIO = BigDecimal.valueOf(0.01); // 基础金额属性 private Integer totalSeparateAmount; // 分账总金额(分) @@ -206,7 +206,16 @@ public class LklSeparateWithTotalAmountDTO { return SeparateResult.failure(errorMsg); } - // 6. 计算商家实际分账比例 + // 6. 根据指定规则调整各参与方分账金额 + Pair adjustResult = adjustAmountsWithLklAmount(); + if (!adjustResult.getFirst()) { + String errorMsg = "分账计算参数异常: " + adjustResult.getSecond(); + log.error(errorMsg); + this.errMsg = errorMsg; + return SeparateResult.failure(errorMsg); + } + + // 7. 计算商家实际分账比例 Pair mchResult = calculateActualMchRatio(); if (!mchResult.getFirst()) { String errorMsg = "分账计算参数异常: " + mchResult.getSecond(); @@ -322,7 +331,7 @@ public class LklSeparateWithTotalAmountDTO { private Pair calculateDefaultRatios() { // 如果平台比例无效,设置默认值0.01 if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) { - platRatio = DEFAULT_PLAT_RATIO; + platRatio = DEFAULT_PLATFORM_RATIO; } return Pair.of(true, ""); @@ -422,6 +431,53 @@ public class LklSeparateWithTotalAmountDTO { return Pair.of(true, ""); } + /** + * 根据指定规则调整各参与方分账金额 + * 如果拉卡拉分账金额lklAmount>0: + * 1. 如果agent2ndAmount>0 且大于lklAmount, 则agent2ndAmount=agent2ndAmount-lklAmount,mchAmount=mchAmount+lklAmount + * 2. 如果agent2ndAmount<=0 但 agent1stAmount>0 且大于lklAmount, 则agent1stAmount=agent1stAmount-lklAmount,mchAmount=mchAmount+lklAmount + * 3. 如果agent2ndAmount<=0 且 agent1stAmount<=0,但 platAmount>0 且大于等于lklAmount, 则 platAmount=platAmount-lklAmount,mchAmount=mchAmount+lklAmount + * + * @return Pair Boolean表示是否成功,String为错误信息 + */ + private Pair adjustAmountsWithLklAmount() { + try { + // 只有当拉卡拉分账金额大于0时才执行调整逻辑 + if (lklAmount != null && lklAmount > 0) { + int adjustment = lklAmount; // 需要调整的金额 + + // 情况1: 如果agent2ndAmount>0 且大于lklAmount + if (agent2ndAmount != null && agent2ndAmount > 0 && agent2ndAmount > adjustment) { + agent2ndAmount -= adjustment; + mchAmount += adjustment; + log.debug("调整二级代理商分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment); + } + // 情况2: 如果agent2ndAmount<=0 但 agent1stAmount>0 且大于lklAmount + else if ((agent2ndAmount == null || agent2ndAmount <= 0) && + agent1stAmount != null && agent1stAmount > 0 && agent1stAmount > adjustment) { + agent1stAmount -= adjustment; + mchAmount += adjustment; + log.debug("调整一级代理商分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment); + } + // 情况3: 如果agent2ndAmount<=0 且 agent1stAmount<=0,但 platAmount>0 且大于等于lklAmount + else if ((agent2ndAmount == null || agent2ndAmount <= 0) && + (agent1stAmount == null || agent1stAmount <= 0) && + platAmount != null && platAmount > 0 && platAmount >= adjustment) { + platAmount -= adjustment; + mchAmount += adjustment; + log.debug("调整平台分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment); + } + // 其他情况不作调整 + else { + log.debug("不满足调整条件,无需调整各参与方分账金额"); + } + } + + return Pair.of(true, ""); + } catch (Exception e) { + return Pair.of(false, "调整分账金额时发生异常: " + e.getMessage()); + } + } /** * 计算商家实际分账比例 @@ -435,9 +491,9 @@ public class LklSeparateWithTotalAmountDTO { .divide(BigDecimal.valueOf(totalSeparateAmount), 6, RoundingMode.HALF_UP); // 如果计算出的实际比例低于阈值,打印日志并返回错误 - if (mchRatio.compareTo(MCH_RATIO_THRESHOLD) < 0) { + if (mchRatio.compareTo(MIN_MERCHANT_RATIO_THRESHOLD) < 0) { String errorMsg = String.format("警告: 商家实际分账比例低于阈值,当前比例: %s,阈值: %s", - mchRatio.toPlainString(), MCH_RATIO_THRESHOLD.toPlainString()); + mchRatio.toPlainString(), MIN_MERCHANT_RATIO_THRESHOLD.toPlainString()); log.warn(errorMsg); return Pair.of(false, errorMsg); } @@ -611,4 +667,4 @@ public class LklSeparateWithTotalAmountDTO { return new SeparateResult(Boolean.TRUE, data, errMsg != null ? errMsg : ""); } } -} +} \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderProductPrintVO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderItemPrintVO.java similarity index 89% rename from mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderProductPrintVO.java rename to mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderItemPrintVO.java index 3bce33ac..bf565b03 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderProductPrintVO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderItemPrintVO.java @@ -16,13 +16,16 @@ import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; +/** + * 订单商品详情打印对象 + */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) @NoArgsConstructor @AllArgsConstructor -@ApiModel(value = "订单商品打印对象", description = "订单商品打印对象") -public class ShopStoreOrderProductPrintVO implements Serializable { +@ApiModel(value = "订单商品详情打印对象", description = "订单商品详情打印对象") +public class OrderItemPrintVO implements Serializable { private static final long serialVersionUID = 1L; @ApiModelProperty(value = "商品名") private String item_name; @@ -47,11 +50,11 @@ public class ShopStoreOrderProductPrintVO implements Serializable { @ApiModelProperty(value = "金额额定字节长度") private Integer amount_blen; - public ShopStoreOrderProductPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn) { - new ShopStoreOrderProductPrintVO(itemName, orderItemQuantity, orderItemAmount, productSn, 18, 6, 8); + public OrderItemPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn) { + new OrderItemPrintVO(itemName, orderItemQuantity, orderItemAmount, productSn, 18, 6, 8); } - public ShopStoreOrderProductPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn, int titleBlen, int quantityBlen, int amountBlen) { + public OrderItemPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn, int titleBlen, int quantityBlen, int amountBlen) { this.item_name = itemName; this.order_item_quantity = orderItemQuantity; this.order_item_amount = orderItemAmount; @@ -90,8 +93,8 @@ public class ShopStoreOrderProductPrintVO implements Serializable { * * @return */ - public ShopStoreOrderProductPrintVO rebuild() { - return new ShopStoreOrderProductPrintVO(this.item_name, this.order_item_quantity, this.order_item_amount, this.product_sn, 18, 6, 8); + public OrderItemPrintVO rebuild() { + return new OrderItemPrintVO(this.item_name, this.order_item_quantity, this.order_item_amount, this.product_sn, 18, 6, 8); } /** diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderPrintVO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderPrintVO.java new file mode 100644 index 00000000..391929fa --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/OrderPrintVO.java @@ -0,0 +1,145 @@ +package com.suisung.mall.common.pojo.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 退货单打印对象 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Builder +@ApiModel(value = "(退款)订单主信息打印对象", description = "(退款)订单主信息打印对象") +public class OrderPrintVO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "订单编号") + private String order_id; + + @ApiModelProperty(value = "退单编号") + private String return_id; + + @ApiModelProperty(value = "店铺Id") + private Integer store_id; + + @ApiModelProperty(value = "店铺名称") + private String store_name; + + @ApiModelProperty(value = "店铺电话") + private String store_tel; + + @ApiModelProperty(value = "(配送)取单号") + @Builder.Default + private String order_pickup_num_str = "#0000000"; + + @ApiModelProperty(value = "退款订单编号") + private String order_message; + + @ApiModelProperty(value = "支付时间") + private Date payment_time; + + @ApiModelProperty(value = "商品总件数") + private Integer order_items_count; + + @ApiModelProperty(value = "商品总类数") + private Integer order_product_amount; + + @ApiModelProperty(value = "订单配送费") + private BigDecimal order_shipping_fee; + + @ApiModelProperty(value = "订单来源") + @Builder.Default + private String order_channel_name = "微信小程序"; + + @ApiModelProperty(value = "支付方式") + @Builder.Default + private String payment_type_name = "微信支付"; + + @ApiModelProperty(value = "配送方式ID") + @Builder.Default + private Integer delivery_type_id = 16; + + @ApiModelProperty(value = "配送方式") + @Builder.Default + private String delivery_type_name = "顺丰同城"; + + @ApiModelProperty(value = "打包费") + private BigDecimal packing_fee; + + @ApiModelProperty(value = "会员优惠权益金额") + private BigDecimal basic_rights; + + @ApiModelProperty(value = "实付金额") + private BigDecimal order_payment_amount; + + @ApiModelProperty(value = "退款金额") + private BigDecimal return_refund_amount; + + @ApiModelProperty(value = "订单状态(LIST):2011-待订单审核;2013-待财务审核;2020-待配货/待出库审核;2030-待发货;2040-已发货/待收货确认;2060-已完成/已签收;2070-已取消/已作废;") + private String order_state; + + @ApiModelProperty(value = "下单时买家退货留言") + private String return_buyer_message; + + @ApiModelProperty(value = "下单时商家留言") + private String seller_message; + + @ApiModelProperty(value = "买家退货留言") + private String return_store_message; + + @ApiModelProperty(value = "申请退款时间") + private Date return_add_time; + + @ApiModelProperty(value = "退款完成时间") + private Date return_finish_time; + + @ApiModelProperty(value = "预订单状态") + private Integer booking_state; + + @ApiModelProperty(value = "预订单开始时间") + private Date booking_begin_time; + + @ApiModelProperty(value = "退货类型(ENUM): 0-不用退货;1-需要退货") + @Builder.Default + private String return_flag_str = "原路返回"; + + @ApiModelProperty(value = "卖家处理状态(ENUM): 3100-【客户】提交退单;3105-退单审核;3110-收货确认;3115-退款确认;3120-【客户】收款确认;3125-完成退款;3130-商家拒绝退货;3135-买家取消退款") + private String return_state; + + @ApiModelProperty(value = "买方用户名") + private String buyer_user_name; + + @ApiModelProperty(value = "退款人手机号") + private String return_tel; + + @ApiModelProperty(value = "收货联系电话") + private String da_mobile; + + @ApiModelProperty(value = "收货省份") + private String da_province; + + @ApiModelProperty(value = "收货城市") + private String da_city; + + @ApiModelProperty(value = "收货详细地址") + private String da_address; + + @ApiModelProperty(value = "(店长)收银员") + @Builder.Default + private String cashier = "收银员"; + + @ApiModelProperty(value = "订单商品详情打印对象列表") + private List order_items; + + +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderPrintVO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderPrintVO.java index 7f251c49..ebe7aa53 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderPrintVO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/vo/ShopStoreOrderPrintVO.java @@ -68,7 +68,7 @@ public class ShopStoreOrderPrintVO implements Serializable { @ApiModelProperty(value = "支付方式") private String pay_type; - @ApiModelProperty(value = "配送来源") + @ApiModelProperty(value = "配送方式") private String shipper_type; @ApiModelProperty(value = "下单时间") @@ -105,9 +105,8 @@ public class ShopStoreOrderPrintVO implements Serializable { @ApiModelProperty(value = "实际应付款") private Long order_payment_amount; - // 订单商品详情信息 @ApiModelProperty(value = "订单商品详情信息") - private ShopStoreOrderProductPrintVO order_items; + private OrderItemPrintVO order_items; @ApiModelProperty(value = "状态:1-有效;2-无效;") private Integer status; diff --git a/mall-common/src/main/java/com/suisung/mall/common/service/impl/CommonService.java b/mall-common/src/main/java/com/suisung/mall/common/service/impl/CommonService.java index 2ca316e6..47eeb7f2 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/service/impl/CommonService.java +++ b/mall-common/src/main/java/com/suisung/mall/common/service/impl/CommonService.java @@ -17,6 +17,7 @@ import com.suisung.mall.common.constant.CommonConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.Objects; import java.util.regex.Pattern; @Slf4j @@ -76,10 +77,37 @@ public class CommonService { return false; } - // 普通配送是指除了同城配送、自提和到店服务之外的配送方式 - return !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_SAME_CITY) - && !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_SELF_PICK_UP) - && !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_IN_STORE_SERVICE); + // 普通配送是指除了同城配送、自提和到店服务之外的配送方式(没有物流轨迹的) + return !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SAME_CITY) + && !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SELF_PICK_UP) + && !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_IN_STORE_SERVICE); + } + + /** + * 获取配送方式名称 + * + * @param deliveryTypeId 配送方式ID + * @return 配送方式名称 + */ + public static String getDeliveryExpressName(Integer deliveryTypeId) { + if (deliveryTypeId == null) return "其他配送"; + String name = StateCode.DELIVERY_TYPE_MAP.get(deliveryTypeId); + return name != null ? name : "其他配送"; + } + + /** + * 判断配送方式是否为同城配送 + * + * @param deliveryTypeId 配送方式ID + * @return 是否为普通配送 + */ + public static boolean isSameCityExpress(Integer deliveryTypeId) { + // 当配送方式ID为空时,返回false + if (deliveryTypeId == null) { + return false; + } + + return Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SAME_CITY); } diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java index 6f559ab4..6b5dcf29 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/DateTimeUtils.java @@ -11,7 +11,6 @@ import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; -import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -492,7 +491,7 @@ public class DateTimeUtils { return -1; } } - + /** * 判断指定时间是否在两个时间点之间(包含边界) * @@ -602,6 +601,27 @@ public class DateTimeUtils { } } + public static boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime) { + log.debug("检查活动时间有效性 - 开始时间: {}, 结束时间: {}, 检查时间: {}", starTime, endTime, checkTime); + + if (starTime == null || endTime == null) { + log.error("活动时间验证失败 - 开始时间或结束时间为null. 开始时间: {}, 结束时间: {}", starTime, endTime); + return false; + } + + if (checkTime == null) { + checkTime = new Date(); + log.warn("检查时间为空,使用当前时间: {}", checkTime); + } + + boolean isValid = !checkTime.before(starTime) && !checkTime.after(endTime); + log.debug("活动时间验证结果: {} (检查时间>=开始时间, 检查时间<=结束时间)", + isValid); + + return isValid; + } + + public static void main(String[] args) { // System.out.println(convertLklDate("2021-02-19")); // 2025-01-02 // System.out.println(convertLklDate("2021-2-3")); // 2025-01-02 @@ -632,29 +652,29 @@ public class DateTimeUtils { // System.out.println("当前时间是否在工作时间内:" + isWorkTime); // System.out.println("多个时间段的交集结果:" + isNight); - - System.out.println("=== 测试 findTimeIntersection ==="); - - // 测试正常交集情况 - List> timeList1 = new ArrayList<>(); - timeList1.add(Pair.of("06:00", "17:00")); - timeList1.add(Pair.of("10:00", "18:00")); - - Pair intersection1 = findTimeInterSection(timeList1); - System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00 - -// 测试无交集情况 - List> timeList2 = new ArrayList<>(); - timeList2.add(Pair.of("09:00", "12:00")); - timeList2.add(Pair.of("13:00", "17:00")); - - Pair intersection2 = findTimeInterSection(timeList2); - System.out.println("交集结果2: " + intersection2); // 应该是null - -// 测试空列表 - Pair intersection3 = findTimeInterSection(null); - System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null - +// +// System.out.println("=== 测试 findTimeIntersection ==="); +// +// // 测试正常交集情况 +// List> timeList1 = new ArrayList<>(); +// timeList1.add(Pair.of("06:00", "17:00")); +// timeList1.add(Pair.of("10:00", "18:00")); +// +// Pair intersection1 = findTimeInterSection(timeList1); +// System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00 +// +//// 测试无交集情况 +// List> timeList2 = new ArrayList<>(); +// timeList2.add(Pair.of("09:00", "12:00")); +// timeList2.add(Pair.of("13:00", "17:00")); +// +// Pair intersection2 = findTimeInterSection(timeList2); +// System.out.println("交集结果2: " + intersection2); // 应该是null +// +//// 测试空列表 +// Pair intersection3 = findTimeInterSection(null); +// System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null +// } } \ No newline at end of file diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/FreeMakerUtils.java b/mall-common/src/main/java/com/suisung/mall/common/utils/FreeMakerUtils.java index 90877d3c..955df822 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/FreeMakerUtils.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/FreeMakerUtils.java @@ -39,7 +39,7 @@ public class FreeMakerUtils { * @throws IOException * @throws TemplateException */ - public static String processTemplate(Configuration configuration, String templateName, String templateValue, Map binding) { + public static String processTemplate(Configuration configuration, String templateName, String templateValue, Object binding) { StringWriter stringWriter = new StringWriter(); try { Template template = new Template(templateName, templateValue, configuration); @@ -63,4 +63,8 @@ public class FreeMakerUtils { public static String processTemplate(String templateName, String templateValue, Map binding) { return processTemplate(stringTempConfiguration(), templateName, templateValue, binding); } + + public static String processTemplate(String templateName, String templateValue, Object binding) { + return processTemplate(stringTempConfiguration(), templateName, templateValue, binding); + } } diff --git a/mall-common/src/test/com/suisung/mall/common/phone/DistributedLockHelperTest.java b/mall-common/src/test/com/suisung/mall/common/phone/DistributedLockHelperTest.java new file mode 100644 index 00000000..9fc90167 --- /dev/null +++ b/mall-common/src/test/com/suisung/mall/common/phone/DistributedLockHelperTest.java @@ -0,0 +1,349 @@ +package com.suisung.mall.common.utils; + +import com.suisung.mall.core.web.service.RedisService; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +/** + * DistributedLockHelper综合测试套件 + */ +public class DistributedLockHelperTest { + + private DistributedLockHelper lockHelper; + + @Mock + private RedisService redisService; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + lockHelper = new DistributedLockHelper(); + ReflectionTestUtils.setField(lockHelper, "redisService", redisService); + System.out.println("=== 初始化测试环境 ==="); + } + + @Test + public void testTryLock_Success() { + System.out.println("开始执行: testTryLock_Success"); + + // 给定条件 + String lockKey = "test_lock"; + long expireTimeSec = 30L; + String expectedKey = "distributed_lock:" + lockKey; + System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒"); + + // 当调用时 + when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true); + boolean result = lockHelper.tryLock(lockKey, expireTimeSec); + System.out.println("获取锁结果: " + (result ? "成功" : "失败")); + + // 那么验证 + assertTrue(result, "锁应该成功获取"); + verify(redisService).set(eq(expectedKey), anyString(), eq(expireTimeSec)); + System.out.println("验证通过: Redis set 方法被正确调用"); + System.out.println("测试完成: testTryLock_Success\n"); + } + + @Test + public void testTryLock_Failure() { + System.out.println("开始执行: testTryLock_Failure"); + + // 给定条件 + String lockKey = "test_lock"; + long expireTimeSec = 30L; + System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒"); + + // 当调用时 + when(redisService.set(anyString(), anyString(), anyLong())).thenThrow(new RuntimeException("Redis错误")); + System.out.println("模拟Redis异常: Redis错误"); + + // 那么验证 + assertThrows(RuntimeException.class, () -> { + lockHelper.tryLock(lockKey, expireTimeSec); + }, "应该抛出RuntimeException"); + System.out.println("验证通过: 正确捕获了RuntimeException"); + System.out.println("测试完成: testTryLock_Failure\n"); + } + + @Test + public void testReleaseLock_Success() { + System.out.println("开始执行: testReleaseLock_Success"); + + // 给定条件 + String lockKey = "test_lock"; + String expectedKey = "distributed_lock:" + lockKey; + String lockValue = "uuid:1"; + System.out.println("测试参数 - 锁键: " + lockKey + ", 锁值: " + lockValue); + + // 设置线程本地变量 + ReflectionTestUtils.setField(lockHelper, "LOCK_VALUE_HOLDER", + new ThreadLocal() { + @Override + protected String initialValue() { + return lockValue; + } + }); + + when(redisService.get(expectedKey)).thenReturn(lockValue); + System.out.println("模拟Redis get返回值: " + lockValue); + + // 当调用时 + lockHelper.releaseLock(lockKey); + System.out.println("执行锁释放操作"); + + // 那么验证 + verify(redisService).del(expectedKey); + System.out.println("验证通过: Redis del 方法被正确调用"); + System.out.println("测试完成: testReleaseLock_Success\n"); + } + + @Test + public void testReleaseLock_WrongOwner() { + System.out.println("开始执行: testReleaseLock_WrongOwner"); + + // 给定条件 + String lockKey = "test_lock"; + String expectedKey = "distributed_lock:" + lockKey; + String currentValue = "uuid:1"; + String threadValue = "uuid:2"; // 不同的值 + System.out.println("测试参数 - 当前锁值: " + currentValue + ", 线程锁值: " + threadValue); + + // 设置线程本地变量 + ReflectionTestUtils.setField(lockHelper, "LOCK_VALUE_HOLDER", + new ThreadLocal() { + @Override + protected String initialValue() { + return threadValue; + } + }); + + when(redisService.get(expectedKey)).thenReturn(currentValue); + System.out.println("模拟Redis中当前锁值: " + currentValue); + + // 当调用时 + lockHelper.releaseLock(lockKey); + System.out.println("执行锁释放操作"); + + // 那么验证 + verify(redisService, never()).del(anyString()); + System.out.println("验证通过: Redis del 方法未被调用(防止误删)"); + System.out.println("测试完成: testReleaseLock_WrongOwner\n"); + } + + @Test + public void testConcurrentAccess_SingleLockAcquirer() throws InterruptedException { + System.out.println("开始执行: testConcurrentAccess_SingleLockAcquirer"); + + // 给定条件 + String lockKey = "concurrent_test_lock"; + int threadCount = 10; + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + CountDownLatch latch = new CountDownLatch(threadCount); + AtomicInteger successCounter = new AtomicInteger(0); + System.out.println("测试参数 - 并发线程数: " + threadCount + ", 锁键: " + lockKey); + + // 模拟Redis并发访问行为 + when(redisService.set(anyString(), anyString(), anyLong())) + .thenAnswer(invocation -> { + // 只有第一次调用成功,其余都失败 + boolean result = successCounter.get() == 0; + if (result) { + System.out.println("线程[" + Thread.currentThread().getName() + "] 成功获取锁"); + } + return result; + }); + System.out.println("模拟Redis行为: 只允许一个线程获取锁"); + + // 当执行时 + for (int i = 0; i < threadCount; i++) { + final int threadId = i; + executor.submit(() -> { + try { + System.out.println("启动线程[" + threadId + "]"); + if (lockHelper.tryLock(lockKey, 30)) { + int count = successCounter.incrementAndGet(); + System.out.println("线程[" + threadId + "] 获取锁成功,当前成功计数: " + count); + lockHelper.releaseLock(lockKey); + System.out.println("线程[" + threadId + "] 释放锁"); + } else { + System.out.println("线程[" + threadId + "] 获取锁失败"); + } + } finally { + latch.countDown(); + System.out.println("线程[" + threadId + "] 执行完毕,剩余线程数: " + (latch.getCount())); + } + }); + } + + boolean completed = latch.await(5, TimeUnit.SECONDS); + executor.shutdown(); + System.out.println("所有线程执行完成: " + (completed ? "正常结束" : "超时结束")); + + // 那么验证 + int successCount = successCounter.get(); + assertEquals(1, successCount, "应该只有一个线程获得锁"); + System.out.println("验证通过: 实际成功获取锁的线程数为 " + successCount); + System.out.println("测试完成: testConcurrentAccess_SingleLockAcquirer\n"); + } + + @Test + public void testAutoRenewalFunctionality() throws InterruptedException { + System.out.println("开始执行: testAutoRenewalFunctionality"); + + // 给定条件 + String lockKey = "auto_renew_test_lock"; + long expireTimeSec = 5L; + long maxHoldTimeSec = 10L; + System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒, 最大持有时间: " + maxHoldTimeSec + "秒"); + + when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true); + when(redisService.get(anyString())).thenReturn("mock-value"); + System.out.println("模拟Redis set/get操作成功"); + + // 当调用时 + DistributedLockHelper.LockHolder lockHolder = + lockHelper.tryLockWithAutoRenew(lockKey, expireTimeSec, maxHoldTimeSec); + System.out.println("获取带自动续期功能的锁"); + + assertNotNull(lockHolder, "锁持有者不应为空"); + System.out.println("锁持有者创建成功"); + + // 等待可能的续期操作 + System.out.println("等待3秒观察自动续期行为..."); + Thread.sleep(3000); + + // 那么验证 + verify(redisService, atLeastOnce()).expire(anyString(), anyLong()); + System.out.println("验证通过: Redis expire 方法至少被调用一次"); + + // 清理 + lockHolder.close(); + System.out.println("关闭锁持有者,清理资源"); + System.out.println("测试完成: testAutoRenewalFunctionality\n"); + } + + @Test + public void testLockHolderAutoClose() { + System.out.println("开始执行: testLockHolderAutoClose"); + + // 给定条件 + String lockKey = "auto_close_test_lock"; + System.out.println("测试参数 - 锁键: " + lockKey); + + when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true); + when(redisService.get(anyString())).thenReturn("mock-value"); + System.out.println("模拟Redis操作成功"); + + // 当调用时 + System.out.println("使用try-with-resources语法获取锁"); + try (DistributedLockHelper.LockHolder holder = + lockHelper.tryLockWithAutoRenew(lockKey, 30, 300)) { + assertNotNull(holder, "锁持有者不应为空"); + System.out.println("锁持有者创建成功"); + } + System.out.println("try-with-resources块结束,自动调用close方法"); + + // 那么验证 + verify(redisService).del(anyString()); + System.out.println("验证通过: Redis del 方法被正确调用"); + System.out.println("测试完成: testLockHolderAutoClose\n"); + } + + @Test + public void testMultipleLocksIndependence() { + System.out.println("开始执行: testMultipleLocksIndependence"); + + // 给定条件 + String lockKey1 = "lock_1"; + String lockKey2 = "lock_2"; + System.out.println("测试参数 - 锁键1: " + lockKey1 + ", 锁键2: " + lockKey2); + + when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true); + System.out.println("模拟Redis set操作成功"); + + // 当调用时 + boolean lock1Acquired = lockHelper.tryLock(lockKey1, 30); + System.out.println("获取第一个锁: " + (lock1Acquired ? "成功" : "失败")); + + boolean lock2Acquired = lockHelper.tryLock(lockKey2, 30); + System.out.println("获取第二个锁: " + (lock2Acquired ? "成功" : "失败")); + + // 那么验证 + assertTrue(lock1Acquired, "第一个锁应该成功获取"); + assertTrue(lock2Acquired, "第二个锁应该成功获取"); + System.out.println("两个锁都成功获取"); + + // 验证它们是不同的键 + verify(redisService).set(eq("distributed_lock:" + lockKey1), anyString(), eq(30L)); + verify(redisService).set(eq("distributed_lock:" + lockKey2), anyString(), eq(30L)); + System.out.println("验证通过: 两个独立的锁键都被正确设置"); + System.out.println("测试完成: testMultipleLocksIndependence\n"); + } + + @Test + public void testThreadIsolation() throws InterruptedException { + System.out.println("开始执行: testThreadIsolation"); + + // 给定条件 + String lockKey = "thread_isolation_lock"; + ExecutorService executor = Executors.newFixedThreadPool(2); + CountDownLatch latch = new CountDownLatch(2); + AtomicInteger[] results = new AtomicInteger[]{new AtomicInteger(0), new AtomicInteger(0)}; + System.out.println("测试参数 - 锁键: " + lockKey + ", 线程数: 2"); + + when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true); + System.out.println("模拟Redis set操作成功"); + + // 当执行时 + System.out.println("启动两个线程竞争同一个锁"); + for (int i = 0; i < 2; i++) { + final int threadIndex = i; + executor.submit(() -> { + try { + System.out.println("线程[" + threadIndex + "] 尝试获取锁"); + if (lockHelper.tryLock(lockKey, 30)) { + int count = results[threadIndex].incrementAndGet(); + System.out.println("线程[" + threadIndex + "] 获取锁成功,计数: " + count); + // 持有锁一段时间 + Thread.sleep(100); + System.out.println("线程[" + threadIndex + "] 持有锁100ms后释放"); + lockHelper.releaseLock(lockKey); + System.out.println("线程[" + threadIndex + "] 释放锁"); + } else { + System.out.println("线程[" + threadIndex + "] 获取锁失败"); + } + } catch (InterruptedException e) { + System.out.println("线程[" + threadIndex + "] 被中断"); + Thread.currentThread().interrupt(); + } finally { + latch.countDown(); + System.out.println("线程[" + threadIndex + "] 执行完毕"); + } + }); + } + + boolean completed = latch.await(5, TimeUnit.SECONDS); + executor.shutdown(); + System.out.println("所有线程执行完成: " + (completed ? "正常结束" : "超时结束")); + + // 那么验证 - 两个线程都应该能够在不同时间获得锁 + int result0 = results[0].get(); + int result1 = results[1].get(); + assertEquals(1, result0, "线程0应该获得一次锁"); + assertEquals(1, result1, "线程1应该获得一次锁"); + System.out.println("验证通过: 线程0获得锁次数=" + result0 + ", 线程1获得锁次数=" + result1); + System.out.println("测试完成: testThreadIsolation\n"); + } +} diff --git a/mall-gateway/src/main/resources/application.yml b/mall-gateway/src/main/resources/application.yml index ab1ee4e9..e0a0fbcb 100644 --- a/mall-gateway/src/main/resources/application.yml +++ b/mall-gateway/src/main/resources/application.yml @@ -93,6 +93,8 @@ secure: - "/mobile/shop/lakala/sign/ec/**" - "/mobile/**/**/test/case" - "/**/**/testcase" + - "/mobile/sns/Story/listComment" + - "/mobile/sns/Story/getStory" universal: urls: - "/admin/account/account-user-base/info" diff --git a/mall-search/src/main/resources/dao/EsProductImage.xml b/mall-search/src/main/resources/dao/EsProductImage.xml index 6160290f..29f99f7f 100644 --- a/mall-search/src/main/resources/dao/EsProductImage.xml +++ b/mall-search/src/main/resources/dao/EsProductImage.xml @@ -16,7 +16,7 @@ lp.id, lp.barcode, lp.name, - lp.product_short_name, + lp.sname, lp.category, lp.thumb, ( @@ -37,7 +37,7 @@ lp.id, lp.barcode, lp.name, - lp.product_short_name, + lp.sname, lp.category, lp.thumb, ( @@ -60,7 +60,7 @@ lp.id, lp.barcode, lp.name, - lp.product_short_name, + lp.sname, lp.category, lp.thumb, ( diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java index 30a64a88..8c958650 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/ShopActivityCutpriceService.java @@ -33,11 +33,11 @@ public interface ShopActivityCutpriceService extends IBaseService canDoOrderCutPriceActivity(Integer ac_id, Integer order_user_id); + Pair canDoOrderCutPriceActivity(Integer activity_id, Integer order_user_id); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java index ba64148c..bca71d1e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java @@ -25,6 +25,7 @@ import com.suisung.mall.common.modules.activity.ShopActivityGroupbooking; import com.suisung.mall.common.modules.activity.ShopActivityGroupbookingHistory; import com.suisung.mall.common.modules.store.ShopStoreActivityBase; import com.suisung.mall.common.utils.CheckUtil; +import com.suisung.mall.common.utils.DateTimeUtils; import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.core.web.service.RedisService; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; @@ -34,6 +35,7 @@ import com.suisung.mall.shop.activity.service.ShopActivityCutpriceService; import com.suisung.mall.shop.activity.service.ShopActivityGroupbookingHistoryService; import com.suisung.mall.shop.activity.service.ShopActivityGroupbookingService; import com.suisung.mall.shop.base.service.AccountBaseConfigService; +import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.store.service.ShopStoreActivityBaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -74,6 +76,10 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl checkStockResult = checkCutPriceExpiredAndStock(activityBase); + + // 立即参与活动? boolean is_join_activity = false; // 如果用户未参与该砍价活动,则创建新的砍价记录 if (cutprice_row == null) { - // 需要检查活动有效期和活动商品库存是否足够 - Pair check_result = checkCutPriceExpiredAndStock(activityBase); - if (!check_result.getFirst()) { - throw new ApiException(check_result.getSecond()); + + if (checkStockResult != null && !checkStockResult.getFirst()) { + // 库存不够,立即更改活动状态为已结束 + shopStoreActivityBaseService.updateActivityState(activity_id, StateCode.ACTIVITY_STATE_FINISHED); + throw new ApiException(checkStockResult.getSecond()); } cutprice_row = new ShopActivityCutprice(); @@ -330,13 +341,11 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl check_result = checkCutPriceExpiredAndStock(activityBase); + if (!check_result.getFirst()) { + // 库存不够,立即更改活动状态为已结束 + shopStoreActivityBaseService.updateActivityState(cutprice.getActivity_id(), StateCode.ACTIVITY_STATE_FINISHED); + throw new ApiException(check_result.getSecond()); + } + + } catch (Exception e) { + 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 (!cutprice.getUser_id().equals(user_id)) { + try { + if (accountBaseConfigService != null && redisService != null) { + DateTime today = DateUtil.beginOfDay(new Date()); + Integer maxCuts = accountBaseConfigService.getConfig("user_cutprice_num", 0); + Integer usedCuts = Convert.toInt(redisService.hGet("cutprice-" + today, user_id.toString()), 0); - if (num > 0 && cut_num >= num) { - return CommonResult.failed(I18nUtil._("今日帮砍次数已用完!")); + if (maxCuts > 0 && usedCuts >= maxCuts) { + return CommonResult.failed(I18nUtil._("今日帮砍次数已达上限!")); + } + } + } catch (Exception e) { + // Redis检查失败时继续执行砍价操作 } } - BigDecimal ac_sale_price = shopActivityCutprice.getAc_sale_price(); - BigDecimal ac_mix_limit_price = shopActivityCutprice.getAc_mix_limit_price(); + BigDecimal salePrice = cutprice.getAc_sale_price(); + BigDecimal minPrice = cutprice.getAc_mix_limit_price(); - // 检查价格数据是否完整 - if (ac_sale_price == null || ac_mix_limit_price == null) { - return CommonResult.failed(I18nUtil._("价格数据异常!")); + // 检查价格数据完整性 + if (salePrice == null || minPrice == null) { + return CommonResult.failed(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._("已达到最低价!")); + if (NumberUtil.isLessOrEqual(salePrice, minPrice)) { + updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_CutFinished); + return CommonResult.failed(I18nUtil._("已砍到最低价啦!")); } - // 检查是否已经帮过好友砍了价 - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("user_id", user_id).eq("ac_id", ac_id); - long ach_num = shopActivityCutpriceHistoryService.count(queryWrapper); - if (ach_num > 0) { - return CommonResult.failed(I18nUtil._("已经帮好友砍过价!")); + // 检查是否已经帮过好友砍价 + try { + if (shopActivityCutpriceHistoryService == null) { + return CommonResult.failed(I18nUtil._("系统繁忙,请稍后再试!")); + } + + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("user_id", user_id).eq("ac_id", ac_id); + if (shopActivityCutpriceHistoryService.count(queryWrapper) > 0) { + return CommonResult.failed(I18nUtil._("您已帮好友砍过价了!")); + } + } catch (Exception e) { + return CommonResult.failed(I18nUtil._("系统繁忙,请稍后再试!")); } // 计算砍价金额 - BigDecimal cut_price = shopStoreActivityBaseService.getCutDownPrice(shopStoreActivityBase, shopActivityCutprice); - if (cut_price == null || cut_price.compareTo(BigDecimal.ZERO) <= 0) { - return CommonResult.failed(I18nUtil._("砍价失败!")); + BigDecimal cutPrice; + try { + cutPrice = shopStoreActivityBaseService.getCutDownPrice(activityBase, cutprice); + if (cutPrice == null || cutPrice.compareTo(BigDecimal.ZERO) <= 0) { + return CommonResult.failed(I18nUtil._("砍价失败,请重试!")); + } + } catch (Exception e) { + return CommonResult.failed(I18nUtil._("砍价失败,请重试!")); } // 获取商品ID - String str_activity_rule = shopStoreActivityBase.getActivity_rule(); - if (StrUtil.isBlank(str_activity_rule)) { + String ruleStr = activityBase.getActivity_rule(); + if (StrUtil.isBlank(ruleStr)) { 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._("未找到商品信息!")); + try { + JSONObject activityRule = JSONUtil.parseObj(ruleStr); + Long itemId = activityRule.get("item_id", Long.class); + if (itemId == null) { + return CommonResult.failed(I18nUtil._("未找到对应商品!")); + } + + // 创建砍价历史记录 + ShopActivityCutpriceHistory history = new ShopActivityCutpriceHistory(); + history.setActivity_id(cutprice.getActivity_id()); + history.setUser_id(user_id); + history.setAch_price(cutPrice); + history.setItem_id(itemId); + history.setAch_datetime(new Date()); + history.setAc_id(ac_id); + + // 保存历史记录 + if (!shopActivityCutpriceHistoryService.saveOrUpdate(history)) { + throw new ApiException(I18nUtil._("保存砍价记录失败!")); + } + + // 更新砍价信息 + cutprice.setAc_sale_price(NumberUtil.sub(salePrice, cutPrice)); + cutprice.setAc_num(cutprice.getAc_num() + 1); + if (!edit(cutprice)) { + throw new ApiException(I18nUtil._("更新砍价信息失败!")); + } + + // 更新帮砍次数 + if (!cutprice.getUser_id().equals(user_id) && redisService != null) { + try { + DateTime today = DateUtil.beginOfDay(new Date()); + Integer cutNum = Convert.toInt(redisService.hGet("cutprice-" + today, user_id.toString()), 0); + redisService.hSet("cutprice-" + today, user_id.toString(), cutNum + 1, 24 * 60 * 60 * 1000); + } catch (Exception e) { + // Redis更新失败不影响主流程 + } + } + + // 如果已达到最低价,更新状态 + if (NumberUtil.isLessOrEqual(cutprice.getAc_sale_price(), minPrice)) { + updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_CutFinished); + } + + return CommonResult.success(history); + } catch (ApiException e) { + throw e; + } catch (Exception e) { + throw new ApiException(I18nUtil._("砍价操作失败!")); } - - // 创建砍价历史记录 - ShopActivityCutpriceHistory ach_data = new ShopActivityCutpriceHistory(); - ach_data.setActivity_id(activity_id); - ach_data.setUser_id(user_id); - ach_data.setAch_price(cut_price); - ach_data.setItem_id(item_id); - ach_data.setAch_datetime(new Date()); - ach_data.setAc_id(ac_id); - - // 保存砍价历史记录 - if (!shopActivityCutpriceHistoryService.saveOrUpdate(ach_data)) { - 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 (!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); } @@ -566,12 +617,12 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl queryWrapperHistory = new QueryWrapper<>(); @@ -627,35 +678,45 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl 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); +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// queryWrapper.eq("activity_id", shopStoreActivityBase.getActivity_id()) +// .isNotNull("order_id").likeRight("order_id", "DD_") +// .eq("state", CommonConstant.CutPrice_Order_State_Finished); +// long recordCount = count(queryWrapper); + + // 实时查询活动的订单数量 + long recordCount = shopOrderInfoService.fetchActivityOrderSuccessCount(Convert.toStr(shopStoreActivityBase.getActivity_id()), Convert.toStr(StateCode.ACTIVITY_TYPE_CUTPRICE)); // 判断库存是否充足(当已占用库存大于等于总库存时,表示库存不足) if (recordCount >= productCount) { - return Pair.of(false, I18nUtil._("活动商品库存不足,请稍后再来。")); + return Pair.of(false, I18nUtil._("活动商品不足,稍后再参与!")); } // 所有检查通过 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/StoreValidTimeJod.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/StoreValidTimeJod.java index c1789a87..12cb0120 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/StoreValidTimeJod.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/StoreValidTimeJod.java @@ -65,6 +65,9 @@ public class StoreValidTimeJod extends QuartzJobBean { } page++; } + + // 准备筹备中的店铺,到期转正常营业 + storeBaseService.batchUpdateStoreBizStateToOpening(); } } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateActivityStatusJob.java b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateActivityStatusJob.java index e8c25bdc..3836ec5f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateActivityStatusJob.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/components/quartz/job/UpdateActivityStatusJob.java @@ -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.common.utils.DateTimeUtils; import com.suisung.mall.shop.activity.service.ShopActivityCutpriceService; import com.suisung.mall.shop.config.SpringUtil; import com.suisung.mall.shop.store.service.ShopStoreActivityBaseService; @@ -28,33 +29,57 @@ 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; List activityBaseList; QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("activity_state", StateCode.ACTIVITY_STATE_WAITING); - queryWrapper.le("activity_starttime", new Date()); + queryWrapper.in("activity_state", StateCode.ACTIVITY_STATE_WAITING, StateCode.ACTIVITY_STATE_NORMAL); +// queryWrapper.le("activity_starttime", new Date()); while (CollUtil.isNotEmpty(activityBaseList = shopStoreActivityBaseService.lists(queryWrapper, page, 20).getRecords())) { for (ShopStoreActivityBase storeActivityBase : activityBaseList) { + if (storeActivityBase.getActivity_state() != null + && storeActivityBase.getActivity_state().intValue() == StateCode.ACTIVITY_STATE_WAITING + && DateTimeUtils.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime(), new Date())) { + Boolean result = transactionTemplate.execute(status -> { + storeActivityBase.setActivity_state(StateCode.ACTIVITY_STATE_NORMAL); + Map activityBaseMap = Convert.toMap(String.class, Object.class, storeActivityBase); - Boolean result = transactionTemplate.execute(status -> { - storeActivityBase.setActivity_state(StateCode.ACTIVITY_STATE_NORMAL); - Map activityBaseMap = Convert.toMap(String.class, Object.class, storeActivityBase); + if (!shopStoreActivityBaseService.editActivityBase(storeActivityBase.getActivity_id(), activityBaseMap)) { + status.isRollbackOnly(); + return false; + } - if (!shopStoreActivityBaseService.editActivityBase(storeActivityBase.getActivity_id(), activityBaseMap)) { - status.isRollbackOnly(); - return false; + return true; + }); + + if (Boolean.FALSE.equals(result)) { + logger.error(String.format("activity_id : %s 开启出错", storeActivityBase.getActivity_id())); } - - return true; - }); - - if (Boolean.FALSE.equals(result)) { - logger.error(String.format("activity_id : %s 开启出错", storeActivityBase.getActivity_id())); } + + if (storeActivityBase.getActivity_state() != null + && storeActivityBase.getActivity_state().intValue() == StateCode.ACTIVITY_STATE_NORMAL + && !DateTimeUtils.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime(), new Date())) { + + Boolean result = transactionTemplate.execute(status -> { + storeActivityBase.setActivity_state(StateCode.ACTIVITY_STATE_FINISHED); + Map activityBaseMap = Convert.toMap(String.class, Object.class, storeActivityBase); + + if (!shopStoreActivityBaseService.editActivityBase(storeActivityBase.getActivity_id(), activityBaseMap)) { + status.isRollbackOnly(); + return false; + } + + return true; + }); + + if (Boolean.FALSE.equals(result)) { + logger.error(String.format("activity_id : %s 设置过期出错", storeActivityBase.getActivity_id())); + } + } + } try { @@ -65,8 +90,18 @@ public class UpdateActivityStatusJob extends QuartzJobBean { page++; } + // 砍价活动时间到,更新砍价订单的过期状态 + autoUpdateCutPriceStateJob(); + } + + /** + * 只针对:砍价活动时间到,更新砍价订单的过期状态 + */ + private void autoUpdateCutPriceStateJob() { + ShopActivityCutpriceService shopActivityCutpriceService = SpringUtil.getBean(ShopActivityCutpriceService.class); // 活动时间到更新砍价订单的过期状态 shopActivityCutpriceService.autoUpdateCutPriceStateJob(); - } + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java index 620147fd..93d1d7e9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/controller/admin/EsignController.java @@ -50,7 +50,7 @@ public class EsignController extends BaseControllerImpl { @ApiOperation(value = "管理员发起签署电子合同流程", notes = "基于文件发起签署电子合同") @RequestMapping(value = "/sign-flow/create-by-file", method = RequestMethod.POST) public CommonResult signFlowCreateByFile(@RequestBody JSONObject paramsJSON) { - return esignContractService.signFlowCreateByFile(paramsJSON.getStr("mchMobile")); + return esignContractService.signFlowCreateByFile(paramsJSON.getInt("store_id")); } @ApiOperation(value = "签署电子合同流程通知接收", notes = "签署电子合同流程通知接收") @@ -62,6 +62,6 @@ public class EsignController extends BaseControllerImpl { @ApiOperation(value = "查看已签署的电子合同文件", notes = "管理员查看已签署的电子合同文件") @RequestMapping(value = "/signed/contract/file", method = RequestMethod.POST) public CommonResult getSignedContactFile(@RequestBody JSONObject paramsJSON) { - return esignContractService.getSignedContactFile(paramsJSON.getStr("mchMobile")); + return esignContractService.getSignedContactFile(paramsJSON.getInt("store_id")); } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractFillingFileService.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractFillingFileService.java index fafe710d..11fbcd4a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractFillingFileService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractFillingFileService.java @@ -18,11 +18,10 @@ public interface EsignContractFillingFileService { /** * 填充合同模版,生成合同文件地址 * - * @param mchMobile 入驻商家注册手机号 - * @param platLicenseNumber 平台方(代理商方)营业执照号 + * @param storeId 入驻成功商家的店铺Id * @return */ - Boolean fillDocTemplate(String mchMobile, String platLicenseNumber); + Boolean fillDocTemplate(Integer storeId); /** * 获取模版的甲方与乙方印章XY位置数据 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java index 41b7afe1..afa26800 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/EsignContractService.java @@ -21,18 +21,18 @@ public interface EsignContractService { /** * 根据商家注册手机号,发起合同签署流程 * - * @param mchMobile + * @param storeId * @return */ - CommonResult signFlowCreateByFile(String mchMobile); + CommonResult signFlowCreateByFile(Integer storeId); /** * 内部调用:发起合同签署流程 * - * @param mchMobile + * @param storeId * @return */ - Pair innerSignFlowCreateByFile(String mchMobile); + Pair innerSignFlowCreateByFile(Integer storeId); /** * 签署流程结束异步通知(由e签宝通知) @@ -44,10 +44,10 @@ public interface EsignContractService { /** * 管理员查看已签署的电子合同文件 * - * @param mchMobile + * @param storeId * @return */ - CommonResult getSignedContactFile(String mchMobile); + CommonResult getSignedContactFile(Integer storeId); /** * 更新合同流程ID和文件地址和状态 @@ -131,4 +131,20 @@ public interface EsignContractService { * @return */ Boolean updateContractStoreId(String mchMobile, Integer storeId); + + /** + * 根据店铺Id,获取一条合同信息 + * + * @param storeId + * @return + */ + EsignContract getEsignContractByStoreId(Integer storeId); + + /** + * 根据店铺Id,获取合同状态和下载地址 + * + * @param storeId + * @return + */ + EsignContract getEsignContractStatusUrl(Integer storeId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java index 88ba9d90..f39b49d3 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractFillingFileServiceImpl.java @@ -9,6 +9,7 @@ package com.suisung.mall.shop.esign.service.impl; import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; @@ -21,6 +22,8 @@ import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.esign.EsignContractFillingFile; import com.suisung.mall.common.modules.esign.EsignPlatformInfo; import com.suisung.mall.common.modules.store.ShopMchEntry; +import com.suisung.mall.common.modules.store.ShopStoreBase; +import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.common.utils.StringUtils; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.esign.mapper.EsignContractFillingFileMapper; @@ -33,6 +36,7 @@ import com.suisung.mall.shop.esign.utils.enums.EsignRequestType; import com.suisung.mall.shop.esign.utils.exception.EsignDemoException; import com.suisung.mall.shop.page.service.OssService; import com.suisung.mall.shop.store.service.ShopMchEntryService; +import com.suisung.mall.shop.store.service.ShopStoreBaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; @@ -41,6 +45,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import javax.annotation.Resource; +import java.math.BigDecimal; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -79,6 +84,10 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl - * 后台管理员审核商家通过之后,触发调用这个方法,生成未签署合同文件,预备签署 + * 拉卡拉审核通过之后,触发调用这个方法,生成未签署合同文件,预备签署 * - * @param mchMobile 入驻商家(甲方)的注册手机号 - * @param platLicenseNumber 平台方(代理商方)(乙方)营业执照号 - * @return + * @param storeId 入驻成功商家的店铺Id + * @return Boolean 是否成功生成合同文件 */ @Override - public Boolean fillDocTemplate(String mchMobile, String platLicenseNumber) { - if (StrUtil.isEmpty(mchMobile)) { - log.error("商家手机号为空"); + public Boolean fillDocTemplate(Integer storeId) { + // 1. 参数校验 + if (CheckUtil.isEmpty(storeId)) { + log.error("[合同生成] 商家店铺ID不能为空"); return false; } - // 获取平台方的信息 - EsignPlatformInfo esignPlatformInfo = esignPlatformInfoService.getEsignPlatformInfo(0, platLicenseNumber); + // 2. 获取平台方信息 + EsignPlatformInfo esignPlatformInfo = esignPlatformInfoService.getEsignPlatformInfo(0, ""); if (ObjectUtils.isEmpty(esignPlatformInfo)) { - log.error("请添加平台方(代理商方)信息"); - return null; + log.error("[合同生成] 平台方(代理商方)信息缺失,请先配置平台信息"); + return false; } - // 获取入驻商家(审批通过的)的信息 - ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByCondition(mchMobile, "", CommonConstant.MCH_APPR_STA_PASS); + if (StrUtil.isBlank(esignPlatformInfo.getDoc_template())) { + log.error("[合同生成] 平台方合同模板信息为空,无法继续生成合同"); + return false; + } + + // 3. 获取入驻商家(审批通过的)的信息 + ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByStoreId(storeId); if (shopMchEntry == null) { - log.error("缺少商家入驻信息"); - return null; + log.error("[合同生成] 找不到商家入驻信息, storeId: {}", storeId); + return false; } - // 代理商信息 + // 4. 检查商家审批状态 + if (!CommonConstant.MCH_APPR_STA_PASS.equals(shopMchEntry.getApproval_status())) { + log.error("[合同生成] 商家入驻审批未通过,当前状态: {}, storeId: {}", shopMchEntry.getApproval_status(), storeId); + return false; + } + + ShopStoreBase shopStoreBase = shopStoreBaseService.getShopStoreBaseByStoreId(storeId); + if (ObjectUtils.isEmpty(shopStoreBase)) { + log.error("[合同生成] 缺少商家店铺信息, storeId: {}", storeId); + return false; + } + + BigDecimal splitRatio = shopStoreBase.getSplit_ratio(); + if (CheckUtil.isEmpty(splitRatio)) { + log.warn("[合同生成] 店铺分账比例为空,将使用入驻申请时设置的比例, storeId: {}", storeId); + splitRatio = shopMchEntry.getSplit_ratio(); + } + + // 5. 获取代理商信息 EsignPlatformInfo distributor = esignPlatformInfoService.getDistributorInfoById(shopMchEntry.getDistributor_id()); - String apiaddr = "/v3/files/create-by-doc-template"; - EsignRequestType requestType = EsignRequestType.POST; - - // 获取平台方(代理商方)合同模版信息 + // 6. 获取平台方合同模版信息 JSONArray templates = JSONUtil.parseArray(esignPlatformInfo.getDoc_template()); - if (ObjectUtils.isEmpty(templates) || templates.size() <= 0) { - log.error("缺少平台方(代理商方)合同模版信息"); - return null; + if (templates == null || templates.isEmpty()) { + log.error("[合同生成] 平台方(代理商方)合同模板信息缺失"); + return false; } + // 7. 准备基础数据 String today = DateUtil.format(new Date(), "yyyy年MM月dd日"); // 甲方公司名称,甲方是个人的时候,没有公司名,直接用店铺名(个人实名) - String mchCompany = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) ? shopMchEntry.getBiz_license_company() : shopMchEntry.getStore_name() + "(" + shopMchEntry.getContact_name() + ")"; + String mchCompany = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) + ? shopMchEntry.getBiz_license_company() + : shopMchEntry.getStore_name() + "(" + shopMchEntry.getContact_name() + ")"; String platCompany = esignPlatformInfo.getLicense_company(); // 甲方法人姓名,甲方是个人的时候,没有法人,直接用个人实名 - String legalPersonName = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) ? shopMchEntry.getLegal_person_name() : shopMchEntry.getContact_name(); - String LegalPersonMobile = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) ? shopMchEntry.getLegal_person_mobile() : shopMchEntry.getLogin_mobile(); + String legalPersonName = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) + ? shopMchEntry.getLegal_person_name() + : shopMchEntry.getContact_name(); + String legalPersonMobile = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) + ? shopMchEntry.getLegal_person_mobile() + : shopMchEntry.getLogin_mobile(); // 甲方法人身份证号,甲方是个人的时候,没有法人,直接用个人身份证 - String legalPersonIdNumber = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) ? shopMchEntry.getLegal_person_id_number() : shopMchEntry.getIndividual_id_number(); - + String legalPersonIdNumber = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) + ? shopMchEntry.getLegal_person_id_number() + : shopMchEntry.getIndividual_id_number(); String contractNumber = StringUtils.genLklOrderNo(4); - int successCnt = 0; - // 模版文件里有三份合同,顺序排列的: 1.平台商户入驻服务框架协议 2.小发同城服务费结算 3.结算授权委托书 + log.info("[合同生成] 开始为商家生成入驻合同文件, storeId: {}, 商家手机号: {}", storeId, shopMchEntry.getLogin_mobile()); + + // 8. 遍历模版文件生成合同(模版文件里有三份合同,顺序排列的: 1.平台商户入驻服务框架协议 2.小发同城服务费结算 3.结算授权委托书) for (JSONObject template : templates.jsonIter()) { - // 从商家信息里获取模版的信息 - String templateId = template.getStr("template_id"); - String fileName = template.getStr("template_name"); - int seq = template.getInt("seq"); + try { + // 从商家信息里获取模版的信息 + String templateId = template.getStr("template_id"); + String fileName = template.getStr("template_name"); + int seq = template.getInt("seq"); - // 获取填充模版的数据 - JSONObject fillJson = new JSONObject(); - fillJson.put("docTemplateId", templateId) - .put("fileName", fileName); + log.debug("[合同生成] 正在处理合同模板: templateId={}, 文件名={}, 序号={}", templateId, fileName, seq); + // 9. 构建填充模版的数据 + JSONObject fillJson = new JSONObject(); + fillJson.put("docTemplateId", templateId) + .put("fileName", fileName); - JSONArray list = new JSONArray(); - EsignPlatformInfo finalEsignPlatformInfo = esignPlatformInfo; + JSONArray list = new JSONArray(); - list.add(new HashMap() {{ - put("componentKey", "plat_contracts"); - put("componentValue", "《平台商户入驻服务框架协议》和《小发同城服务费结算》"); - }}); - - // 签署时间 - for (int i = 1; i <= 3; i++) { - int finalI = i; + // 10. 填充合同组件数据 + // 平台合同名称 list.add(new HashMap() {{ - put("componentKey", "mch_sign_date" + finalI); - put("componentValue", today); + put("componentKey", "plat_contracts"); + put("componentValue", "《平台商户入驻服务框架协议》和《小发同城服务费结算》"); }}); - } - for (int i = 1; i <= 3; i++) { - int finalI = i; - list.add(new HashMap() {{ - put("componentKey", "plat_sign_date" + finalI); - put("componentValue", today); - }}); - } - - if (CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type())) { - // 甲方公司名称,甲方是个人的时候,没有公司名,直接用店铺名 - for (int i = 1; i <= 17; i++) { + // 签署时间(甲方) + for (int i = 1; i <= 3; i++) { int finalI = i; list.add(new HashMap() {{ - put("componentKey", "mch_company" + finalI); - put("componentValue", mchCompany); + put("componentKey", "mch_sign_date" + finalI); + put("componentValue", today); }}); } - } else { - // 甲方公司名称,甲方是个人的时候,没有公司名,直接用店铺名 - for (int i = 1; i <= 16; i++) { + + // 签署时间(乙方) + for (int i = 1; i <= 3; i++) { + int finalI = i; + list.add(new HashMap() {{ + put("componentKey", "plat_sign_date" + finalI); + put("componentValue", today); + }}); + } + + // 甲方公司名称 + int mchCompanyCount = CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type()) ? 17 : 16; + for (int i = 1; i <= mchCompanyCount; i++) { int finalI = i; list.add(new HashMap() {{ put("componentKey", "mch_company" + finalI); @@ -199,233 +232,242 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl() {{ put("componentKey", "mch_company17"); put("componentValue", shopMchEntry.getContact_name()); }}); } - } - // 甲方法人姓名,甲方是个人的时候,没有法人,直接用个人实名 - for (int i = 1; i <= 4; i++) { - int finalI = i; - list.add(new HashMap() {{ - put("componentKey", "mch_legal_person_name" + finalI); - put("componentValue", legalPersonName); - }}); - } - - // 甲方法人手机号 - for (int i = 1; i <= 3; i++) { - int finalI = i; - list.add(new HashMap() {{ - put("componentKey", "mch_legal_person_mobile" + finalI); - put("componentValue", LegalPersonMobile); - }}); - } - - // 甲方身份证号码 - list.add(new HashMap() {{ - put("componentKey", "mch_legal_person_id_number1"); - put("componentValue", legalPersonIdNumber); - }}); - - list.add(new HashMap() {{ - put("componentKey", "mch_store_name1"); - put("componentValue", shopMchEntry.getStore_name()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "rule_no"); - put("componentValue", 3); - }}); - - list.add(new HashMap() {{ - put("componentKey", "mch_ratio"); - put("componentValue", shopMchEntry.getSplit_ratio()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "settlement_method"); - put("componentValue", shopMchEntry.getSettlement_method()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "mch_address1"); - put("componentValue", shopMchEntry.getStore_address()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "mch_bank1"); - put("componentValue", shopMchEntry.getBank_name()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "mch_account_number1"); - put("componentValue", shopMchEntry.getAccount_number()); - }}); - - // 乙方公司名称 - list.add(new HashMap() {{ - put("componentKey", "plat_company1"); - put("componentValue", platCompany + "和代理商"); - }}); - - for (int i = 2; i <= 5; i++) { - int finalI = i; - list.add(new HashMap() {{ - put("componentKey", "plat_company" + finalI); - put("componentValue", platCompany); - }}); - } - - for (int i = 1; i <= 2; i++) { - int finalI = i; - list.add(new HashMap() {{ - put("componentKey", "plat_mobile" + finalI); - put("componentValue", finalEsignPlatformInfo.getLegal_person_mobile()); - }}); - } - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_email1"); - put("componentValue", finalEsignPlatformInfo.getEmail()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_bank1"); - put("componentValue", finalEsignPlatformInfo.getRec_acc_bank_name()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "plat_account_number1"); - put("componentValue", finalEsignPlatformInfo.getRec_acc_card_no()); - }}); - - // 代理商相关 - if (distributor != null) { - // 有代理商的时候,才填充代理商的信息 - list.add(new HashMap() {{ - put("componentKey", "distr_company1"); - put("componentValue", distributor.getLicense_company()); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_mobile1"); - put("componentValue", distributor.getLegal_person_mobile()); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_company2"); - put("componentValue", distributor.getLicense_company()); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_bank1"); - put("componentValue", distributor.getRec_acc_bank_name()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "distr_account_number1"); - put("componentValue", distributor.getRec_acc_card_no()); - }}); - - list.add(new HashMap() {{ - put("componentKey", "distr_sign_date1"); - put("componentValue", today); - }}); - } else { - // 有代理商的时候,才填充代理商的信息 - list.add(new HashMap() {{ - put("componentKey", "distr_company1"); - put("componentValue", "无"); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_mobile1"); - put("componentValue", "无"); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_company2"); - put("componentValue", "无"); - }}); - list.add(new HashMap() {{ - put("componentKey", "distr_bank1"); - put("componentValue", "无"); - }}); - - list.add(new HashMap() {{ - put("componentKey", "distr_account_number1"); - put("componentValue", "无"); - }}); - - } - - fillJson.put("components", list); - - String jsonParma = fillJson.toString(); - - //生成签名鉴权方式的的header - Map header = null; - try { - header = EsignHttpHelper.signAndBuildSignAndJsonHeader(appId, appSecret, jsonParma, requestType.name(), apiaddr, debug); - //发起接口请求 - EsignHttpResponse createByDocTemplate = EsignHttpHelper.doCommHttp(serverUrl, apiaddr, requestType, jsonParma, header, debug); - log.info("合同生成数据:{}", createByDocTemplate); - if (createByDocTemplate.getStatus() != 200) { - return false; + // 甲方法人姓名 + for (int i = 1; i <= 4; i++) { + int finalI = i; + list.add(new HashMap() {{ + put("componentKey", "mch_legal_person_name" + finalI); + put("componentValue", legalPersonName); + }}); } - EsignContractFillingFile esignContractFillingFile = new EsignContractFillingFile(); + // 甲方法人手机号 + for (int i = 1; i <= 3; i++) { + int finalI = i; + list.add(new HashMap() {{ + put("componentKey", "mch_legal_person_mobile" + finalI); + put("componentValue", legalPersonMobile); + }}); + } + // 甲方身份证号码 + list.add(new HashMap() {{ + put("componentKey", "mch_legal_person_id_number1"); + put("componentValue", legalPersonIdNumber); + }}); + + // 甲方店铺名称 + list.add(new HashMap() {{ + put("componentKey", "mch_store_name1"); + put("componentValue", shopMchEntry.getStore_name()); + }}); + + // 规则编号 + list.add(new HashMap() {{ + put("componentKey", "rule_no"); + put("componentValue", 3); + }}); + + // 分账比例 + BigDecimal finalSplitRatio = splitRatio; + list.add(new HashMap() {{ + put("componentKey", "mch_ratio"); + put("componentValue", finalSplitRatio); + }}); + + // 结算方式 + list.add(new HashMap() {{ + put("componentKey", "settlement_method"); + put("componentValue", shopMchEntry.getSettlement_method()); + }}); + + // 甲方地址 + list.add(new HashMap() {{ + put("componentKey", "mch_address1"); + put("componentValue", shopMchEntry.getStore_address()); + }}); + + // 甲方银行 + list.add(new HashMap() {{ + put("componentKey", "mch_bank1"); + put("componentValue", shopMchEntry.getBank_name()); + }}); + + // 甲方账户号 + list.add(new HashMap() {{ + put("componentKey", "mch_account_number1"); + put("componentValue", shopMchEntry.getAccount_number()); + }}); + + // 乙方公司名称 + list.add(new HashMap() {{ + put("componentKey", "plat_company1"); + put("componentValue", platCompany + "和代理商"); + }}); + + // 其他乙方公司名称 + for (int i = 2; i <= 5; i++) { + int finalI = i; + list.add(new HashMap() {{ + put("componentKey", "plat_company" + finalI); + put("componentValue", platCompany); + }}); + } + + // 乙方手机号 + for (int i = 1; i <= 2; i++) { + int finalI = i; + list.add(new HashMap() {{ + put("componentKey", "plat_mobile" + finalI); + put("componentValue", esignPlatformInfo.getLegal_person_mobile()); + }}); + } + + // 乙方邮箱(重复填充,但保持原逻辑) + for (int i = 0; i < 6; i++) { + list.add(new HashMap() {{ + put("componentKey", "plat_email1"); + put("componentValue", esignPlatformInfo.getEmail()); + }}); + } + + // 乙方银行 + list.add(new HashMap() {{ + put("componentKey", "plat_bank1"); + put("componentValue", esignPlatformInfo.getRec_acc_bank_name()); + }}); + + // 乙方账户号 + list.add(new HashMap() {{ + put("componentKey", "plat_account_number1"); + put("componentValue", esignPlatformInfo.getRec_acc_card_no()); + }}); + + // 11. 处理代理商相关数据 + if (distributor != null) { + // 有代理商的时候,填充代理商的信息 + list.add(new HashMap() {{ + put("componentKey", "distr_company1"); + put("componentValue", distributor.getLicense_company()); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_mobile1"); + put("componentValue", distributor.getLegal_person_mobile()); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_company2"); + put("componentValue", distributor.getLicense_company()); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_bank1"); + put("componentValue", distributor.getRec_acc_bank_name()); + }}); + + list.add(new HashMap() {{ + put("componentKey", "distr_account_number1"); + put("componentValue", distributor.getRec_acc_card_no()); + }}); + + list.add(new HashMap() {{ + put("componentKey", "distr_sign_date1"); + put("componentValue", today); + }}); + + log.debug("[合同生成] 已填充代理商信息: {}", distributor.getLicense_company()); + } else { + // 无代理商时填充默认值 + list.add(new HashMap() {{ + put("componentKey", "distr_company1"); + put("componentValue", "无"); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_mobile1"); + put("componentValue", "无"); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_company2"); + put("componentValue", "无"); + }}); + list.add(new HashMap() {{ + put("componentKey", "distr_bank1"); + put("componentValue", "无"); + }}); + + list.add(new HashMap() {{ + put("componentKey", "distr_account_number1"); + put("componentValue", "无"); + }}); + + log.debug("[合同生成] 未配置代理商信息,使用默认值"); + } + + fillJson.put("components", list); + String jsonParam = fillJson.toString(); + + String apiAddr = "/v3/files/create-by-doc-template"; + EsignRequestType requestType = EsignRequestType.POST; + + // 12. 调用e签宝API生成合同文件 + Map header = EsignHttpHelper.signAndBuildSignAndJsonHeader( + appId, appSecret, jsonParam, requestType.name(), apiAddr, debug); + + // 发起接口请求 + EsignHttpResponse createByDocTemplate = EsignHttpHelper.doCommHttp( + serverUrl, apiAddr, requestType, jsonParam, header, debug); + + log.info("[合同生成] 调用电子签约服务生成合同文件: status={}, response={}", + createByDocTemplate.getStatus(), createByDocTemplate.getBody()); + + if (createByDocTemplate.getStatus() != 200) { + log.error("[合同生成] 调用电子签约服务生成合同文件失败, HTTP状态码: {}", createByDocTemplate.getStatus()); + continue; // 继续处理下一个模版 + } + + // 13. 解析API返回结果 JSONObject jsonObject = JSONUtil.parseObj(createByDocTemplate.getBody()).getJSONObject("data"); - esignContractFillingFile.setUnsigned_contract_url(jsonObject.getStr("fileDownloadUrl")); + String fileDownloadUrl = jsonObject.getStr("fileDownloadUrl"); String fileId = jsonObject.getStr("fileId"); - // 把合同文件 url 上传到cos服务器 -// String contractPath = StrUtil.isBlank(shopMchEntry.getBiz_license_number()) ? mchMobile : shopMchEntry.getBiz_license_number(); - String cosFileName = TENGXUN_DEFAULT_DIR.concat("/").concat("contract") - .concat("/").concat(shopMchEntry.getLogin_mobile()).concat("/") - .concat(jsonObject.getStr("fileId")).concat(".pdf"); - // 上传到cos服务器 - String localFileUrl = ossService.uploadObject4OSS(esignContractFillingFile.getUnsigned_contract_url(), cosFileName); - esignContractFillingFile.setUnsigned_contract_local_url(localFileUrl); + if (StrUtil.isBlank(fileDownloadUrl) || StrUtil.isBlank(fileId)) { + log.error("[合同生成] 电子签约服务返回的合同文件信息不完整, 下载地址: {}, 文件ID: {}", fileDownloadUrl, fileId); + continue; + } + + // 14. 创建合同填充文件记录 + EsignContractFillingFile esignContractFillingFile = new EsignContractFillingFile(); + esignContractFillingFile.setUnsigned_contract_url(fileDownloadUrl); esignContractFillingFile.setFile_id(fileId); + // 15. 上传合同文件到OSS + String cosFileName = TENGXUN_DEFAULT_DIR.concat("/contract/") + .concat(shopMchEntry.getLogin_mobile()).concat("/") + .concat(fileId).concat(".pdf"); + + String localFileUrl = ossService.uploadObject4OSS(fileDownloadUrl, cosFileName); + if (StrUtil.isBlank(localFileUrl)) { + log.error("[合同生成] 合同文件上传到对象存储失败, fileId: {}", fileId); + continue; + } + + esignContractFillingFile.setUnsigned_contract_local_url(localFileUrl); esignContractFillingFile.setDoc_template_id(templateId); esignContractFillingFile.setContract_number(contractNumber + seq); esignContractFillingFile.setContract_name("商户入驻小发同城平台合同协议"); - esignContractFillingFile.setStore_id(contractNumber); - esignContractFillingFile.setMobile(mchMobile); - esignContractFillingFile.setDoc_template_filling_values(jsonParma); + esignContractFillingFile.setStore_id(Convert.toStr(storeId)); + esignContractFillingFile.setMobile(shopMchEntry.getLogin_mobile()); + esignContractFillingFile.setDoc_template_filling_values(jsonParam); esignContractFillingFile.setSeq(seq); esignContractFillingFile.setStatus(CommonConstant.Enable); - // 获取印章的位置信息,写入数据库 + // 16. 获取印章位置信息 Map signPositionMap = getSignPosition(templateId, fileId); if (signPositionMap != null) { if (signPositionMap.get("mch") != null) { @@ -441,22 +483,34 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl 0; + boolean result = successCnt > 0; + log.info("[合同生成] 商家合同文件全部生成完毕, storeId: {}, 总模板数: {}, 成功处理数: {}, 最终结果: {}", + storeId, templates.size(), successCnt, result); + + return result; } + /** * 获取模版的甲方与乙方印章XY位置数据 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java index 32e9a451..e5147939 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignContractServiceImpl.java @@ -14,6 +14,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.suisung.mall.common.api.CommonResult; @@ -22,6 +23,7 @@ import com.suisung.mall.common.modules.esign.EsignContract; import com.suisung.mall.common.modules.esign.EsignContractFillingFile; import com.suisung.mall.common.modules.esign.EsignPlatformInfo; import com.suisung.mall.common.modules.store.ShopMchEntry; +import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.components.TaskService; import com.suisung.mall.shop.esign.mapper.EsignContractMapper; @@ -32,11 +34,8 @@ import com.suisung.mall.shop.esign.utils.comm.EsignHttpHelper; import com.suisung.mall.shop.esign.utils.comm.EsignHttpResponse; import com.suisung.mall.shop.esign.utils.enums.EsignRequestType; import com.suisung.mall.shop.esign.utils.exception.EsignDemoException; -import com.suisung.mall.shop.lakala.service.LakalaApiService; -import com.suisung.mall.shop.lakala.service.LklLedgerMemberService; import com.suisung.mall.shop.page.service.OssService; import com.suisung.mall.shop.store.service.ShopMchEntryService; -import com.suisung.mall.shop.store.service.ShopStoreBaseService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -91,18 +90,6 @@ public class EsignContractServiceImpl extends BaseServiceImpl ret = innerSignFlowCreateByFile(mchMobile); + public CommonResult signFlowCreateByFile(Integer storeId) { + Pair ret = innerSignFlowCreateByFile(storeId); if (!ret.getFirst()) { return CommonResult.failed(ret.getSecond()); } @@ -179,21 +166,25 @@ public class EsignContractServiceImpl extends BaseServiceImpl innerSignFlowCreateByFile(String mchMobile) { - //String userId = "0"; - - if (StrUtil.isBlank(mchMobile)) { + public Pair innerSignFlowCreateByFile(Integer storeId) { + if (CheckUtil.isEmpty(storeId)) { return Pair.of(false, "缺少必要参数!"); } - EsignContract esignContract = getEsignContractByMchMobile(mchMobile); + // 组织和填充商家店铺的模版数据 + Boolean isFill = esignContractFillingFileService.fillDocTemplate(storeId); + if (!isFill) { + return Pair.of(false, "合同信息未准备好,请检查商家入驻手续是否已完成!"); + } + + EsignContract esignContract = getEsignContractByStoreId(storeId); if (esignContract == null) { return Pair.of(false, "未找到商家合同信息"); } // 检查商户入驻信息是否被审核通过 // 检查店铺是否已经申请过入驻 - Integer apprStatus = shopMchEntryService.getApprovalStatus(mchMobile); + Integer apprStatus = shopMchEntryService.getApprovalStatus(esignContract.getMch_mobile()); if (!CommonConstant.MCH_APPR_STA_PASS.equals(apprStatus)) { return Pair.of(false, "请先审核商家入驻信息"); } @@ -211,7 +202,7 @@ public class EsignContractServiceImpl extends BaseServiceImpl>> {}", requestBody); log.debug("签署流程结束通知:header >>> {}", request.getParameterMap()); - //异步通知获取到的header头中的签名值:X-Tsign-Open-App-Id + // 1. 验证请求头参数完整性 String reqAppId = request.getHeader("X-Tsign-Open-App-Id"); - //异步通知获取到的header头中的签名值:X-Tsign-Open-SIGNATURE - String signture = request.getHeader("X-Tsign-Open-SIGNATURE"); - //异步通知获取到的header头中的时间戳:X-Tsign-Open-TIMESTAMP + String signature = request.getHeader("X-Tsign-Open-SIGNATURE"); String timestamp = request.getHeader("X-Tsign-Open-TIMESTAMP"); - if (StrUtil.isBlank(reqAppId) || StrUtil.isBlank(signture) || StrUtil.isBlank(timestamp)) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JSONObject().put("code", 400).put("msg", "缺少必要参数").toString()); + if (StrUtil.isBlank(reqAppId) || StrUtil.isBlank(signature) || StrUtil.isBlank(timestamp)) { + log.warn("e签宝异步通知缺少必要参数: reqAppId={}, signature={}, timestamp={}", reqAppId, signature, timestamp); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "缺少必要参数").toString()); } + // 2. 验证AppId是否匹配 if (!reqAppId.equals(appId)) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JSONObject().put("code", 400).put("msg", "appId 有误").toString()); + log.warn("e签宝异步通知AppId不匹配: 请求AppId={}, 配置AppId={}", reqAppId, appId); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "appId 有误").toString()); } - //按照规则进行加密 + // 3. 验证签名 String signData = timestamp + requestBody; String mySignature = getSignature(signData, appSecret, "HmacSHA256", "UTF-8"); - log.debug("加密出来的签名值:----------->>>>>>" + mySignature); - log.debug("header里面的签名值:---------->>>>>>" + signture); - if (!mySignature.equals(signture)) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JSONObject().put("code", 400).put("msg", "签名校验失败").toString()); + + // 签名生成失败处理 + if (mySignature == null) { + log.error("生成签名失败,signData: {}", signData); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "签名生成失败").toString()); + } + + log.debug("加密出来的签名值:----------->>>>>> {}", mySignature); + log.debug("header里面的签名值:---------->>>>>> {}", signature); + + if (!mySignature.equals(signature)) { + log.warn("e签宝异步通知签名校验失败: 计算签名={}, 请求签名={}", mySignature, signature); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "签名校验失败").toString()); + } + + // 4. 解析请求体数据 + JSONObject reqBodyJSON; + try { + reqBodyJSON = JSONUtil.parseObj(requestBody); + } catch (Exception e) { + log.error("解析e签宝异步通知请求体失败: requestBody={}", requestBody, e); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "请求体格式错误").toString()); } - // 处理业务逻辑 - JSONObject reqBodyJSON = JSONUtil.parseObj(requestBody); String action = reqBodyJSON.getStr("action"); String signFlowId = reqBodyJSON.getStr("signFlowId"); Integer signResult = reqBodyJSON.getInt("signResult"); + if (StrUtil.isBlank(action) || StrUtil.isBlank(signFlowId)) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JSONObject().put("code", 400).put("msg", "返回数据有误").toString()); + log.warn("e签宝异步通知缺少必要业务参数: action={}, signFlowId={}", action, signFlowId); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "返回数据有误").toString()); } - // 获取合同签署记录 - EsignContract esignContract = baseMapper.selectOne(new QueryWrapper().eq("sign_flow_id", signFlowId)); - if (esignContract == null) { - return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new JSONObject().put("code", 400).put("msg", "获取不到合同记录").toString()); + // 5. 获取合同签署记录 + EsignContract esignContract; + try { + esignContract = baseMapper.selectOne(new QueryWrapper().eq("sign_flow_id", signFlowId)); + if (esignContract == null) { + log.warn("未找到对应的合同记录: signFlowId={}", signFlowId); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "获取不到合同记录").toString()); + } + } catch (Exception e) { + log.error("查询合同记录异常: signFlowId={}", signFlowId, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new JSONObject().put("code", 500).put("msg", "查询合同记录异常").toString()); } log.debug("签署流程结束通知:action >>> {}", action); - if (CommonConstant.CONTRACT_SIGN_STA_FINISH.equals(esignContract.getSign_flow_status()) && StrUtil.isNotEmpty(esignContract.getLocal_contract_url())) { - // 已经签署完毕,不用在更改状态 + + // 6. 如果合同已经完成且已有本地文件URL,则直接返回成功 + if (CommonConstant.CONTRACT_SIGN_STA_FINISH.equals(esignContract.getSign_flow_status()) + && StrUtil.isNotEmpty(esignContract.getLocal_contract_url())) { + log.debug("合同已处理完成,无需重复处理: signFlowId={}", signFlowId); return ResponseEntity.ok(new JSONObject().put("code", 200).put("msg", "success").toString()); } - // 获取正式盖章合同文件,上传到 oss 服务器,更状态,保存数据 - if (action.equals("SIGN_FLOW_COMPLETE")) {// 签署流程完毕 - log.debug("签署流程完毕,开始处理业务逻辑"); - // 获取正式盖章合同文件地址 - String downloadUrl = getSignedContractFileUrl(signFlowId); + // 7. 根据不同动作类型处理业务逻辑 + try { + // 签署流程完毕 + if ("SIGN_FLOW_COMPLETE".equals(action)) { + log.debug("签署流程完毕,开始处理业务逻辑: signFlowId={}", signFlowId); - // 更新合同流程状态和文件地址 - boolean success = updateContractFlowStatusAndFileUrlBySignFlowId(signFlowId, CommonConstant.CONTRACT_SIGN_STA_FINISH, downloadUrl); - if (success && StrUtil.isNotBlank(downloadUrl)) { - - // 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略 - Pair retPair = lakalaApiService.innerApplyLedgerMer("", false); - if (!retPair.getFirst()) { - log.error("商家申请分账业务异常:{}", retPair.getSecond()); + // 获取正式盖章合同文件地址 + String downloadUrl = getSignedContractFileUrl(signFlowId); + if (StrUtil.isBlank(downloadUrl)) { + log.warn("获取签署完成的合同文件URL失败: signFlowId={}", signFlowId); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new JSONObject().put("code", 500).put("msg", "获取合同文件失败").toString()); } - // TODO mchId 必填字段,更新商家的hasEsigned状态=1 - shopMchEntryService.updateMulStatus(0L, "", 1, 0, 0, 0, 0, 0, CommonConstant.MCH_APPR_STA_LKL_PADDING); + // 更新合同流程状态和文件地址 + boolean success = updateContractFlowStatusAndFileUrlBySignFlowId( + signFlowId, + CommonConstant.CONTRACT_SIGN_STA_FINISH, + downloadUrl); + if (success) { + log.info("合同签署完成处理成功: signFlowId={}", signFlowId); + return ResponseEntity.ok(new JSONObject().put("code", 200).put("msg", "success").toString()); + } else { + log.error("更新合同流程状态失败: signFlowId={}", signFlowId); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new JSONObject().put("code", 500).put("msg", "更新合同状态失败").toString()); + } + } + // 签署方签署结果通知(含拒签) + else if ("SIGN_MISSON_COMPLETE".equals(action) && ObjectUtil.isNotEmpty(signResult)) { + Integer signFlowStatus = null; + + // 根据签署结果确定合同状态 + if (Integer.valueOf(2).equals(signResult)) { + signFlowStatus = CommonConstant.CONTRACT_SIGN_STA_PARTIALLY; + } else if (Integer.valueOf(4).equals(signResult)) { + signFlowStatus = CommonConstant.CONTRACT_SIGN_STA_REJECT; + } + + if (signFlowStatus == null) { + log.warn("未知的签署结果类型: signResult={}", signResult); + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(new JSONObject().put("code", 400).put("msg", "未知的签署结果").toString()); + } + + // 更新合同流程状态 + boolean success = updateContractFlowStatusAndFileUrlBySignFlowId(signFlowId, signFlowStatus, ""); + if (success) { + log.info("签署任务完成处理成功: signFlowId={}, signResult={}, status={}", + signFlowId, signResult, signFlowStatus); + return ResponseEntity.ok(new JSONObject().put("code", 200).put("msg", "success").toString()); + } else { + log.error("更新合同流程状态失败: signFlowId={}, signResult={}", signFlowId, signResult); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new JSONObject().put("code", 500).put("msg", "更新合同状态失败").toString()); + } + } + // 其他情况,记录日志但不处理 + else { + log.debug("签署流程未完成或未知动作类型,不做处理: action={}, signFlowId={}", action, signFlowId); return ResponseEntity.ok(new JSONObject().put("code", 200).put("msg", "success").toString()); } - } else if (action.equals("SIGN_MISSON_COMPLETE") && ObjectUtil.isNotEmpty(signResult)) {// 签署方-签署结果(含拒签)通知 - Integer signFlowStatus = null; - if (signResult.equals(2)) { - signFlowStatus = CommonConstant.CONTRACT_SIGN_STA_PARTIALLY; - } else if (signResult.equals(4)) { - signFlowStatus = CommonConstant.CONTRACT_SIGN_STA_REJECT; - } - // 更新合同流程状态和文件地址 - boolean success = updateContractFlowStatusAndFileUrlBySignFlowId(signFlowId, signFlowStatus, ""); - if (success) { - return ResponseEntity.ok(new JSONObject().put("code", 200).put("msg", "success").toString()); - } - } else { - log.debug("签署流程未完成,不做处理"); + } catch (Exception e) { + log.error("处理e签宝异步通知异常: action={}, signFlowId={}", action, signFlowId, e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body(new JSONObject().put("code", 500).put("msg", "处理通知异常").toString()); } - - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new JSONObject().put("code", 400).put("msg", "未更新数据!").toString()); } + /** * 管理员查看已签署的电子合同文件 * - * @param mchMobile + * @param storeId * @return */ @Override - public CommonResult getSignedContactFile(String mchMobile) { - String userId = "0"; - -// UserDto user = getCurrentUser(); -// if (!user.isAdmin()) { -// return CommonResult.failed("权限不足!"); -// } -// userId = user.getId().toString(); - - EsignContract esignContract = getEsignContractByMchMobile(mchMobile); + public CommonResult getSignedContactFile(Integer storeId) { + EsignContract esignContract = getEsignContractByStoreId(storeId); if (esignContract == null) { return CommonResult.success(null, "未找到商家合同信息"); } @@ -583,22 +634,86 @@ public class EsignContractServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("mch_mobile", mchMobile); - queryWrapper.eq("status", CommonConstant.Enable); - List esignContractList = this.list(queryWrapper); - if (CollectionUtil.isEmpty(esignContractList)) { + // 参数校验 + if (StrUtil.isBlank(mchMobile)) { + log.warn("查询合同信息失败:商家手机号为空"); return null; } - return esignContractList.get(0); + try { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("mch_mobile", mchMobile); + queryWrapper.eq("status", CommonConstant.Enable); + + return findOne(queryWrapper); + } catch (Exception e) { + log.error("根据商家手机号查询合同信息异常,手机号: {}", mchMobile, e); + return null; + } } + /** + * 根据店铺Id,获取一条合同信息 + * + * @param storeId 店铺ID + * @return EsignContract 合同信息对象,若未找到则返回null + */ + @Override + public EsignContract getEsignContractByStoreId(Integer storeId) { + // 参数校验 + if (storeId == null) { + log.warn("查询合同信息失败:店铺ID为空"); + return null; + } + + try { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("store_id", storeId); + queryWrapper.eq("status", CommonConstant.Enable); + + return findOne(queryWrapper); + } catch (Exception e) { + log.error("根据店铺ID查询合同信息异常,店铺ID: {}", storeId, e); + return null; + } + } + + /** + * 根据店铺Id,获取合同状态和下载地址 + * + * @param storeId + * @return + */ + @Override + public EsignContract getEsignContractStatusUrl(Integer storeId) { + // 参数校验 + if (storeId == null) { + log.warn("查询合同状态和URL失败:店铺ID为空"); + return null; + } + + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(EsignContract::getStore_id, storeId) + .eq(EsignContract::getStatus, CommonConstant.Enable) + .eq(EsignContract::getSign_flow_status, CommonConstant.CONTRACT_SIGN_STA_FINISH) + .isNotNull(EsignContract::getLocal_contract_url) + .ne(EsignContract::getLocal_contract_url, "") + .select(EsignContract::getSign_flow_status, EsignContract::getLocal_contract_url); + + return findOne(queryWrapper); + } catch (Exception e) { + log.error("根据店铺ID查询合同状态和URL异常,店铺ID: {}", storeId, e); + return null; + } + } + + @Override public EsignContract getEsignContractBySignFlowId(String signFlowId) { QueryWrapper queryWrapper = new QueryWrapper<>(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignPlatformInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignPlatformInfoServiceImpl.java index 6aa55a76..6c3d3c7a 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignPlatformInfoServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/esign/service/impl/EsignPlatformInfoServiceImpl.java @@ -149,12 +149,7 @@ public class EsignPlatformInfoServiceImpl extends BaseServiceImpl esignPlatformInfos = list(queryWrapper); - if (CollectionUtil.isEmpty(esignPlatformInfos)) { - return null; - } - - return esignPlatformInfos.get(0); + return findOne(queryWrapper); } /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java index 265453ad..984bf952 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/controller/mobile/LakalaController.java @@ -14,13 +14,13 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.service.impl.BaseControllerImpl; import com.suisung.mall.shop.lakala.service.LakalaApiService; import com.suisung.mall.shop.lakala.service.LklLedgerEcService; -import com.suisung.mall.shop.lakala.service.LklLedgerReceiverService; import com.suisung.mall.shop.library.service.LibraryProductService; import com.suisung.mall.shop.message.service.MqMessageService; import com.suisung.mall.shop.message.service.PushMessageService; import com.suisung.mall.shop.order.service.ShopOrderBaseService; import com.suisung.mall.shop.order.service.ShopOrderReturnService; import com.suisung.mall.shop.sfexpress.service.SFExpressApiService; +import com.suisung.mall.shop.store.service.ShopStorePrinterService; import com.suisung.mall.shop.store.service.ShopStoreSameCityTransportBaseService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -78,13 +78,11 @@ public class LakalaController extends BaseControllerImpl { @Lazy @Resource - private LakalaApiService lakalaApiService; + private ShopStorePrinterService shopStorePrinterService; @Resource private LklLedgerEcService lklLedgerEcService; - @Resource - private LklLedgerReceiverService lklLedgerReceiverService; @ApiOperation(value = "测试案例", notes = "测试案例") @RequestMapping(value = "/testcase", method = RequestMethod.POST) @@ -94,7 +92,9 @@ public class LakalaController extends BaseControllerImpl { // return lakalaApiService.ewalletWithDrawNotify(null, paramsJSON.getStr("a"), paramsJSON.getStr("b")); - return lakalaApiService.tradeQuery(paramsJSON.getInt("storeId"), paramsJSON.getStr("orderId")); + // return lakalaApiService.tradeQuery(paramsJSON.getInt("storeId"), paramsJSON.getStr("orderId")); + + return shopStorePrinterService.printShopStoreReturnOrder(paramsJSON.getStr("returnId")); } @ApiOperation(value = "批量发送推送消息 - 测试案例", notes = "批量发送推送消息 - 测试案例") @@ -119,8 +119,8 @@ public class LakalaController extends BaseControllerImpl { return lakalaPayService.getBankCardBin(paramsJSON.getStr("bankCardNo")); } - @ApiOperation(value = "发货类交易确认收货通知", notes = "发货类交易确认收货通知 https://o.lakala.com/#/home/document/detail?id=1003") - @RequestMapping(value = "/trans/receive/completeNotify", method = RequestMethod.POST) + @ApiOperation(value = "接收拉卡拉发货类交易确认收货通知", notes = "接收拉卡拉发货类交易确认收货通知 https://o.lakala.com/#/home/document/detail?id=1003") + @RequestMapping(value = "/trans/receive/completeNotify", method = {RequestMethod.POST, RequestMethod.GET}) public ResponseEntity receiveCompleteNotify(HttpServletRequest request) { // 完整地址: https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify JSONObject resp = lakalaPayService.receiveCompleteNotify(request); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java index 8f96e232..bafd6aef 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LakalaApiServiceImpl.java @@ -27,6 +27,7 @@ import com.suisung.mall.common.api.CommonResult; import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.exception.ApiException; import com.suisung.mall.common.feignService.ShopService; +import com.suisung.mall.common.modules.esign.EsignPlatformInfo; import com.suisung.mall.common.modules.lakala.*; import com.suisung.mall.common.modules.order.ShopOrderLkl; import com.suisung.mall.common.modules.store.ShopMchEntry; @@ -54,6 +55,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @@ -470,7 +472,17 @@ public class LakalaApiServiceImpl implements LakalaApiService { // 7. 构建合同参数 LocalDate today = LocalDate.now(); String signDate = DateTimeUtils.formatLocalDate(today, "yyyy-MM-dd"); + + // 2. 获取平台方信息 String platformName = "桂平发发网络有限公司"; + String platMobile = "17777525395"; // 平台联系电话 + EsignPlatformInfo esignPlatformInfo = esignPlatformInfoService.getEsignPlatformInfo(0, ""); + if (!ObjectUtils.isEmpty(esignPlatformInfo) + && StrUtil.isNotBlank(esignPlatformInfo.getLicense_company()) + && StrUtil.isNotBlank(esignPlatformInfo.getTelephone())) { + platformName = esignPlatformInfo.getLicense_company(); + platMobile = esignPlatformInfo.getTelephone(); + } JSONObject ecParams = new JSONObject(); ecParams.put("A1", isQy ? shopMchEntry.getBiz_license_company() : shopMchEntry.getAccount_holder_name()); @@ -511,7 +523,7 @@ public class LakalaApiServiceImpl implements LakalaApiService { ecParams.put("D1", shopMchEntry.getBank_name()); ecParams.put("D2", signDate); ecParams.put("D4", platformName); - ecParams.put("D5", contractMobile); + ecParams.put("D5", platMobile); ecParams.put("D7", signDate); ecParams.put("D9", signDate); ecParams.put("D11", signDate); @@ -811,6 +823,14 @@ public class LakalaApiServiceImpl implements LakalaApiService { Pair checkResult = LakalaUtil.chkLklApiNotifySign(request, lklNotifyCerPath, false); if (!checkResult.getFirst()) { log.warn("[确认收货通知] 验签失败: {}", checkResult.getSecond()); + + try { + // 不管成功与否,写入确认收货通知日志,后续补偿遗漏分账的材料 + lklReceiveNotifyLogService.addOrUpdate(LakalaUtil.getBody(request)); + } catch (Exception e) { + log.error("[确认收货通知] 写入确认收货通知日志失败", e); + } + return JSONUtil.createObj().set("code", "FAIL").set("message", checkResult.getSecond()); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/MqMessageServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/MqMessageServiceImpl.java index 2bf95821..f82caac7 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/MqMessageServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/message/service/impl/MqMessageServiceImpl.java @@ -54,27 +54,9 @@ public class MqMessageServiceImpl extends BaseServiceImpl { String createNextSeq(String prefix); + /** + * 创建下一个编号(键值对) + * + * @param prefix 前缀 + * @return 序号和完整编号 + */ + Pair createNextSeqPair(String prefix); + Long createNextNo(String prefix); List batchCreateNextNo(String seqName, int batchSize); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java index 20b9bb23..a2ead0e8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/number/service/impl/ShopNumberSeqServiceImpl.java @@ -27,6 +27,7 @@ import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; +import org.springframework.data.util.Pair; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -125,6 +126,44 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl createNextSeqPair(String prefix) { + String ymd = DateUtil.format(new Date(), "yyyyMMdd"); + String id = String.format("%s_%s_", prefix, ymd); + ShopNumberSeq shopNumberSeq = this.baseMapper.selectById(id); + if (shopNumberSeq == null) { + shopNumberSeq = new ShopNumberSeq(); + shopNumberSeq.setPrefix(id); + shopNumberSeq.setNumber(1L); + if (!save(shopNumberSeq)) { + return null; + } + } + + String order_id = String.format("%s_%s_%s", prefix, ymd, shopNumberSeq.getNumber()); + shopNumberSeq.setPrefix(id); + boolean flag = edit(shopNumberSeq); + if (flag) { + return Pair.of(shopNumberSeq.getNumber(), order_id); + } + + return null; + } + /** * 得到下一个Id * @@ -280,12 +319,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl getBatchSpecItemId(int batchSize) { + public List getBatchSpecItemId(int batchSize) { // 定义锁的key,这个key在所有服务实例中必须一致 String lockKey = "LOCK:" + RedisKey.STOREDATASPECITEMID; // 2. 获取分布式锁对象 @@ -330,12 +369,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl getBatchLibraryProductId(int batchSize) { + public List getBatchLibraryProductId(int batchSize) { // 定义锁的key,这个key在所有服务实例中必须一致 String lockKey = "LOCK:" + RedisKey.STOREDATALIBRARYID; // 2. 获取分布式锁对象 @@ -434,7 +473,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.select("max(page_id) as page_id"); ShopPageBase shopPageBase = shopPageBaseService.getOne(queryWrapper); @@ -493,41 +532,41 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl shopNumberSeqList=new ArrayList<>(); - QueryWrapper baseWrapper=new QueryWrapper<>(); + private void syncPrimaryKey() { + List shopNumberSeqList = new ArrayList<>(); + QueryWrapper baseWrapper = new QueryWrapper<>(); baseWrapper.select("MAX(product_id) as product_id"); - ShopProductBase shopProductBase= shopProductBaseService.getOne(baseWrapper); - Long productId= shopProductBase.getProduct_id(); + ShopProductBase shopProductBase = shopProductBaseService.getOne(baseWrapper); + Long productId = shopProductBase.getProduct_id(); //QueryWrapper baseSeWrapper=new QueryWrapper(); //baseSeWrapper.eq("prefix", "product_id"); - ShopNumberSeq shopNumberSeqBase= new ShopNumberSeq(); + ShopNumberSeq shopNumberSeqBase = new ShopNumberSeq(); shopNumberSeqBase.setPrefix("product_id"); shopNumberSeqBase.setNumber(productId); //shopNumberSeqServiceImpl.edit(shopNumberSeqBase,baseWrapper); //查询产品item - QueryWrapper itemQuery=new QueryWrapper<>(); + QueryWrapper itemQuery = new QueryWrapper<>(); itemQuery.select("MAX(item_id) as item_id"); - ShopProductItem shopProductItem= shopProductItemService.getOne(itemQuery); - Long itemtId=1L; - if(null!=shopProductItem){ - itemtId= shopProductItem.getItem_id(); + ShopProductItem shopProductItem = shopProductItemService.getOne(itemQuery); + Long itemtId = 1L; + if (null != shopProductItem) { + itemtId = shopProductItem.getItem_id(); } // QueryWrapper itemWrapper=new QueryWrapper(); //itemWrapper.eq("prefix", "item_id"); - ShopNumberSeq shopNumberSeqItem= new ShopNumberSeq(); + ShopNumberSeq shopNumberSeqItem = new ShopNumberSeq(); shopNumberSeqItem.setNumber(itemtId); shopNumberSeqItem.setPrefix("item_id"); shopNumberSeqList.add(shopNumberSeqBase); @@ -537,5 +576,4 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl { List> statisticCountSeller(@Param("end") Date end, @Param("days") int days, @Param("store_id") int store_id); IPage getReturnGroupByOrderId(Page page, @Param("map") Map params); + + /** + * 根据退货订单id获取退货订单打印信息 + * + * @param orderId + * @param returnId + * @return + */ + OrderPrintVO fetchReturnOrderPrintInfo(@Param("orderId") String orderId, @Param("returnId") String returnId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java index e60c32c2..41a68fe5 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderBaseService.java @@ -522,7 +522,7 @@ public interface ShopOrderBaseService extends IBaseService { * @param payState 支付状态,参考StateCode.ORDER_PAID_STATE_YES * @return */ - Map getOrderPrintInfo(Integer storeId, String orderId, Integer payState); + Map fetchOrderPrintInfo(Integer storeId, String orderId, Integer payState); /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java index b0027f30..968209fd 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderInfoService.java @@ -143,4 +143,13 @@ public interface ShopOrderInfoService extends IBaseService { * @return */ List genBookingOrderArgList(String storeId); + + /** + * 获取某个活动订单成功数量 + * + * @param activityId 活动ID + * @param activityTypeId 活动类型ID, 可选参数 + * @return + */ + long fetchActivityOrderSuccessCount(String activityId, String activityTypeId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderItemService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderItemService.java index 67018791..2ae3cf47 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderItemService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderItemService.java @@ -2,7 +2,7 @@ package com.suisung.mall.shop.order.service; import com.suisung.mall.common.modules.order.ShopOrderItem; import com.suisung.mall.common.modules.pay.dto.ItemActivityInfoDTO; -import com.suisung.mall.common.pojo.vo.ShopStoreOrderProductPrintVO; +import com.suisung.mall.common.pojo.vo.OrderItemPrintVO; import com.suisung.mall.core.web.service.IBaseService; import java.util.List; @@ -30,5 +30,5 @@ public interface ShopOrderItemService extends IBaseService { * @param orderId * @return */ - List selectOrderItemPrintInfo(String orderId); + List selectOrderItemPrintInfo(String orderId); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java index 211916bc..e00dcb59 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/ShopOrderReturnService.java @@ -7,6 +7,7 @@ import com.suisung.mall.common.modules.order.ShopOrderItem; import com.suisung.mall.common.modules.order.ShopOrderReturn; import com.suisung.mall.common.modules.order.ShopOrderReturnItem; import com.suisung.mall.common.modules.product.ShopProductIndex; +import com.suisung.mall.common.pojo.vo.OrderPrintVO; import com.suisung.mall.core.web.service.IBaseService; import com.suisung.mall.shop.order.vo.OrderReturnInputVo; @@ -225,4 +226,14 @@ public interface ShopOrderReturnService extends IBaseService { CommonResult doRefundForMch(JSONObject params); + /** + * 根据退货订单id获取退货订单打印信息 + * + * @param orderId + * @param returnId + * @return + */ + OrderPrintVO fetchReturnOrderPrintInfo(String orderId, String returnId); + + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index 36313c8c..aece29b9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -65,7 +65,7 @@ import com.suisung.mall.common.pojo.to.MsgTO; import com.suisung.mall.common.pojo.to.PayMoneyTO; import com.suisung.mall.common.pojo.to.PayPointTO; import com.suisung.mall.common.pojo.to.UserLevelTO; -import com.suisung.mall.common.pojo.vo.ShopStoreOrderProductPrintVO; +import com.suisung.mall.common.pojo.vo.OrderItemPrintVO; import com.suisung.mall.common.service.MessageService; import com.suisung.mall.common.service.impl.CommonService; import com.suisung.mall.common.utils.*; @@ -1580,20 +1580,6 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl canDoOrderCutPriceActivity = shopActivityCutpriceService.canDoOrderCutPriceActivity(acId, userId); - if (!canDoOrderCutPriceActivity.getFirst()) { - logger.error(canDoOrderCutPriceActivity.getSecond()); - throw new ApiException(I18nUtil._("砍价活动未达标,请继续努力,再提交订单。")); - } - } // 注:(重要)邮费检测和计算 dealWithCalFee(calFreight, cartData, chainId, isEdu); @@ -1621,22 +1607,29 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl pair = shopOrderInfoService.checkBookingOrderArgs(checkedStore, bookingState, bookingBeginTime, bookingEndTime); -// if (!pair.getFirst()) { -// throw new ApiException(I18nUtil._(pair.getSecond())); -// } -// -// cartData.put("booking_state", bookingState); -// cartData.put("booking_begin_time", bookingBeginTime); -// cartData.put("booking_end_time", bookingEndTime); -// cartData.put("booking_at", bookingAt); -// } + // 判断是否砍价购买 + Integer acId = getParameter("ac_id", Integer.class); + // 判断是否为砍价活动 + Boolean isCutPriceActivity = shopStoreActivityBaseService.isCutPriceActivity(activityId); + if (CheckUtil.isNotEmpty(activityId) && isCutPriceActivity) { + // 判断砍价商品库存是否已售罄? + if (shopStoreActivityBaseService.isActProdSoldOut(activityId)) { + logger.error("检查商品库存:{}", activityId); + throw new ApiException(I18nUtil._("砍价商品已售罄,请选择其他商品。")); + } + + // 校验砍价活动,用户是否可以立即购买了? + Pair canDoOrderCutPriceActivity = shopActivityCutpriceService.canDoOrderCutPriceActivity(activityId, userId); + if (!canDoOrderCutPriceActivity.getFirst()) { + logger.error("检查活动是否达标:{}", canDoOrderCutPriceActivity.getSecond()); + throw new ApiException(I18nUtil._("活动已过期或未砍到底价,暂无法提交订单。")); + } + + cartData.put("ac_id", acId); // shop_activity_cutprice 活动参与记录的自增id + cartData.put("activity_id", activityId); + cartData.put("activity_type", StateCode.ACTIVITY_TYPE_CUTPRICE); + } + // 添加保存订单(关键方法) List orderIdRow = addOrder(cartData, true, false, null); @@ -1662,6 +1655,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl historyQueryWrapper = new QueryWrapper<>(); @@ -1673,10 +1667,6 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl orderIds) { + public void processOrderJumpPathAsync(List orderIds) { if (CollectionUtils.isEmpty(orderIds)) { return; } @@ -5211,15 +5201,15 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl seqPair = shopNumberSeqService.createNextSeqPair(type_code);// 位数的序号: DD_20251205_1 2025-12-05 1 + String order_id = seqPair.getSecond(); + Long seqNo = seqPair.getFirst(); // 序号: DD_20251205_1 得出 1 RootContext.bind(xid); List item_rows = new ArrayList(); @@ -6656,6 +6668,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl shopStoreOrderProductPrintVOList = shopOrderItemService.selectOrderItemPrintInfo(orderId); - if (CollUtil.isEmpty(shopStoreOrderProductPrintVOList)) { + List orderItemPrintVOList = shopOrderItemService.selectOrderItemPrintInfo(orderId); + if (CollUtil.isEmpty(orderItemPrintVOList)) { return null; } // 重构实体类,处理标题,数量,价格适应长度 - List orderItems = new ArrayList<>(); - for (ShopStoreOrderProductPrintVO ent : shopStoreOrderProductPrintVOList) { + List orderItems = new ArrayList<>(); + for (OrderItemPrintVO ent : orderItemPrintVOList) { orderItems.add(ent.rebuild()); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java index c5699e6d..ad1c8b51 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderInfoServiceImpl.java @@ -7,6 +7,7 @@ import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.suisung.mall.common.api.CommonResult; @@ -206,7 +207,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); // 已发货,已签收 queryWrapper.in("order_state_id", StateCode.ORDER_STATE_SHIPPED, StateCode.ORDER_STATE_RECEIVED); - queryWrapper.eq("order_is_received",CommonConstant.Disable); + queryWrapper.eq("order_is_received", CommonConstant.Disable); // 默认7天,自动收货 // 配置了发货或已签收的订单,1天自动收货 Float order_autofinish_time = accountBaseConfigService.getConfig("order_autofinish_time", 7f); @@ -371,8 +372,8 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ShopOrderInfo::getActivity_id, activityId); + + // 如果activityTypeId不为空,则添加条件 + if (StrUtil.isNotBlank(activityTypeId)) { + queryWrapper.eq(ShopOrderInfo::getActivity_type_id, activityTypeId); + } + + // 添加订单状态条件 + List successOrderStates = Arrays.asList( + StateCode.ORDER_STATE_WAIT_PAID, + StateCode.ORDER_STATE_PICKING, + StateCode.ORDER_STATE_WAIT_SHIPPING, + StateCode.ORDER_STATE_SHIPPED, + StateCode.ORDER_STATE_RECEIVED, + StateCode.ORDER_STATE_FINISH); + + queryWrapper.in(ShopOrderInfo::getOrder_state_id, successOrderStates); + + try { + return count(queryWrapper); + } catch (Exception e) { + logger.error("获取活动订单出错, activityId: {}, activityTypeId: {}", + activityId, activityTypeId, e); + return 0L; + } + } + /** * 根据一个或多个店铺id获取有效店铺的有效营业时间段 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java index d2d4fd49..3ef2a698 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderItemServiceImpl.java @@ -17,7 +17,7 @@ import com.suisung.mall.common.modules.order.ShopOrderDeliveryAddress; import com.suisung.mall.common.modules.order.ShopOrderInfo; import com.suisung.mall.common.modules.order.ShopOrderItem; import com.suisung.mall.common.modules.pay.dto.ItemActivityInfoDTO; -import com.suisung.mall.common.pojo.vo.ShopStoreOrderProductPrintVO; +import com.suisung.mall.common.pojo.vo.OrderItemPrintVO; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.activity.service.ShopActivityGroupbookingHistoryService; import com.suisung.mall.shop.order.mapper.ShopOrderItemMapper; @@ -226,7 +226,7 @@ public class ShopOrderItemServiceImpl extends BaseServiceImpl selectOrderItemPrintInfo(String orderId) { + public List selectOrderItemPrintInfo(String orderId) { // 输入验证 if (StrUtil.isBlank(orderId)) { return Collections.emptyList(); @@ -238,7 +238,7 @@ public class ShopOrderItemServiceImpl extends BaseServiceImpl { - ShopStoreOrderProductPrintVO vo = new ShopStoreOrderProductPrintVO(); + OrderItemPrintVO vo = new OrderItemPrintVO(); vo.setProduct_sn(Convert.toStr(map.get("product_sn"))); vo.setItem_name(Convert.toStr(map.get("item_name"))); vo.setOrder_item_amount(Convert.toBigDecimal(map.get("order_item_amount"))); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLogisticsServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLogisticsServiceImpl.java index 927670af..8c85cad9 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLogisticsServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderLogisticsServiceImpl.java @@ -268,7 +268,9 @@ public class ShopOrderLogisticsServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("order_id", shopOrderId); + ShopOrderReturn shopOrderReturn = findOne(queryWrapper); + if (shopOrderReturn == null) { + logger.error("[顺丰超时自动退款] 订单信息异常,未找到退货单: shopOrderId={}", shopOrderId); + return false; + } + + if (ObjectUtil.equal(shopOrderReturn.getReturn_state_id(), StateCode.RETURN_PROCESS_FINISH) + || ObjectUtil.equal(shopOrderReturn.getReturn_is_paid(), CommonConstant.Enable)) { + logger.warn("[顺丰超时自动退款] 订单之前已处理完成,请勿重复处理: shopOrderId={}", shopOrderId); + return true; + } + // 对已存在部分退款的订单,进行剩余商品的全部退款 CommonResult commonResult = addRemainingItems(shopOrderId, true, remark); commonResult.checkFenResult(); logger.debug("[顺丰超时自动退款] 整单退货申请创建成功: shopOrderId={}", shopOrderId); - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("order_id", shopOrderId); - ShopOrderReturn shopOrderReturn = findOne(queryWrapper); - - if (shopOrderReturn == null) { - logger.error("[顺丰超时自动退款] 订单信息异常,未找到退货单: shopOrderId={}", shopOrderId); - throw new ApiException(I18nUtil._("订单信息异常!")); - } - shopOrderReturn.setReturn_flag(0); // 0-不用退货;1-需要退货 shopOrderReturn.setReturn_buyer_message(remark); shopOrderReturn.setReturn_store_message(remark); @@ -1377,7 +1391,9 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl Convert.toInt(getCurrentUser().getStore_id())); - List return_ids = Convert.toList(String.class, shopOrderReturn.getReturn_id()); + String returnId = shopOrderReturn.getReturn_id(); + + List return_ids = Convert.toList(String.class, returnId); List orderReturns = gets(return_ids); if (!CheckUtil.checkDataRights(store_id, orderReturns, ShopOrderReturn::getStore_id)) { @@ -1415,6 +1431,9 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl returnItemQueryWrapper = new QueryWrapper<>(); returnItemQueryWrapper.in("return_id", return_ids); List returnItems = orderReturnItemService.find(returnItemQueryWrapper); - QueryWrapper shopOrderReturnQueryWrapper= new QueryWrapper<>(); + QueryWrapper shopOrderReturnQueryWrapper = new QueryWrapper<>(); shopOrderReturnQueryWrapper.in("return_id", return_ids); - List shopOrderReturnList= shopOrderReturnService.find(shopOrderReturnQueryWrapper); - Map shopOrderReturnMap=new HashMap<>(); - if(!shopOrderReturnList.isEmpty()){ - shopOrderReturnMap=shopOrderReturnList.stream().collect(Collectors.toMap(ShopOrderReturn::getReturn_id, s->s.getUpdated_at().getTime())); + List shopOrderReturnList = shopOrderReturnService.find(shopOrderReturnQueryWrapper); + Map shopOrderReturnMap = new HashMap<>(); + if (!shopOrderReturnList.isEmpty()) { + shopOrderReturnMap = shopOrderReturnList.stream().collect(Collectors.toMap(ShopOrderReturn::getReturn_id, s -> s.getUpdated_at().getTime())); } if (CollUtil.isNotEmpty(returnItems)) { for (ShopOrderReturnItem returnItem : returnItems) { @@ -1637,17 +1656,17 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); String item_src_id = productItem.getItem_src_id(); - String mapKey=item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getOrder_item_unit_price(); - if(null!=shopOrderReturnMap.get(returnItem.getReturn_id())){ - mapKey=mapKey+"-" + shopOrderReturnMap.get(returnItem.getReturn_id());//时间 + String mapKey = item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getOrder_item_unit_price(); + if (null != shopOrderReturnMap.get(returnItem.getReturn_id())) { + mapKey = mapKey + "-" + shopOrderReturnMap.get(returnItem.getReturn_id());//时间 } stockDeltaMap.put(mapKey, returnNum); syncThirdDataService.incrProductStockToRedis(stockDeltaMap, returnItem.getReturn_item_subtotal()); - logger.info("退货返回给思迅,存入redis成功,item_src_id:{},订单号:{},数量:{},mapKey:{}", item_src_id, shopOrderItem.getOrder_id(), returnNum,mapKey); + logger.info("退货返回给思迅,存入redis成功,item_src_id:{},订单号:{},数量:{},mapKey:{}", item_src_id, shopOrderItem.getOrder_id(), returnNum, mapKey); } else { logger.warn("退货数量为空,无法增加库存,订单项ID: {}", orderItemId); } - }else {//没有出库也要做思迅出库,在客户端计算支付流水,防止部分数据有出无进 + } else {//没有出库也要做思迅出库,在客户端计算支付流水,防止部分数据有出无进 Long orderItemId = returnItem.getOrder_item_id(); ShopOrderItem shopOrderItem = shopOrderItemService.get(orderItemId); if (shopOrderItem == null) { @@ -1669,13 +1688,13 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); String item_src_id = productItem.getItem_src_id(); - String mapKey=item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getOrder_item_unit_price(); - if(null!=shopOrderReturnMap.get(returnItem.getReturn_id())){ - mapKey=mapKey+"-" + shopOrderReturnMap.get(returnItem.getReturn_id());//时间 + String mapKey = item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getOrder_item_unit_price(); + if (null != shopOrderReturnMap.get(returnItem.getReturn_id())) { + mapKey = mapKey + "-" + shopOrderReturnMap.get(returnItem.getReturn_id());//时间 } stockDeltaMap.put(mapKey, returnNum); syncThirdDataService.incrProductStockToRedis(stockDeltaMap, returnItem.getReturn_item_subtotal()); - logger.info("未出库退货返回给思迅,存入redis成功,item_src_id:{},订单号:{},数量:{},mapKey:{}", item_src_id, shopOrderItem.getOrder_id(), returnNum,mapKey); + logger.info("未出库退货返回给思迅,存入redis成功,item_src_id:{},订单号:{},数量:{},mapKey:{}", item_src_id, shopOrderItem.getOrder_id(), returnNum, mapKey); } } } @@ -1872,7 +1891,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl updateWrapper = new QueryWrapper<>(); @@ -2564,7 +2583,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl itemQueryWrapper = new QueryWrapper<>(); itemQueryWrapper.eq("order_id", orderId); List allOrderItems = shopOrderItemService.find(itemQueryWrapper); - if (CollectionUtil.isEmpty(allOrderItems)) { logger.warn("订单剩余商品退款申请失败:订单商品表为空,订单ID: {}", orderId); throw new ApiException(I18nUtil._("订单商品表为空!")); @@ -2875,7 +2893,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl orderItems = shopOrderItemService.find(new QueryWrapper().eq("order_id", orderId)); @@ -2983,16 +3001,16 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl params); + /** + * 订单实时信息查询 + * https://openic.sf-express.com/open/api/external/getorderstatus?sign=$sign + * + * @param sfOrderId + * @return + */ + ThirdApiRes getOrderStatus(String sfOrderId); // *********** 顺丰同城回调相关业务 **************** @@ -194,18 +202,6 @@ public interface SFExpressApiService { */ ThirdApiRes receiveOrderCompleteNotify(String jsonData, String sign); -// -// /** -// * 个推推送消息到员工 -// * -// * @param storeId -// * @param orderId -// * @param message -// * @param payloadJson -// * @return -// */ -// void pushMessageToStoreEmployee(Integer storeId, String orderId, String message, String payloadJson); - /** * 商家自行配送发货 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java index 8604ef80..b4e5af13 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/sfexpress/service/impl/SFExpressApiServiceImpl.java @@ -185,42 +185,10 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { return Pair.of(false, "联系人手机号不能为空"); } -// AddressParseResultTO addressParseResultTO = AddressUtil.parseAddress(shopMchEntry.getStore_address()); -// // 解析城市名称 -// String cityName = "桂平市"; // 默认城市 -// -// // 去掉省市区的详细地址 -// String storeAddress = addressParseResultTO.getDetailAddress(); -// -// if (StrUtil.isNotBlank(shopMchEntry.getStore_area())) { -// String[] areaNames = shopMchEntry.getStore_area().split("/"); -// if (areaNames.length >= 3) { -// cityName = areaNames[areaNames.length - 1]; -// } else { -// cityName = shopMchEntry.getStore_area().replace("/", ""); -// } -// } else { -// cityName = addressParseResultTO.getCity(); -// } -// -// // 如果解析后城市名为空,使用默认值 -// if (StrUtil.isBlank(cityName)) { -// cityName = "桂平市"; -// logger.warn("[顺丰] 城市名为空,使用默认城市: {}", cityName); -// } else { -// logger.debug("[顺丰] 解析得到城市名: {}", cityName); -// } -// -// // 为了其他顺丰店同名,店铺名称加上[门店ID]; 如:xxxx[xxxx] 聚万家生鲜超市[69] -// String shopStoreName = String.format("%s[%s]", shopMchEntry.getStore_name(), shopMchEntry.getStore_id()); - // 调用创建店铺方法 Pair result = createSfExpressShop( mchId, Convert.toInt(shopMchEntry.getStore_id()), -// shopStoreName, -// cityName, -// storeAddress, shopMchEntry.getContact_name(), contactMobile, shopMchEntry.getStore_longitude(), @@ -707,9 +675,10 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { public ThirdApiRes cancelOrder(String orderId, Integer cancelCode, String cancelReason) { Map params = buildCommonParams(); params.put("order_id", shopStoreSfOrderService.getSfOrderIdByShopOrderId(orderId)); // 商家 orderId 转 顺丰的订单号 + params.put("cancel_type", 1); //1、顺丰订单号 2、商家订单号 if (StrUtil.isNotBlank(cancelReason) && cancelCode != null) { - params.put("cancel_code", orderId); - params.put("cancel_reason", orderId); + params.put("cancel_code", cancelCode); + params.put("cancel_reason", cancelReason); } return cancelOrder(params); } @@ -718,77 +687,107 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { * 取消订单 * * @param params 综合参数,请参考:https://openic.sf-express.com/open/api/docs/index/#/apidoc - * @return + * @return ThirdApiRes 取消订单结果 */ @Override @Transactional public ThirdApiRes cancelOrder(Map params) { - // TODO 检验用户权限 + logger.info("[取消顺丰订单] 开始取消顺丰订单流程"); + // 1. 参数校验 if (params == null || ObjectUtil.isEmpty(params.get("order_id"))) { + logger.warn("[取消顺丰订单] 取消订单参数校验失败: 参数为空或缺少order_id"); return new ThirdApiRes().fail(1003, "请求参数有误!"); } - // 转换 json 字符串参数 - String sfOrderId = params.get("order_id").toString(); // 这是顺丰的订单号,不是商城的订单号 - params.putAll(buildCommonParams()); + try { + // 2. 获取顺丰订单号 + String sfOrderId = params.get("order_id").toString(); + logger.debug("[取消顺丰订单] 准备取消订单: sfOrderId={}", sfOrderId); - String paramJSON = JsonUtil.toJSONString(params); + // 先实时查询顺丰订单状态 + ThirdApiRes thirdApiRes = getOrderStatus(sfOrderId); + if (thirdApiRes != null && ObjectUtil.equal(thirdApiRes.getError_code(), 0)) { + Object resultObj = thirdApiRes.getResult(); + if (resultObj != null) { + JSONObject result = JSONUtil.parseObj(resultObj); + Integer orderStatus = result.getInt("order_status"); + if (orderStatus != null && + (ObjectUtil.equal(orderStatus, SFExpressConstant.Cons_CanceledOrder) || + ObjectUtil.equal(orderStatus, SFExpressConstant.Cons_CancelingOrder))) { + return new ThirdApiRes().success("订单已取消过!"); + } + } + } - // 根据参数生成请求签名 - String send_url = buildUrl("cancelorder", paramJSON); - String retRespStr = HttpUtil.post(send_url, paramJSON); - if (StrUtil.isBlank(retRespStr)) { - logger.error("顺丰同城:取消订单异常,无返回值!"); - return new ThirdApiRes().fail(2, "顺丰同城:无返回值!"); + // 3. 添加公共参数 + params.putAll(buildCommonParams()); + String paramJSON = JsonUtil.toJSONString(params); + + // 4. 调用顺丰取消订单接口 + String sendUrl = buildUrl("cancelorder", paramJSON); + logger.debug("[取消顺丰订单] 调用取消订单接口: url={}, params={}", sendUrl, paramJSON); + + String responseStr = HttpUtil.post(sendUrl, paramJSON); + if (StrUtil.isBlank(responseStr)) { + logger.error("[取消顺丰订单] 取消订单接口调用失败: 无返回值, sfOrderId={}", sfOrderId); + return new ThirdApiRes().fail(2, "顺丰同城:无返回值!"); + } + + // 5. 解析接口响应 + ThirdApiRes sfExpressApiRes = JsonUtil.json2object(responseStr, ThirdApiRes.class); + if (sfExpressApiRes == null) { + logger.error("[取消顺丰订单] 取消订单接口响应解析失败: {}, sfOrderId={}", responseStr, sfOrderId); + return new ThirdApiRes().fail(2, "顺丰同城:响应解析失败!"); + } + + // 6. 检查接口调用结果 + if (!sfExpressApiRes.getError_code().equals(0)) { + logger.error("[取消顺丰订单] 取消订单接口调用失败: errorCode={}, errorMsg={}, sfOrderId={}", + sfExpressApiRes.getError_code(), sfExpressApiRes.getError_msg(), sfOrderId); + return new ThirdApiRes().fail(2, sfExpressApiRes.getError_msg()); + } + + logger.info("[取消顺丰订单] 顺丰接口取消订单成功: sfOrderId={}", sfOrderId); + + // 7. 检查本地订单状态 + ShopStoreSfOrder existingOrder = shopStoreSfOrderService.getBySfOrderId(sfOrderId); + if (existingOrder == null) { + logger.error("[取消顺丰订单] 本地订单不存在: sfOrderId={}", sfOrderId); + return new ThirdApiRes().fail(2, "订单不存在!"); + } + + // 8. 检查订单是否已经取消 + if (existingOrder.getOrder_status() != null && + (existingOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELED) || + existingOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELING))) { + logger.info("[取消顺丰订单] 订单已处于取消状态,无需重复操作: sfOrderId={}, status={}", + sfOrderId, existingOrder.getOrder_status()); + return new ThirdApiRes().success("订单已取消过!"); + } + + // 9. 更新顺丰订单状态为已取消 + ShopStoreSfOrder updateOrder = new ShopStoreSfOrder(); + updateOrder.setSf_order_id(existingOrder.getSf_order_id()); + updateOrder.setOrder_status(StateCode.SF_ORDER_STATUS_CANCELED); + updateOrder.setStatus_desc("线上商城发起取消订单"); + + Boolean updateSuccess = shopStoreSfOrderService.updateShopStoreSfOrderStatus(updateOrder); + if (!updateSuccess) { + logger.error("[取消顺丰订单] 更新本地订单状态失败: sfOrderId={}", sfOrderId); + throw new ApiException(_("取消顺丰订单失败!")); + } + + logger.info("[取消顺丰订单] 本地订单状态更新成功: sfOrderId={}", sfOrderId); + return sfExpressApiRes; + + } catch (Exception e) { + logger.error("[取消顺丰订单] 取消订单过程中发生异常: ", e); + return new ThirdApiRes().fail(-1, "系统异常: " + e.getMessage()); } - - ThirdApiRes sfExpressApiRes = JsonUtil.json2object(retRespStr, ThirdApiRes.class); - if (sfExpressApiRes == null) { - logger.error("顺丰同城:取消订单,返回值异常!{}", retRespStr); - return new ThirdApiRes().fail(2, "顺丰同城:无返回值!"); - } - - if (!sfExpressApiRes.getError_code().equals(0)) { - logger.error("顺丰同城:取消订单失败!{}", retRespStr); - return new ThirdApiRes().fail(2, sfExpressApiRes.getError_msg()); - } - - // 判断订单的状态,是否已经取消了?已取消,不再执行 - ShopStoreSfOrder shopStoreSfOrderExist = shopStoreSfOrderService.getByShopOrderId(sfOrderId); - if (shopStoreSfOrderExist == null) { - return new ThirdApiRes().fail(2, "订单有误!"); - } - - if (shopStoreSfOrderExist.getOrder_status() != null - && (shopStoreSfOrderExist.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELED) || - (shopStoreSfOrderExist.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELING)))) { - return new ThirdApiRes().success("订单已取消过!"); - } - -// // 更改商城订单状态为:已取消,注意事务问题 -// List orderList = new ArrayList<>(); -// orderList.add(shopStoreSfOrderExist.getShop_order_id()); - // 取消订单, 流程:订单状态;积分、众宝、库存、礼包、优惠券 有就统统退还 -// Boolean success = shopOrderReturnService.sfExpressExpiredForceRefund(shopStoreSfOrderExist.getShop_order_id()); // 不检查订单付款状态 -// if (!success) { -// throw new ApiException(I18nUtil._("取消商家订单失败!")); -// } - - // 更改顺丰的订单状态 - ShopStoreSfOrder shopStoreSfOrder = new ShopStoreSfOrder(); - shopStoreSfOrder.setSf_order_id(shopStoreSfOrderExist.getSf_order_id()); - shopStoreSfOrder.setOrder_status(StateCode.SF_ORDER_STATUS_CANCELED); - shopStoreSfOrder.setStatus_desc("线上商城发起取消订单"); - Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder); - if (!success) { - throw new ApiException(_("取消顺丰订单失败!")); - } - - - return sfExpressApiRes; } + /** * 订单加小费,订单创建后,骑士未接单的情况下通过该接口对订单进行加小费,促进订单接单,截止订单完成前,都可以对订单加小费 * @@ -991,6 +990,39 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { return JsonUtil.json2object(retRespStr, ThirdApiRes.class); } + /** + * 订单实时信息查询 + * https://openic.sf-express.com/open/api/external/getorderstatus?sign=$sign + * + * @param sfOrderId + * @return + */ + @Override + public ThirdApiRes getOrderStatus(String sfOrderId) { + if (StrUtil.isBlank(sfOrderId)) { + return new ThirdApiRes().fail(1003, "缺少必要参数!"); + } + + Map params = buildCommonParams(); + params.put("order_id", sfOrderId); + params.put("order_type", 1);//查询订单ID类型:1、顺丰订单号 2、商家订单号 + + + // 转换 json 字符串参数 + String paramJSON = JsonUtil.toJSONString(params); + logger.debug("订单实时信息查询:" + paramJSON); + + // 根据参数生成请求签名 + String send_url = buildUrl("getorderstatus", paramJSON); + String retRespStr = HttpUtil.post(send_url, paramJSON); + if (StrUtil.isBlank(retRespStr)) { + logger.error("顺丰同城:订单实时信息查询,无返回值!"); + return new ThirdApiRes().fail(-1, "顺丰同城:无返回值!"); + } + + return JsonUtil.json2object(retRespStr, ThirdApiRes.class); + } + /** * 接收顺丰原因订单取消回调 @@ -1170,10 +1202,10 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { String order_id = shopStoreSfOrder.getShop_order_id(); itemQueryWrapper.eq("order_id", order_id); List order_item_rows = shopOrderItemService.find(itemQueryWrapper); - ShopOrderBase shopOrderBase=shopOrderBaseService.get(order_id); - String saleTimeStr=null; - if(null!=shopOrderBase){ - saleTimeStr= String.valueOf(shopOrderBase.getOrder_time().getTime()); + ShopOrderBase shopOrderBase = shopOrderBaseService.get(order_id); + String saleTimeStr = null; + if (null != shopOrderBase) { + saleTimeStr = String.valueOf(shopOrderBase.getOrder_time().getTime()); } if (picking(order_item_rows)) { logger.info("顺丰发货商品扣减库存成功"); @@ -1183,12 +1215,12 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { Map stockDeltaMap = new HashMap<>(); String item_src_id = shopOrderItem.getItem_src_id(); Integer order_item_quantity = shopOrderItem.getOrder_item_quantity(); - String mapKey=item_src_id + "-" + shopStoreSfOrder.getShop_order_id()+"-"+shopOrderItem.getOrder_item_unit_price(); - if(StringUtils.isNotEmpty(saleTimeStr)){ - mapKey=mapKey+"-"+saleTimeStr; + String mapKey = item_src_id + "-" + shopStoreSfOrder.getShop_order_id() + "-" + shopOrderItem.getOrder_item_unit_price(); + if (StringUtils.isNotEmpty(saleTimeStr)) { + mapKey = mapKey + "-" + saleTimeStr; } stockDeltaMap.put(mapKey, -order_item_quantity); - syncThirdDataService.incrProductStockToRedis(stockDeltaMap,null); + syncThirdDataService.incrProductStockToRedis(stockDeltaMap, null); } } //出库扣减思迅库存end @@ -1356,9 +1388,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService { Map stockDeltaMap = new HashMap<>(); String item_src_id = shopOrderItem.getItem_src_id(); Integer order_item_quantity = shopOrderItem.getOrder_item_quantity(); - String mapKey=item_src_id + "-" + shopOrderItem.getOrder_id()+"-"+shopOrderItem.getOrder_item_unit_price()+"-"+shopOrderBase.getOrder_time().getTime(); + String mapKey = item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getOrder_item_unit_price() + "-" + shopOrderBase.getOrder_time().getTime(); stockDeltaMap.put(mapKey, -order_item_quantity); - syncThirdDataService.incrProductStockToRedis(stockDeltaMap,null); + syncThirdDataService.incrProductStockToRedis(stockDeltaMap, null); } } return CommonResult.success("操作成功"); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java index 2aa1bf4f..6ffbd5cb 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/controller/mobile/ShopMchEntryController.java @@ -44,7 +44,7 @@ public class ShopMchEntryController extends BaseControllerImpl { @ApiOperation(value = "测试", notes = "测试") @RequestMapping(value = "/gencon", method = RequestMethod.POST) public Object fillDocTemplate() { - return esignContractFillingFileService.fillDocTemplate("13128997057", "91450881MADEQ92533"); + return esignContractFillingFileService.fillDocTemplate(57); } @ApiOperation(value = "店铺主营分类(类目)", notes = "店铺主营分类(类目)") diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java index f1859fa4..40ce92cc 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopMchEntryService.java @@ -367,4 +367,12 @@ public interface ShopMchEntryService { * @return 包含ID路径和名称路径的字符串数组,格式为 [ID路径, 名称路径] */ String[] handleStoreDistrictInfo(String storeDistrict, String storeArea, String refStoreAddress); + + /** + * 根据店铺Id,获取拉卡拉的审核状态和下载地址 + * + * @param storeId + * @return + */ + ShopMchEntry getLklContractStatusUrl(Integer storeId); } \ No newline at end of file diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java index c457c7fe..de1cc826 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStoreActivityBaseService.java @@ -69,15 +69,15 @@ public interface ShopStoreActivityBaseService extends IBaseService { */ Date getLatestBizOpeningDate(String storeIds); + + /** + * 批量更新店铺营业状态为营业中 + * 将符合条件的"筹备中"状态店铺更新为"营业中"状态 + * + * @return Boolean 更新是否成功 + */ + Boolean batchUpdateStoreBizStateToOpening(); + // Page getMobileStoreList(Integer page, Integer rows); } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java index 833c0e35..638e06a4 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/ShopStorePrinterService.java @@ -80,6 +80,14 @@ public interface ShopStorePrinterService extends IBaseService */ Boolean printShopStoreOrder(Integer storeId, String orderId); + /** + * 退货成功后,立即打印门店的退货信息 + * + * @param returnId + * @return + */ + Boolean printShopStoreReturnOrder(String returnId); + /** * 获取店铺所有有效打印机 * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java index cddf2a7d..498727e8 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java @@ -17,6 +17,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -339,9 +340,10 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(1); tmplArgs.put("name", record.getStore_name()); // 商家店铺名 - // 【桂平发发网络】通知管理员有商家申请入驻 - if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs)) { - shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); // 【小发同城】通知管理员有商家申请入驻 + // 【小发同城】通知管理员有商家申请入驻 + if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs)) { + // 【桂平发发网络】通知管理员有商家申请入驻 + shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs); } } @@ -471,10 +473,10 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(1); tmplArgs.put("name", mchName); // 商家公司名称 - // 【桂平发发网络】通知管理员有商家申请入驻 - if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs)) { - // 尊敬的管理员,商家 ${name},提交了入驻我们平台的申请,请及时对相关资质材料予以审核,以便推进后续流程。 - shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); // 【小发同城】通知管理员有商家申请入驻 + // 【小发同城】通知管理员有商家申请入驻:尊敬的管理员,商家 ${name},提交了入驻我们平台的申请,请及时对相关资质材料予以审核,以便推进后续流程。 + if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs)) { + // 【桂平发发网络】通知管理员有商家申请入驻 + shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs); } } @@ -2510,6 +2512,36 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(ShopMchEntry::getStore_id, storeId) + .eq(ShopMchEntry::getStatus, CommonConstant.Enable) + .eq(ShopMchEntry::getApproval_status, CommonConstant.MCH_APPR_STA_PASS) + .isNotNull(ShopMchEntry::getContract_download_url) + .ne(ShopMchEntry::getContract_download_url, "") + .select(ShopMchEntry::getApproval_status, ShopMchEntry::getContract_download_url); + + return findOne(queryWrapper); + } catch (Exception e) { + log.error("根据店铺ID查询合同状态和URL异常,店铺ID: {}", storeId, e); + return null; + } + } + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java index 6d239d18..e072ea71 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStoreActivityBaseServiceImpl.java @@ -15,6 +15,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; 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.ResultCode; @@ -36,10 +37,7 @@ import com.suisung.mall.common.modules.product.ShopProductItem; import com.suisung.mall.common.modules.store.ShopStoreActivityBase; import com.suisung.mall.common.modules.store.ShopStoreActivityItem; import com.suisung.mall.common.modules.store.ShopStoreBase; -import com.suisung.mall.common.utils.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.common.utils.*; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.activity.service.*; import com.suisung.mall.shop.base.service.AccountBaseConfigService; @@ -48,6 +46,7 @@ import com.suisung.mall.shop.base.service.ShopBaseCurrencyService; import com.suisung.mall.shop.base.service.ShopBaseStateCodeService; import com.suisung.mall.shop.order.service.ShopOrderBaseService; import com.suisung.mall.shop.order.service.ShopOrderDataService; +import com.suisung.mall.shop.order.service.ShopOrderInfoService; import com.suisung.mall.shop.product.pojo.vo.ProductVo; import com.suisung.mall.shop.product.service.ShopProductBaseService; import com.suisung.mall.shop.product.service.ShopProductImageService; @@ -92,6 +91,9 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl=开始时间, 检查时间<=结束时间)", +// isValid); +// +// return isValid; +// } - if (checkTime == null) { - checkTime = new Date(); - } - - return !checkTime.before(starTime) && !checkTime.after(endTime); - } @Override public Map listsMarketing() { @@ -4433,6 +4445,100 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl() + .eq("activity_id", activity_id) + .set("activity_state", activity_state)); + + if (result) { + logger.info("活动状态更新成功,活动ID: {},新状态: {}", activity_id, activity_state); + } else { + logger.warn("活动状态更新失败,活动ID: {},状态: {}", activity_id, activity_state); + } + + return result; + } catch (Exception e) { + logger.error("活动状态更新异常,活动ID: {},状态: {}", activity_id, activity_state, e); + return false; + } + } + + /** + * 判断活动商品是否售完 + * + * @param activity_id 活动ID + * @return boolean 是否已售完 + */ + @Override + public boolean isActProdSoldOut(Integer activity_id) { + // 参数校验:检查活动ID是否有效 + if (activity_id == null || activity_id <= 0) { + logger.info("活动商品售完检查失败:无效的活动ID - {}", activity_id); + return true; // 将无效输入视为"已售完" + } + + try { + // 直接从数据库查询活动记录,只获取需要的字段以提高性能 + ShopStoreActivityBase activity = get(activity_id); + // 如果活动不存在,视为已售完 + if (activity == null) { + logger.info("活动商品售完检查:未找到活动记录,activity_id={}", activity_id); + return true; + } + + // 获取商品库存数量 + Integer productCount = activity.getProduct_count(); + logger.info("活动商品库存检查:activity_id={}, 库存数量={}", activity_id, productCount); + + // 如果库存为null或小于等于0,视为已售完 + if (productCount == null || productCount <= 0) { + logger.info("活动商品售完检查:库存为0或空,activity_id={}", activity_id); + return true; + } + + // 获取该活动已成功的订单数量 + long soldCount = shopOrderInfoService.fetchActivityOrderSuccessCount( + Convert.toStr(activity_id), + ""); //Convert.toStr(StateCode.ACTIVITY_TYPE_CUTPRICE) + + logger.info("活动商品售完检查:activity_id={}, 已售数量={}, 库存数量={}", + activity_id, soldCount, productCount); + + // 如果已售数量大于等于库存数量,则已售完 + boolean isSoldOut = soldCount >= productCount; + if (isSoldOut) { + logger.info("活动商品已售完:activity_id={}, 已售数量={}, 库存数量={}", + activity_id, soldCount, productCount); + } + + return isSoldOut; + } catch (Exception e) { + logger.error("活动商品售完检查异常:activity_id={}", activity_id, e); + return true; // 出现异常时保守地返回已售完 + } + } + @Transactional public boolean removeActivityBase(Integer activity_id, ShopStoreActivityBase activity_row) { @@ -5105,7 +5211,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl item_row = shopStoreActivityItemService.find(itemQueryWrapper); - List item_id_row = item_row.stream().filter(s->s.getActivity_item_state().equals(StateCode.ACTIVITY_STATE_NORMAL)).map(s -> s.getItem_id()).distinct().collect(Collectors.toList()); + List item_id_row = item_row.stream().filter(s -> s.getActivity_item_state().equals(StateCode.ACTIVITY_STATE_NORMAL)).map(s -> s.getItem_id()).distinct().collect(Collectors.toList()); List activity_id_row = item_row.stream().map(s -> s.getActivity_id()).distinct().collect(Collectors.toList()); List used_id_row = new ArrayList<>(); @@ -5553,13 +5659,14 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl lists = shopStoreActivityBaseService.lists(queryWrapper, 1, 500); + Page lists = shopStoreActivityBaseService.lists(queryWrapper, 1, 200); + + // Optimized version with improved code quality and readability + List filteredActivities = new ArrayList<>(); + for (ShopStoreActivityBase activity : lists.getRecords()) { + // Check if it's a cut-price activity that needs validation + if (activity.getActivity_type_id() != null + && activity.getActivity_type_id() == StateCode.ACTIVITY_TYPE_CUTPRICE) { + + // Validate cut-price activity stock and expiration + Pair checkStockResult = shopActivityCutpriceService.checkCutPriceExpiredAndStock(activity); + if (checkStockResult != null && !checkStockResult.getFirst()) { + // Insufficient stock or expired - update activity state to finished + shopStoreActivityBaseService.updateActivityState(activity.getActivity_id(), StateCode.ACTIVITY_STATE_FINISHED); + continue; // Skip adding this activity to results + } + } + + // Add valid activities to filtered list + filteredActivities.add(activity); + } + + // Update the page records with filtered results + lists.setRecords(filteredActivities); + Map data = toMobileResult(lists); ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); @@ -1442,6 +1481,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl items = (List) data.get("items"); //经营期限 + List store_ids = items.stream().map(s -> Convert.toInt(s.get("store_id"))).collect(Collectors.toList()); List store_info_rows = shopStoreInfoService.gets(store_ids); List subsite_ids = items.stream().map(s -> Convert.toInt(s.get("subsite_id"))).distinct().collect(Collectors.toList()); @@ -1449,6 +1489,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl store_analytics_rows = shopStoreAnalyticsService.gets(store_ids); List finalSubsite_rows = subsite_rows; @@ -1493,6 +1534,25 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl) data.get("items"), true)); @@ -1526,6 +1586,24 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl updateWrapper = new UpdateWrapper<>(); + updateWrapper.eq("store_biz_state", CommonConstant.Store_Biz_State_PreActivity) // 筹备中状态 + .eq("store_is_open", CommonConstant.Enable) // 店铺已开启 + .le("store_biz_opening_date", new Date()) // 开业日期小于等于当前日期 + .set("store_biz_state", CommonConstant.Store_Biz_State_Opening); // 更新为营业中状态 + + return update(updateWrapper); + } catch (Exception e) { + log.error("批量更新店铺营业状态失败", e); + // 发生异常时返回true,避免影响主流程执行 + return Boolean.TRUE; + } + } + // @Override // public Page getMobileStoreList(Integer page, Integer rows) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterLogServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterLogServiceImpl.java index 413bd440..8257424e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterLogServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterLogServiceImpl.java @@ -5,11 +5,9 @@ import cn.hutool.core.util.StrUtil; import com.suisung.mall.common.modules.store.ShopStorePrinterLog; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.store.mapper.ShopStorePrinterLogMapper; -import com.suisung.mall.shop.store.mapper.ShopStorePrinterMapper; import com.suisung.mall.shop.store.service.ShopStorePrinterLogService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @@ -19,41 +17,70 @@ import java.util.List; public class ShopStorePrinterLogServiceImpl extends BaseServiceImpl implements ShopStorePrinterLogService { private static final Logger logger = LoggerFactory.getLogger(ShopStorePrinterLogServiceImpl.class); - @Autowired - private ShopStorePrinterMapper shopStorePrinterMapper; @Override public Boolean insertShopStorePrinterLog(ShopStorePrinterLog record) { + // 参数校验 if (record == null) { + logger.warn("插入打印日志失败:记录为空"); return false; } + // 必要字段校验 if (record.getStore_id() == null || StrUtil.isBlank(record.getOrder_id()) || StrUtil.isBlank(record.getTemplate_value()) || StrUtil.isBlank(record.getPrint_content())) { + logger.warn("插入打印日志失败:必要字段缺失,storeId={}, orderId={}", + record.getStore_id(), record.getOrder_id()); return false; } - return add(record); + try { + // 执行插入操作 + return add(record); + } catch (Exception e) { + logger.error("插入打印日志时发生异常,storeId={}, orderId={}", + record.getStore_id(), record.getOrder_id(), e); + return false; + } } + @Override public void insertShopStorePrinterLogBatch(List records) { + // 参数校验 if (CollUtil.isEmpty(records)) { + logger.debug("批量插入打印日志失败:记录列表为空"); return; } - for (ShopStorePrinterLog record : records) { - if (record.getStore_id() == null || - StrUtil.isBlank(record.getOrder_id()) || - StrUtil.isBlank(record.getTemplate_value()) || - StrUtil.isBlank(record.getPrint_content())) { - continue; + try { + for (ShopStorePrinterLog record : records) { + // 单条记录校验 + if (record == null) { + logger.warn("批量插入打印日志跳过:记录为空"); + continue; + } + + if (record.getStore_id() == null || + StrUtil.isBlank(record.getOrder_id()) || + StrUtil.isBlank(record.getTemplate_value()) || + StrUtil.isBlank(record.getPrint_content())) { + logger.warn("批量插入打印日志跳过:必要字段缺失,storeId={}, orderId={}", + record.getStore_id(), record.getOrder_id()); + continue; + } + + // 执行插入操作 + add(record); } - add(record); + logger.debug("批量插入打印日志完成,共处理 {} 条记录", records.size()); + } catch (Exception e) { + logger.error("批量插入打印日志时发生异常,记录数: {}", records.size(), e); } } + } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java index f597f44d..23b062bd 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterServiceImpl.java @@ -14,13 +14,15 @@ import com.suisung.mall.common.domain.UserDto; import com.suisung.mall.common.modules.store.ShopStorePrinter; import com.suisung.mall.common.modules.store.ShopStorePrinterLog; import com.suisung.mall.common.modules.store.ShopStorePrinterTemplate; +import com.suisung.mall.common.pojo.vo.OrderItemPrintVO; +import com.suisung.mall.common.pojo.vo.OrderPrintVO; import com.suisung.mall.common.pojo.vo.ShopStorePrinterVO; import com.suisung.mall.common.utils.CheckUtil; import com.suisung.mall.common.utils.FreeMakerUtils; import com.suisung.mall.common.utils.JsonUtil; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.order.service.ShopOrderBaseService; -import com.suisung.mall.shop.order.service.impl.ShopOrderInfoServiceImpl; +import com.suisung.mall.shop.order.service.ShopOrderReturnService; import com.suisung.mall.shop.store.mapper.ShopStorePrinterMapper; import com.suisung.mall.shop.store.service.ShopStorePrinterLogService; import com.suisung.mall.shop.store.service.ShopStorePrinterService; @@ -30,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.util.Pair; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -49,15 +52,13 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl binding = shopOrderBaseService.getOrderPrintInfo(storeId, orderId, StateCode.ORDER_PAID_STATE_YES); + Map binding = shopOrderBaseService.fetchOrderPrintInfo(storeId, orderId, StateCode.ORDER_PAID_STATE_YES); if (binding == null) { logger.error("订单{}信息无法获取,无法打印小票。", orderId); return false; @@ -406,6 +407,139 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl + * 该方法通过调用飞鹅打票机接口,完成指定订单的退货小票打印功能。主要流程包括: + * 1. 校验订单号是否合法; + * 2. 获取订单及店铺相关信息; + * 3. 查询对应店铺绑定的打印机列表与打印模板; + * 4. 渲染打印内容并发送至所有绑定的打印机; + * 5. 记录打印日志。 + *

+ * + * @param returnId 退货单编号,用于标识具体的退货记录 + * @return 打印操作是否成功,成功返回 true,否则返回 false + */ + @Async + @Override + public Boolean printShopStoreReturnOrder(String returnId) { + logger.debug("#### 调用飞鹅打票机的打印退款订单操作开始 ####"); + + // 参数校验 + if (StrUtil.isBlank(returnId)) { + logger.error("退款订单为空,无法打印小票。"); + return false; + } + + try { + // 获取打印的订单信息,并检查订单是否存在且已支付 + OrderPrintVO orderPrintVO = shopOrderReturnService.fetchReturnOrderPrintInfo("", returnId); + if (orderPrintVO == null) { + logger.error("退款订单Id{}信息无法获取,无法打印小票。", returnId); + return false; + } + + logger.debug("退款订单信息:{}", orderPrintVO); + + String orderId = orderPrintVO.getOrder_id(); + Integer storeId = orderPrintVO.getStore_id(); + if (CheckUtil.isEmpty(storeId) || CheckUtil.isEmpty(orderId)) { + logger.error("订单Id:{},店铺ID:{}为空,无法打印小票。", orderId, storeId); + return false; + } + + // 查询店铺配置的打印机列表 + List printerList = selectPrinterList(storeId); + if (CollUtil.isEmpty(printerList)) { + // 店铺没有打印机,不再继续执行打印逻辑 + logger.error("店铺{}未添加打票机,无法打印小票。", storeId); + return false; + } + + // 获取适用于退货场景的打印模板 + ShopStorePrinterTemplate template = shopStorePrinterTemplateService.getShopStorePrinterTemplateInner(storeId, StateCode.PRINTER_TEMP_CATE_REFUND); + if (template == null + || StrUtil.isBlank(template.getTemplate_name()) + || StrUtil.isBlank(template.getTemplate_value())) { + // 模板缺失或无效,终止打印流程 + logger.error("店铺{}未添加打票机打印模版,无法打印小票。", storeId); + return false; + } + + // 重要:重构实体类,处理标题,数量,价格适应长度 + List orderItems = orderPrintVO.getOrder_items(); + if (CollUtil.isEmpty(orderItems)) { + logger.error("订单{}商品信息为空,无法打印小票。", orderId); + return false; + } + + List rebuiltItems = new ArrayList<>(orderItems.size()); + for (OrderItemPrintVO item : orderItems) { + if (item != null) { + rebuiltItems.add(item.rebuild()); + } + } + orderPrintVO.setOrder_items(rebuiltItems); + + // 使用 FreeMarker 模板引擎渲染实际要打印的内容 + String printContent = FreeMakerUtils.processTemplate(template.getTemplate_name(), template.getTemplate_value(), orderPrintVO); + if (StrUtil.isBlank(printContent)) { + logger.error("订单{}信息模版渲染异常,无法打印小票。", orderId); + return false; + } + logger.debug("打印内容长度:{}", printContent.length()); + + // 提取所有打印机设备序列号,准备批量打印 + List printerSnList = new ArrayList<>(printerList.size()); + for (ShopStorePrinter printer : printerList) { + if (printer != null && StrUtil.isNotBlank(printer.getPrinter_sn())) { + printerSnList.add(printer.getPrinter_sn()); + } + } + + if (CollUtil.isEmpty(printerSnList)) { + logger.error("订单{}没有有效的打印机设备,无法打印小票。", orderId); + return false; + } + + // 向飞鹅打票机发送打印任务 + List> respList = feieUtil.printContentByList(printerSnList, printContent); + if (CollUtil.isEmpty(respList)) { + // 所有打印机均未能响应,视为失败 + logger.error("订单{}信息打印,调用飞鹅打印机打印失败。", orderId); + return false; + } + + // 遍历每台打印机的响应结果,记录打印日志 + for (Pair respSn : respList) { + if (respSn != null) { + // 构造并保存本次打印的日志记录 + ShopStorePrinterLog shopStorePrinterLog = new ShopStorePrinterLog( + template.getCategory(), + storeId, + orderId, + template.getTemplate_id(), + template.getTemplate_value(), + JsonUtil.object2json(orderPrintVO), + printContent, + respSn.getSecond(), + respSn.getFirst() + ); + shopStorePrinterLogService.insertShopStorePrinterLog(shopStorePrinterLog); + } + } + + logger.debug("#### 调用飞鹅打票机的打印退款订单操作结束 ####"); + return true; + + } catch (Exception e) { + logger.error("打印退货订单小票时发生异常, 退货单ID: {}", returnId, e); + return false; + } + } + + @Override public List selectPrinterList(Integer storeId) { if (storeId == null) { diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterTemplateServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterTemplateServiceImpl.java index ed02d601..04999e93 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterTemplateServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopStorePrinterTemplateServiceImpl.java @@ -2,6 +2,7 @@ package com.suisung.mall.shop.store.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.suisung.mall.common.api.CommonResult; +import com.suisung.mall.common.constant.CommonConstant; import com.suisung.mall.common.modules.store.ShopStorePrinterTemplate; import com.suisung.mall.core.web.service.impl.BaseServiceImpl; import com.suisung.mall.shop.store.mapper.ShopStorePrinterTemplateMapper; @@ -37,6 +38,7 @@ public class ShopStorePrinterTemplateServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("store_id", storeId != null ? storeId : 0) .eq("category", category) + .eq("status", CommonConstant.Enable) .orderByAsc("template_id"); ShopStorePrinterTemplate shopStorePrinterTemplate = findOne(queryWrapper); @@ -46,6 +48,7 @@ public class ShopStorePrinterTemplateServiceImpl extends BaseServiceImpl wrapper = new QueryWrapper<>(); - wrapper.eq("shop_order_id", shopOrderId); - return getOne(wrapper); + + if (StrUtil.startWith(shopOrderId, "JS")) { // 顺丰同城的订单号标志 + return getBySfOrderId(shopOrderId); + } else { + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.eq("shop_order_id", shopOrderId); + return getOne(wrapper); + } } /** diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java index 3f0a5ae2..756ca615 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/utis/FeieUtil.java @@ -6,7 +6,7 @@ import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.suisung.mall.common.pojo.res.FeiePrinterApiRes; -import com.suisung.mall.common.pojo.vo.ShopStoreOrderProductPrintVO; +import com.suisung.mall.common.pojo.vo.OrderItemPrintVO; import com.suisung.mall.common.utils.JsonUtil; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.HttpEntity; @@ -330,7 +330,7 @@ public class FeieUtil { * @param amountLen 金额字节数,最大8个字节 * @return */ - public String genProductStr(List productList, int titleLen, int numLen, int amountLen) { + public String genProductStr(List productList, int titleLen, int numLen, int amountLen) { String resultStr = ""; resultStr += "商品名称 数量 金额
"; resultStr += "--------------------------------
"; diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml index b18acf8f..b6507d86 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml @@ -819,8 +819,8 @@ (od.order_discount_amount + od.voucher_price + od.order_points_fee + od.order_adjust_fee) as total_discount_amount, - - (ob.order_product_amount-od.order_discount_amount-od.voucher_price-od.order_points_fee-od.order_adjust_fee-od.platform_fee-order_shipping_fee_inner-lkl_fee+od.packing_fee) + + (ob.order_product_amount-od.order_discount_amount-od.voucher_price-od.order_points_fee-od.order_adjust_fee-od.platform_fee-order_shipping_fee_inner+od.packing_fee) as order_income_amount, (od.platform_fee+lkl_fee) as platform_fee, od.packing_fee, @@ -1027,9 +1027,9 @@ - + - AND oi.delivery_type_id IN (1,2,3,4,10) + AND oi.delivery_type_id IN (1,2,3,4,5,10,15) diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderReturnMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderReturnMapper.xml index 90471d81..2386122d 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderReturnMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderReturnMapper.xml @@ -86,4 +86,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mall-shop/src/main/resources/mapper/product/ShopProductImageMapper.xml b/mall-shop/src/main/resources/mapper/product/ShopProductImageMapper.xml index 2b885a43..10d0e652 100644 --- a/mall-shop/src/main/resources/mapper/product/ShopProductImageMapper.xml +++ b/mall-shop/src/main/resources/mapper/product/ShopProductImageMapper.xml @@ -161,7 +161,7 @@ shop_product_image spi INNER JOIN library_product lp ON - spi.product_short_name = lp.product_short_name + spi.product_short_name = lp.sname LEFT JOIN ( SELECT @@ -178,7 +178,7 @@ WHERE spi.product_from = '1005' AND spi.item_image_default = '1' - '' ]]> + '' ]]> )t )temp where @@ -213,7 +213,7 @@ shop_product_image spi INNER JOIN library_product lp ON - spi.product_short_name = lp.product_short_name + spi.product_short_name = lp.sname LEFT JOIN ( SELECT @@ -230,7 +230,7 @@ WHERE spi.product_from = '1005' AND spi.item_image_default = '1' - '' ]]> + '' ]]> )t )temp where diff --git a/mall-shop/src/main/resources/templates/order_printer.txt b/mall-shop/src/main/resources/templates/order_printer.txt index aca6a23e..2b4ec44c 100644 --- a/mall-shop/src/main/resources/templates/order_printer.txt +++ b/mall-shop/src/main/resources/templates/order_printer.txt @@ -51,6 +51,48 @@ 收银员:李小璐
+格式化带变量模版: +<#if is_booking_order>【预约订单】
+${store_name}
+--------------------------------
+#${order_pickup_num_str}
+买家备注:${order_message!'-'}
+配送时间:${payment_time?string('MM-dd HH:mm')}~${delivery_time?string('HH:mm')}
+--------------------------------
+订单编号:${order_id}
+订单来源:微信小程序
+支付方式:微信支付
+配送来源:顺丰同城
+付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
+--------------------------------
+商品名称 数量 金额
+--------------------------------
+<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
+<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
+<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
+
+ +--------------------------------
+商品总件数:${order_items_count!0}
+商品总额:¥${order_product_amount?string('0.00')}
+运费:¥${order_shipping_fee?string('0.00')}
+<#if packing_fee?? && (packing_fee > 0)>打包费:¥${packing_fee?string('0.00')}
+优惠金额:-¥${(quanyi!0)?string('0.00')}
+实付金额:¥${order_payment_amount?string('0.00')}
+<#if seller_message?default("")?trim?length gt 1> +--------------------------------
+商家备注:${seller_message!'-'}
+ +--------------------------------
+收货人:${buyer_user_name!''}
+收货人手机:${da_mobile!'-'}
+收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
+--------------------------------
+门店:${store_name}
+门店电话:${store_tel!'-'}
+收银员:${cashier!'店长'}
+ + 格式化的示例: 小发同城
--------------------------------
#00019232
买家备注:不用敲门,放在门口旁边的外卖箱,打个电话告知送达就行,谢谢!!!
配送时间:2024-10-25 14:00-14:30
--------------------------------
订单编号:ES20231026111444527685
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:2024-10-25 14:00:23
--------------------------------
商品名称 数量 金额
--------------------------------
可口可乐CocaC x110 8100.45
ola经典美味汽水1.2
5L/瓶
6970448170051
排骨约350g(默 1 150.13
认砍小块)
6970448170053
新鲜虫草花1包约2 x11 4.01
00g 韭菜1000g 鸡蛋
2003克
6970448170054
冰红茶风味饮料 1 13.24
6970448170055
--------------------------------
商品总件数:3
商品总额:¥18.7
押金:¥500
运费:¥5.54
会员权益:-¥50
秒杀:-¥100
实付金额:¥428.9元
--------------------------------
商家备注:老顾客赠送一箱牛奶;玻璃瓶包装轻拿轻放!
--------------------------------
收货人:张三
收货人手机:13128778765
收货地址:北京市朝阳区朝阳路朝阳人民小区1号楼1栋1101
--------------------------------
门店:岛内价生活超市
门店电话:13665822542
收银员:李小璐
diff --git a/mall-shop/src/main/resources/templates/refund_order_printer.txt b/mall-shop/src/main/resources/templates/refund_order_printer.txt index 7a9e5bba..af2f3dff 100644 --- a/mall-shop/src/main/resources/templates/refund_order_printer.txt +++ b/mall-shop/src/main/resources/templates/refund_order_printer.txt @@ -37,8 +37,45 @@ --------------------------------
操作员:李小明
-格式化模版: +############################################ + +修改后的模版 + + +用户退款订单
+${store_name}
+--------------------------------
+#${order_pickup_num_str}
+退款原因:${return_buyer_message!'-'}
+配送时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}(20-30分钟)
+--------------------------------
+订单编号:${order_id}
+退单编号:${return_id}
+订单来源:${order_channel_name!'微信小程序'}
+支付方式:${payment_type_name!'微信支付'}
+配送来源:${deliver_type_name!'顺丰同城'}
+付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
+申请退款:${return_add_time?string('yyyy-MM-dd HH:mm:ss')}
+确认退款:${return_finish_time?string('yyyy-MM-dd HH:mm:ss')}
+--------------------------------
+******* 退款商品 ******
+--------------------------------
+<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
+--------------------------------
+实付金额:¥${order_payment_amount?string('0.00')}元
+配送费:¥${order_shipping_fee?string('0.00')}元
+申请退款:¥${return_refund_amount?string('0.00')}
+退款方式:${return_flag_str}
+商家审批备注:${return_store_message!'-'}
+--------------------------------
+会员名称:${buyer_user_name!'微信用户'}
+会员手机:${return_tel!'-'}
+--------------------------------
+操作员:${cashier!'-'}
原模版: ${store_name}
--------------------------------
#${order_pickup_num_str}
买家备注:${order_message!'-'}
配送时间:${delivery_time}
--------------------------------
订单编号:${order_id}
订单来源:微信小程序
支付方式:微信支付
配送来源:顺丰同城
付款时间:${payment_time}
--------------------------------
商品名称 数量 金额
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
商品总件数:${order_items_count!0}
商品总额:¥${order_product_amount?string('0.00')}
押金:¥${(yajin!0)?string('0.00')}
运费:¥${order_shipping_fee?string('0.00')}
会员权益:-¥${(quanyi!0)?string('0.00')}
秒杀:-¥${(miaosha!0)?string('0.00')}
实付金额:¥${order_payment_amount?string('0.00')}
<#if seller_message?default("")?trim?length gt 1>--------------------------------
商家备注:${seller_message!'---'}
--------------------------------
收货人:${buyer_user_name!''}
收货人手机:${store_tel!''}
收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}
--------------------------------
门店:${store_name}
门店电话:${store_tel!'-'}
收银员:${cashier!'-'}
+ +带参模版: +用户退款订单
${store_name}
--------------------------------
#${order_pickup_num_str}
退款原因:${return_buyer_message!'-'}
配送时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}(20-30分钟)
--------------------------------
订单编号:${order_id}
退单编号:${return_id}
订单来源:${order_channel_name!'微信小程序'}
支付方式:${payment_type_name!'微信支付'}
配送来源:${deliver_type_name!'顺丰同城'}
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}
申请退款:${return_add_time?string('yyyy-MM-dd HH:mm:ss')}
确认退款:${return_finish_time?string('yyyy-MM-dd HH:mm:ss')}
--------------------------------
******* 退款商品 ******
--------------------------------
<#list order_items as item>${item.s_name}${item.s_quantity}${item.s_amount}
<#if item.s_name_segs??><#list item.s_name_segs as seg>${seg}
<#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}
--------------------------------
实付金额:¥${order_payment_amount?string('0.00')}元
配送费:¥${order_shipping_fee?string('0.00')}元
申请退款:¥${return_refund_amount?string('0.00')}
退款方式:${return_flag_str}
商家审批备注:${return_store_message!'-'}
--------------------------------
会员名称:${buyer_user_name}
会员手机:${return_tel}
--------------------------------
操作员:${cashier!'-'}
diff --git a/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryBaseServiceImpl.java b/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryBaseServiceImpl.java index 35ac0cbb..40b707fb 100644 --- a/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryBaseServiceImpl.java +++ b/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryBaseServiceImpl.java @@ -185,7 +185,7 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl column_row = new QueryWrapper<>(); //查询获得相关的SnsStoryBase列表集合 if (storySearchDTO.getStory_type() != null) { @@ -266,44 +266,44 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl queryWrapper=new QueryWrapper<>(); - queryWrapper.eq("user_id",user.getId()); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("user_id", user.getId()); List snsStoryLikes = snsStoryLikeService.find(queryWrapper); List collect = snsStoryLikes.stream().map(s -> s.getStory_id()).distinct().collect(Collectors.toList()); - for(Map item :items){ + for (Map item : items) { Integer story_id = Convert.toInt(item.get("story_id")); - if(collect.contains(story_id)){ - item.put("IsFabulous",1); - }else{ - item.put("IsFabulous",0); + if (collect.contains(story_id)) { + item.put("IsFabulous", 1); + } else { + item.put("IsFabulous", 0); } } //用户是否收藏 - QueryWrapper snsStoryCollectionQueryWrapper=new QueryWrapper<>(); - snsStoryCollectionQueryWrapper.eq("user_id",user.getId()); + QueryWrapper snsStoryCollectionQueryWrapper = new QueryWrapper<>(); + snsStoryCollectionQueryWrapper.eq("user_id", user.getId()); List snsStoryCollections = snsStoryCollectionService.find(snsStoryCollectionQueryWrapper); List collectionList = snsStoryCollections.stream().map(s -> s.getStory_id()).distinct().collect(Collectors.toList()); - for(Map item :items){ + for (Map item : items) { Integer story_id = Convert.toInt(item.get("story_id")); - if(collectionList.contains(story_id)){ - item.put("IsCollection",1); - }else{ - item.put("IsCollection",0); + if (collectionList.contains(story_id)) { + item.put("IsCollection", 1); + } else { + item.put("IsCollection", 0); } } //用户是否关注 - for(Map item:items){ - QueryWrapper friendQueryWrapper=new QueryWrapper<>(); - friendQueryWrapper.eq("user_id",user.getId()).eq("friend_id",Convert.toInt(item.get("user_id"))); + for (Map item : items) { + QueryWrapper friendQueryWrapper = new QueryWrapper<>(); + friendQueryWrapper.eq("user_id", user.getId()).eq("friend_id", Convert.toInt(item.get("user_id"))); SnsUserFriend one = snsUserFriendService.findOne(friendQueryWrapper); - if(one!=null){ - item.put("IsFollow",true); - }else{ - item.put("IsFollow",false); + if (one != null) { + item.put("IsFollow", true); + } else { + item.put("IsFollow", false); } } } @@ -382,7 +382,7 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl item_ids = Convert.toList(Long.class, str_item_id); - Map product_row = shopService.getProductItemOne(item_ids.get(0)); + try { + List item_ids = Convert.toList(Long.class, str_item_id); + // 检查列表是否为空以及索引是否有效 + if (CollUtil.isNotEmpty(item_ids) && item_ids.get(0) != null) { + Map product_row = shopService.getProductItemOne(item_ids.get(0)); - if (CollUtil.isNotEmpty(product_row)) { - String product_name = Convert.toStr(product_row.get("product_name"), ""); - String item_name = Convert.toStr(product_row.get("item_name"), ""); - row.put("product_item_name", product_name + " " + item_name); - row.put("item_unit_price", product_row.get("item_unit_price")); - row.put("item_market_price", product_row.get("item_market_price")); - row.put("product_image", product_row.get("product_image")); + if (CollUtil.isNotEmpty(product_row)) { + String product_name = Convert.toStr(product_row.get("product_name"), ""); + String item_name = Convert.toStr(product_row.get("item_name"), ""); + row.put("product_item_name", product_name + " " + item_name); + row.put("item_unit_price", product_row.get("item_unit_price")); + row.put("item_market_price", product_row.get("item_market_price")); + row.put("product_image", product_row.get("product_image")); + } + } + } catch (Exception e) { + log.warn("获取商品信息失败, story_id: {}, item_id: {}", story_id, str_item_id, e); + // 发生异常时保持默认值 } } - // 是否点赞 - QueryWrapper likeQueryWrapper = new QueryWrapper<>(); - likeQueryWrapper.eq("story_id", story_id).eq("user_id", user_id); - SnsStoryLike story_like_row = snsStoryLikeService.findOne(likeQueryWrapper); - row.put("IsFabulous", story_like_row != null ? 1 : 0); + // 是否点赞 - 只有登录用户才能查看点赞状态 + if (user_id != null) { + try { + QueryWrapper likeQueryWrapper = new QueryWrapper<>(); + likeQueryWrapper.eq("story_id", story_id).eq("user_id", user_id); + SnsStoryLike story_like_row = snsStoryLikeService.findOne(likeQueryWrapper); + row.put("IsFabulous", story_like_row != null ? 1 : 0); + } catch (Exception e) { + log.warn("查询点赞状态失败, story_id: {}, user_id: {}", story_id, user_id, e); + row.put("IsFabulous", 0); + } + } else { + row.put("IsFabulous", 0); // 未登录用户默认未点赞 + } // 下面两个查询用不到 - /*QueryWrapper advertisementQueryWrapper = new QueryWrapper<>(); - advertisementQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 1); - row.put("adv_top", snsStoryAdvertisementService.findOne(advertisementQueryWrapper)); + /*QueryWrapper advertisementQueryWrapper = new QueryWrapper<>(); + advertisementQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 1); + row.put("adv_top", snsStoryAdvertisementService.findOne(advertisementQueryWrapper)); - QueryWrapper advListQueryWrapper = new QueryWrapper<>(); - advListQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 0); - row.put("adv_lists", snsStoryAdvertisementService.find(advListQueryWrapper));*/ + QueryWrapper advListQueryWrapper = new QueryWrapper<>(); + advListQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 0); + row.put("adv_lists", snsStoryAdvertisementService.find(advListQueryWrapper));*/ // 移动端不需要分类 if (!isMobile()) { - row.put("category_row", snsStoryCategoryService.find(new QueryWrapper<>())); - QueryWrapper baseQueryWrapper = new QueryWrapper<>(); - baseQueryWrapper.eq("user_id", row.get("user_id")).eq("story_status", 1).eq("story_enable", 1).eq("story_privacy", 0).orderByDesc("story_time"); - row.put("story_user_items", snsStoryBaseService.lists(baseQueryWrapper, 1, 5).getRecords()); + try { + row.put("category_row", snsStoryCategoryService.find(new QueryWrapper<>())); + QueryWrapper baseQueryWrapper = new QueryWrapper<>(); + baseQueryWrapper.eq("user_id", row.get("user_id")) + .eq("story_status", 1) + .eq("story_enable", 1) + .eq("story_privacy", 0) + .orderByDesc("story_time"); + + Object userId = row.get("user_id"); + if (userId != null) { + Page storyPage = snsStoryBaseService.lists(baseQueryWrapper, 1, 5); + row.put("story_user_items", storyPage.getRecords()); + } else { + row.put("story_user_items", new ArrayList<>()); + } + } catch (Exception e) { + log.warn("获取用户故事列表失败, user_id: {}", row.get("user_id"), e); + row.put("story_user_items", new ArrayList<>()); + } } - QueryWrapper friendQueryWrapper = new QueryWrapper<>(); - friendQueryWrapper.eq("friend_id", row.get("user_id")).eq("user_id", user_id); - SnsUserFriend friend_row = snsUserFriendService.findOne(friendQueryWrapper); + // 关注状态 - 只有登录用户才能查看关注状态 + if (user_id != null && row.get("user_id") != null) { + try { + QueryWrapper friendQueryWrapper = new QueryWrapper<>(); + friendQueryWrapper.eq("friend_id", row.get("user_id")).eq("user_id", user_id); + SnsUserFriend friend_row = snsUserFriendService.findOne(friendQueryWrapper); - if (friend_row != null) { - row.put("IsFollow", 1); + if (friend_row != null) { + row.put("IsFollow", 1); + } else { + row.put("IsFollow", 0); + } + } catch (Exception e) { + log.warn("查询关注状态失败, friend_id: {}, user_id: {}", row.get("user_id"), user_id, e); + row.put("IsFollow", 0); + } } else { - row.put("IsFollow", 0); + row.put("IsFollow", 0); // 未登录用户默认未关注 } - //row.put("story_file", StrUtil.split(row.get("story_file").toString(), ",")); - row.put("story_file", Convert.toList(String.class, row.get("story_file"))); + // 处理故事文件 + try { + //row.put("story_file", StrUtil.split(row.get("story_file").toString(), ",")); + Object storyFileObj = row.get("story_file"); + if (storyFileObj != null) { + row.put("story_file", Convert.toList(String.class, storyFileObj)); + } else { + row.put("story_file", new ArrayList<>()); + } + } catch (Exception e) { + log.warn("处理故事文件失败, story_id: {}", story_id, e); + row.put("story_file", new ArrayList<>()); + } return row; } diff --git a/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryCategoryServiceImpl.java b/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryCategoryServiceImpl.java index b9e4292d..86336148 100644 --- a/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryCategoryServiceImpl.java +++ b/mall-sns/src/main/java/com/suisung/mall/sns/service/impl/SnsStoryCategoryServiceImpl.java @@ -189,99 +189,170 @@ public class SnsStoryCategoryServiceImpl extends BaseServiceImpl wrapper = new QueryWrapper<>(); Integer story_id = Convert.toInt(getParameter("story_id")); + + // 参数校验 + if (story_id == null || story_id <= 0) { + return new HashMap(); + } + wrapper.eq("story_id", story_id); wrapper.orderByDesc("comment_time"); - Map data = snsStoryCommentService.getLists(wrapper, page, rows); + + Map data = new HashMap(); + try { + data = snsStoryCommentService.getLists(wrapper, page, rows); + } catch (Exception e) { + // 处理查询异常 + data.put("items", new ArrayList<>()); + data.put("total", 0); + } + + // 确保 items 字段存在 + if (data.get("items") == null) { + data.put("items", new ArrayList<>()); + } + data.put("items", accountService.fixUserAvatar((List) data.get("items"), false)); //读取评论回复 List items = (List) data.get("items"); if (CollUtil.isEmpty(items)) { - return new HashMap(); + return data; } - List comment_id_row = items.stream().map(s -> Convert.toInt(s.get("comment_id"))).collect(Collectors.toList()); + // 避免转换中的空值问题 + List comment_id_row = items.stream() + .map(s -> Convert.toInt(s.get("comment_id"))) + .filter(id -> id != null) + .collect(Collectors.toList()); + + if (CollUtil.isEmpty(comment_id_row)) { + return data; + } QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.in("comment_id", comment_id_row); - queryWrapper.eq("user_id", user_id); - // + if (user_id != null) { + queryWrapper.eq("user_id", user_id); + } + //判断评论是否IsFabulous - List snsStoryCommentHelpfuls = snsStoryCommentHelpfulService.find(queryWrapper); + List snsStoryCommentHelpfuls = new ArrayList<>(); + try { + snsStoryCommentHelpfuls = snsStoryCommentHelpfulService.find(queryWrapper); + } catch (Exception e) { + // 忽略查询异常 + } + List comment_helpful_rows = Convert.toList(Map.class, snsStoryCommentHelpfuls); + if (comment_helpful_rows == null) { + comment_helpful_rows = new ArrayList<>(); + } QueryWrapper snsStoryCommentReplyQueryWrapper = new QueryWrapper<>(); snsStoryCommentReplyQueryWrapper.in("comment_id", comment_id_row); snsStoryCommentReplyQueryWrapper.eq("comment_reply_show_flag", 1); snsStoryCommentReplyQueryWrapper.orderByDesc("comment_reply_time"); - List snsStoryCommentReplies = snsStoryCommentReplyService.find(snsStoryCommentReplyQueryWrapper); - List comment_reply_rows = Convert.toList(Map.class, snsStoryCommentReplies); + List snsStoryCommentReplies = new ArrayList<>(); + try { + snsStoryCommentReplies = snsStoryCommentReplyService.find(snsStoryCommentReplyQueryWrapper); + } catch (Exception e) { + // 忽略查询异常 + } + + List comment_reply_rows = Convert.toList(Map.class, snsStoryCommentReplies); + if (comment_reply_rows == null) { + comment_reply_rows = new ArrayList<>(); + } + + try { + accountService.fixUserAvatar(comment_reply_rows, false); + } catch (Exception e) { + // 忽略头像修复异常 + } - accountService.fixUserAvatar(comment_reply_rows, false); Map comment_reply_tmp_rows = new HashMap(); for (Map comment_reply_row : comment_reply_rows) { Integer comment_id = Convert.toInt(comment_reply_row.get("comment_id")); + if (comment_id == null) continue; // 避免空值 + List comment_rows = (List) ObjectUtil.defaultIfNull(comment_reply_tmp_rows.get(comment_id), new ArrayList()); - if (CollUtil.isEmpty(comment_rows)) comment_reply_tmp_rows.put(comment_id, comment_rows); + if (CollUtil.isEmpty(comment_rows)) { + comment_reply_tmp_rows.put(comment_id, new ArrayList<>()); // 使用新实例 + comment_rows = (List) comment_reply_tmp_rows.get(comment_id); + } comment_rows.add(comment_reply_row); } //判断子评论是否IsFabulous - List comment_reply_id_row = comment_reply_rows.stream().map(s -> Convert - .toInt(s.get("comment_reply_id"))).collect(Collectors.toList()); + List comment_reply_id_row = comment_reply_rows.stream() + .map(s -> Convert.toInt(s.get("comment_reply_id"))) + .filter(id -> id != null) + .collect(Collectors.toList()); - QueryWrapper helpfulQueryWrapper = new QueryWrapper<>(); - if (CollUtil.isNotEmpty(comment_reply_id_row)) helpfulQueryWrapper.in("comment_reply_id", comment_reply_id_row); + Map comment_reply_helpful_tmp_rows = new HashMap(); // 提前定义变量 + if (CollUtil.isNotEmpty(comment_reply_id_row)) { + QueryWrapper helpfulQueryWrapper = new QueryWrapper<>(); + helpfulQueryWrapper.in("comment_reply_id", comment_reply_id_row); + if (user_id != null) { + helpfulQueryWrapper.eq("user_id", user_id); + } - helpfulQueryWrapper.eq("user_id", user_id); + List storyCommentHelpfuls = new ArrayList<>(); + try { + storyCommentHelpfuls = snsStoryCommentReplyHelpfulService.find(helpfulQueryWrapper); + } catch (Exception e) { + // 忽略查询异常 + } - List storyCommentHelpfuls = snsStoryCommentReplyHelpfulService.find(helpfulQueryWrapper); - List comment_reply_helpful_rows = Convert.toList(Map.class, storyCommentHelpfuls); + List comment_reply_helpful_rows = Convert.toList(Map.class, storyCommentHelpfuls); + if (comment_reply_helpful_rows == null) { + comment_reply_helpful_rows = new ArrayList<>(); + } - Map comment_reply_helpful_tmp_rows = new HashMap(); - for (Map comment_reply_helpful_row : comment_reply_helpful_rows) { - Integer comment_reply_id = Convert.toInt(comment_reply_helpful_row.get("comment_reply_id")); - List comment_rows = (List) ObjectUtil.defaultIfNull(comment_reply_helpful_tmp_rows.get(comment_reply_id), new ArrayList()); - if (CollUtil.isEmpty(comment_rows)) comment_reply_helpful_tmp_rows.put(comment_reply_id, comment_rows); - comment_rows.add(comment_reply_helpful_row); + for (Map comment_reply_helpful_row : comment_reply_helpful_rows) { + Integer comment_reply_id = Convert.toInt(comment_reply_helpful_row.get("comment_reply_id")); + if (comment_reply_id == null) continue; // 避免空值 + + List comment_rows = (List) ObjectUtil.defaultIfNull(comment_reply_helpful_tmp_rows.get(comment_reply_id), new ArrayList()); + if (CollUtil.isEmpty(comment_rows)) { + comment_reply_helpful_tmp_rows.put(comment_reply_id, new ArrayList<>()); // 使用新实例 + comment_rows = (List) comment_reply_helpful_tmp_rows.get(comment_reply_id); + } + comment_rows.add(comment_reply_helpful_row); + } } - /* - QueryWrapper wrapper1 = new QueryWrapper<>(); - wrapper1.in("comment_id", comment_id_row); - wrapper1.eq("user_id", user_id); - List storyCommentReplies = snsStoryCommentReplyService.find(wrapper1); - - */ - - comment_helpful_rows = Convert.toList(Map.class, snsStoryCommentHelpfuls); - Map comment_helpful_tmp_rows = new HashMap(); for (Map comment_helpful_row : comment_helpful_rows) { - Integer comment_id = Convert.toInt(comment_helpful_row.get("comment_id")); - List comment_rows = (List) ObjectUtil.defaultIfNull(comment_helpful_tmp_rows.get(comment_id), new ArrayList()); - if (CollUtil.isEmpty(comment_rows)) comment_reply_helpful_tmp_rows.put(comment_id, comment_rows); + if (comment_id == null) continue; // 避免空值 + List comment_rows = (List) ObjectUtil.defaultIfNull(comment_helpful_tmp_rows.get(comment_id), new ArrayList()); + if (CollUtil.isEmpty(comment_rows)) { + comment_helpful_tmp_rows.put(comment_id, new ArrayList<>()); // 使用新实例 + comment_rows = (List) comment_helpful_tmp_rows.get(comment_id); + } comment_rows.add(comment_helpful_row); - comment_helpful_tmp_rows.put(comment_helpful_row.get("comment_id"), comment_helpful_row); } for (Map item : items) { Integer comment_id = Convert.toInt(item.get("comment_id")); + if (comment_id == null) continue; // 避免空值 + if (ObjectUtil.isNotNull(comment_reply_tmp_rows.get(comment_id))) { item.put("commentList", comment_reply_tmp_rows.get(comment_id)); } else { @@ -292,22 +363,22 @@ public class SnsStoryCategoryServiceImpl extends BaseServiceImpltrue ${docker.ca} - openjdk:8-jre @@ -600,11 +599,6 @@ ["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"] - - - - - /