Compare commits

...

89 Commits

Author SHA1 Message Date
48802a666b Merge branch 'main' into dev 2025-12-08 10:22:18 +08:00
97ee21b5fb 编辑模板入库新增参数app_id 2025-12-05 16:04:35 +08:00
295275b3dc Merge remote-tracking branch 'origin/main' 2025-12-05 15:57:51 +08:00
f9192c74ff 修改取单号逻辑算法 2025-12-05 15:57:39 +08:00
a53ccaadc7 编辑装修js传入app_id 2025-12-05 15:55:02 +08:00
7a26bdec49 装修模板新增行业类别,补充字段 2025-12-05 14:21:29 +08:00
89a326a05b 店铺列表增加合同的状态返回,修复顺丰同城取消订单逻辑 2025-12-05 13:03:30 +08:00
def93e54fa 装修模板新增行业类别 2025-12-05 12:02:37 +08:00
48da403179 es日志打印去除,确认收货后加入去除重复日志筛选 2025-12-05 11:04:28 +08:00
ca48e78dd1 es日志打印去除,确认收货后加入去除重复日志筛选 2025-12-04 15:27:14 +08:00
76b7832908 分享图问题修复,翻译部分代码 2025-12-04 14:27:23 +08:00
87f777ca78 装修控制图片大小 2025-12-04 11:19:15 +08:00
9051b0702d 装修控制图片大小 2025-12-04 10:30:01 +08:00
fc177025a0 装修控制图片大小 2025-12-03 18:42:05 +08:00
28e5b83f6c 同城打票机打印实体类,确认收货通知记录数据 2025-12-03 17:50:55 +08:00
b1f401ff81 重要:分账计算方法做调整,拉卡拉手续费由 代理商或平台出,商家不会扣除。 2025-12-03 11:46:26 +08:00
950b7b9b32 重要:分账计算方法做调整,拉卡拉手续费由 代理商或平台出,商家不会扣除。 2025-12-03 11:38:47 +08:00
46b27539b5 拉卡拉签署入网合同,获取平台的公司名和手机号,填充合同数据 2025-12-03 10:42:03 +08:00
7326d0505f 商家预计收入无需减 拉卡拉平台费,配送费计算表增加是否平台字段 2025-12-02 23:45:46 +08:00
1684e43663 新增平台订单查询条件 2025-12-02 17:22:42 +08:00
6575858004 取消订单,同时也要取消顺丰同城的配送订单 2025-12-01 17:42:27 +08:00
cb0d5e0cc3 Merge remote-tracking branch 'origin/main' 2025-12-01 17:36:03 +08:00
2ea022091c 取消订单,同时也要取消顺丰同城的配送订单 2025-12-01 17:35:52 +08:00
2eed30bdf4 修复图片空内容默认显示问题 2025-12-01 16:09:19 +08:00
dcaf638b58 修复图片空内容无默认问题 2025-12-01 14:47:40 +08:00
e59fa56784 平台装修首页商品显示问题修复 2025-11-29 17:31:25 +08:00
d530054e03 修复错误,和添加打印流程 2025-11-29 17:18:47 +08:00
81abf15dcd Merge remote-tracking branch 'origin/main' 2025-11-29 15:34:09 +08:00
655998c805 异步非阻塞 秒数 增加。 2025-11-29 15:33:55 +08:00
24a9def012 新增接口,分类新增,校验 2025-11-29 11:35:12 +08:00
b4e2b92730 新增接口,分类新增,优化添加分类的复杂性-脚本 2025-11-29 10:39:37 +08:00
d9bb03770a 新增接口,分类新增,优化添加分类的复杂性 2025-11-29 10:39:20 +08:00
1d328b38f7 退款打印方法调试 2025-11-29 02:27:02 +08:00
a73615f350 退款打印方法调试 2025-11-29 00:44:04 +08:00
e7d8a126fc 管理员分类页面优化 2025-11-28 16:13:17 +08:00
62239d4dec 思迅总额计算 2025-11-28 11:18:27 +08:00
30b24dda04 商品图库附图新增 2025-11-28 11:08:06 +08:00
e1bd542c12 打印实体更改名称 2025-11-28 10:48:14 +08:00
7e4c2a9ccf 思迅同步问题修复,无出库也要生成记录 2025-11-27 17:55:21 +08:00
ed29dcd419 思迅同步问题修复 2025-11-27 16:26:13 +08:00
857447ae8b 思迅同步问题修复 2025-11-27 15:44:57 +08:00
c0f11aa224 思迅初始化与更新区分 2025-11-27 15:02:13 +08:00
504f03e5f6 思迅同步问题修复 2025-11-27 14:58:24 +08:00
4e2a75520c 图库图片路径问题修复 2025-11-27 10:14:15 +08:00
fb10c6e173 图库图片路径问题修复 2025-11-27 10:14:14 +08:00
45469be52d 1、客户端商品问题修改,2、客户端新增版本号9.7和商云10分开 2025-11-26 11:21:16 +08:00
71cef21472 图库查询问题修复 2025-11-25 17:18:21 +08:00
75795bad0e 图库查询问题修复 2025-11-25 17:18:20 +08:00
200f66839b 咨询列表和店铺 api 接口加入白名单,不需要登录。 2025-11-25 16:36:58 +08:00
9085dc7681 Merge remote-tracking branch 'origin/main' 2025-11-25 16:29:13 +08:00
5c6a4e985a 思迅同步商品名称调整 2025-11-25 16:27:46 +08:00
d8aadb2d47 新店铺模板问题修改 2025-11-25 16:27:35 +08:00
3bdb5d73d1 Merge remote-tracking branch 'origin/main' 2025-11-25 12:26:57 +08:00
06df245d59 模板新增删除功能,调整店铺的模板逻辑 2025-11-25 11:26:38 +08:00
d4c3bce80f Merge remote-tracking branch 'origin/main' 2025-11-24 20:05:04 +08:00
d2c9b6c492 补充文件 2025-11-24 18:08:56 +08:00
da771d42f7 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	mall-shop/src/main/java/com/suisung/mall/shop/sync/keymanage/RedisKey.java
2025-11-24 18:02:41 +08:00
f06a166693 redis key 冲突,提交合并 2025-11-24 18:02:21 +08:00
c14cdb0967 修改字段 2025-11-24 17:40:59 +08:00
b628fffca7 图库修改接口,新增装修空白模板 2025-11-24 17:31:33 +08:00
3802044eea 对话session移除问题修复 2025-11-24 15:21:16 +08:00
32bd2fab91 装修表增加 创建时间和更改时间字段 2025-11-24 00:11:34 +08:00
de19921581 新增店铺装修路由-补充代码 2025-11-22 10:51:47 +08:00
2cb58bffa3 新增店铺装修路由 2025-11-22 10:47:06 +08:00
9c8f78f3cf 店铺装修调整 2025-11-22 10:46:18 +08:00
9bc2d32c45 商品库表字段 product_short_name 改写 sname 简化 2025-11-22 01:54:00 +08:00
8f20e755fb 提示语优化 2025-11-21 15:01:01 +08:00
db5bd06c35 砍价过期逻辑 补充和优化 2025-11-20 22:13:40 +08:00
528c76a912 同步订单到思迅,取价格为最终价格 2025-11-20 15:15:18 +08:00
1022468778 同步订单到思迅,取价格为最终价格 2025-11-20 15:07:28 +08:00
9d59fbcc9c 添加活动商品时,过滤关闭活动 2025-11-20 14:40:21 +08:00
8eeee63f58 im调整sesion,解决一次性清除redis问题 2025-11-20 11:56:12 +08:00
3971c5f504 im调整sesion,解决一次性清除session问题 2025-11-20 11:35:18 +08:00
47331a5b2a im调整组的sesion,代码优化 2025-11-20 10:35:53 +08:00
0b6c87aa31 列表显示第一个规格 2025-11-20 09:37:15 +08:00
c8699f0bf2 im分布式测试方案-单机测试 2025-11-20 09:10:05 +08:00
ac3b9216d8 删除预约订单无用代码 2025-11-20 00:18:18 +08:00
a3fa5a3fa8 砍价活动,下单前检查 库存 2025-11-20 00:10:07 +08:00
3ca6dcbec9 砍价活动,库存逻辑处理。 2025-11-19 23:21:37 +08:00
1b97836bea im分布式测试方案-单机测试 2025-11-19 17:40:46 +08:00
fddd1750a1 im分布式测试方案-单机测试 2025-11-19 17:21:55 +08:00
4ce40ede17 im分布式测试方案-单机测试 2025-11-19 17:00:09 +08:00
023bd790df 店铺开业筹备状态到期转正。 2025-11-19 16:56:51 +08:00
8c9661c77c im分布式测试方案 2025-11-19 16:18:12 +08:00
928b16da60 im还原单机测试 2025-11-19 15:19:15 +08:00
7dd3822e44 im还原单机测试 2025-11-19 15:15:52 +08:00
4259a7e454 同步下单时间给思迅流水 2025-11-19 11:00:29 +08:00
da449a5921 砍价超时时间,赋值 2025-11-19 08:29:03 +08:00
a2e0854a7d im问题修复-redis统一key,保证不同服务器保存相同的客服 2025-11-18 16:53:33 +08:00
66 changed files with 2994 additions and 1113 deletions

View File

@ -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<Integer, String> 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, "顺丰同城配送");
}
};

View File

@ -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服务交换机

View File

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

View File

@ -10,6 +10,7 @@ import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* <p>
@ -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;
}

View File

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

View File

@ -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<Boolean, String> adjustResult = adjustAmountsWithLklAmount();
if (!adjustResult.getFirst()) {
String errorMsg = "分账计算参数异常: " + adjustResult.getSecond();
log.error(errorMsg);
this.errMsg = errorMsg;
return SeparateResult.failure(errorMsg);
}
// 7. 计算商家实际分账比例
Pair<Boolean, String> mchResult = calculateActualMchRatio();
if (!mchResult.getFirst()) {
String errorMsg = "分账计算参数异常: " + mchResult.getSecond();
@ -322,7 +331,7 @@ public class LklSeparateWithTotalAmountDTO {
private Pair<Boolean, String> 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-lklAmountmchAmount=mchAmount+lklAmount
* 2. 如果agent2ndAmount<=0 agent1stAmount>0 且大于lklAmount, 则agent1stAmount=agent1stAmount-lklAmountmchAmount=mchAmount+lklAmount
* 3. 如果agent2ndAmount<=0 agent1stAmount<=0 platAmount>0 且大于等于lklAmount, platAmount=platAmount-lklAmountmchAmount=mchAmount+lklAmount
*
* @return Pair<Boolean, String> Boolean表示是否成功String为错误信息
*/
private Pair<Boolean, String> 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 : "");
}
}
}
}

View File

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

View File

@ -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<OrderItemPrintVO> order_items;
}

View File

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

View File

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

View File

@ -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<Pair<String, String>> timeList1 = new ArrayList<>();
timeList1.add(Pair.of("06:00", "17:00"));
timeList1.add(Pair.of("10:00", "18:00"));
Pair<String, String> intersection1 = findTimeInterSection(timeList1);
System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
// 测试无交集情况
List<Pair<String, String>> timeList2 = new ArrayList<>();
timeList2.add(Pair.of("09:00", "12:00"));
timeList2.add(Pair.of("13:00", "17:00"));
Pair<String, String> intersection2 = findTimeInterSection(timeList2);
System.out.println("交集结果2: " + intersection2); // 应该是null
// 测试空列表
Pair<String, String> intersection3 = findTimeInterSection(null);
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null
//
// System.out.println("=== 测试 findTimeIntersection ===");
//
// // 测试正常交集情况
// List<Pair<String, String>> timeList1 = new ArrayList<>();
// timeList1.add(Pair.of("06:00", "17:00"));
// timeList1.add(Pair.of("10:00", "18:00"));
//
// Pair<String, String> intersection1 = findTimeInterSection(timeList1);
// System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
//
//// 测试无交集情况
// List<Pair<String, String>> timeList2 = new ArrayList<>();
// timeList2.add(Pair.of("09:00", "12:00"));
// timeList2.add(Pair.of("13:00", "17:00"));
//
// Pair<String, String> intersection2 = findTimeInterSection(timeList2);
// System.out.println("交集结果2: " + intersection2); // 应该是null
//
//// 测试空列表
// Pair<String, String> intersection3 = findTimeInterSection(null);
// System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null
//
}
}

View File

@ -39,7 +39,7 @@ public class FreeMakerUtils {
* @throws IOException
* @throws TemplateException
*/
public static String processTemplate(Configuration configuration, String templateName, String templateValue, Map<String, Object> 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<String, Object> binding) {
return processTemplate(stringTempConfiguration(), templateName, templateValue, binding);
}
public static String processTemplate(String templateName, String templateValue, Object binding) {
return processTemplate(stringTempConfiguration(), templateName, templateValue, binding);
}
}

View File

@ -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<String>() {
@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<String>() {
@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");
}
}

View File

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

View File

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

View File

@ -33,11 +33,11 @@ public interface ShopActivityCutpriceService extends IBaseService<ShopActivityCu
/**
* 砍价活动是否可以下单
*
* @param ac_id 活动 自增Id
* @param activity_id 活动 自增Id
* @param order_user_id 下单用户
* @return
*/
Pair<Boolean, String> canDoOrderCutPriceActivity(Integer ac_id, Integer order_user_id);
Pair<Boolean, String> canDoOrderCutPriceActivity(Integer activity_id, Integer order_user_id);
/**

View File

@ -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<ShopActivit
@Autowired
private ShopStoreActivityBaseService shopStoreActivityBaseService;
@Lazy
@Autowired
private ShopOrderInfoService shopOrderInfoService;
@Lazy
@Autowired
private AccountService accountService;
@ -275,10 +281,10 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
// 参数校验
if (activity_id == null || activity_id <= 0) {
throw new ApiException(I18nUtil._("活动ID无效!"));
throw new ApiException(I18nUtil._("活动编号无效!"));
}
if (user_id == null || user_id <= 0) {
throw new ApiException(I18nUtil._("用户ID无效!"));
throw new ApiException(I18nUtil._("用户编号无效!"));
}
// 获取活动基础信息
@ -300,14 +306,19 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
queryWrapper.eq("activity_id", activity_id).eq("user_id", user_id);
ShopActivityCutprice cutprice_row = findOne(queryWrapper);
// 需要检查活动有效期和活动商品库存是否足够
Pair<Boolean, String> checkStockResult = checkCutPriceExpiredAndStock(activityBase);
// 立即参与活动
boolean is_join_activity = false;
// 如果用户未参与该砍价活动则创建新的砍价记录
if (cutprice_row == null) {
// 需要检查活动有效期和活动商品库存是否足够
Pair<Boolean, String> check_result = checkCutPriceExpiredAndStock(activityBase);
if (!check_result.getFirst()) {
throw new ApiException(check_result.getSecond());
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<ShopActivit
// 该砍价记录过期时间戳
Integer cut_hour = activityBase.getCut_hour();
// log.info("砍价失效小时cut_hour:{}", cut_hour);
if (CheckUtil.isEmpty(cut_hour)) {
cut_hour = 48;
}
cutprice_row.setExpired_at(now.getTime() + cut_hour * 3600000L);
// log.info("砍价失效时间戳:{}", cutprice_row.getExpired_at());
cutprice_row.setAc_datetime(now);
cutprice_row.setOrder_id("");
@ -344,7 +353,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
// 保存砍价记录
if (!saveOrUpdate(cutprice_row)) {
throw new ApiException(I18nUtil._("创建砍价失败!"));
throw new ApiException(I18nUtil._("参加砍价活动失败!"));
}
is_join_activity = true;
@ -369,19 +378,26 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
// 设置活动规则信息
activity_row.put("activity_rule", JSONUtil.parseObj(activity_row.get("activity_rule")));
// 判断是否可以立即购买
boolean canBuyNow = cutprice_row != null
&& CheckUtil.isNotEmpty(cutprice_row.getAc_sale_price())
&& CheckUtil.isNotEmpty(cutprice_row.getAc_mix_limit_price())
&& NumberUtil.isLessOrEqual(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
if (checkStockResult != null && !checkStockResult.getFirst()) {
// 库存不够立即更改活动状态为已结束
shopStoreActivityBaseService.updateActivityState(activity_id, StateCode.ACTIVITY_STATE_FINISHED);
activity_row.put("can_buy_now", CommonConstant.Disable2);
activity_row.put("cannot_buy_now_reason", "抱歉,商品已抢完,活动结束。");
} else {
// 判断是否可以立即购买
boolean canBuyNow = cutprice_row != null
&& CheckUtil.isNotEmpty(cutprice_row.getAc_sale_price())
&& CheckUtil.isNotEmpty(cutprice_row.getAc_mix_limit_price())
&& NumberUtil.isLessOrEqual(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
BigDecimal subtractPrice = NumberUtil.sub(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
BigDecimal subtractPrice = NumberUtil.sub(cutprice_row.getAc_sale_price(), cutprice_row.getAc_mix_limit_price());
// 设置购买状态和提示信息
activity_row.put("can_buy_now", canBuyNow ? CommonConstant.Enable : CommonConstant.Disable2);
activity_row.put("cannot_buy_now_reason",
canBuyNow ? "恭喜您,商品可以立即出手了。" :
String.format("还剩%.2f元到达商品底价,请继续加油!", subtractPrice));
// 设置购买状态和提示信息
activity_row.put("can_buy_now", canBuyNow ? CommonConstant.Enable : CommonConstant.Disable2);
activity_row.put("cannot_buy_now_reason",
canBuyNow ? "恭喜您,商品可以立即出手了。" :
String.format("还剩%.2f元到达商品底价,继续加油!", subtractPrice));
}
return activity_row;
}
@ -390,7 +406,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
/**
* 自己或朋友砍价
*
* @param ac_id 活动 Id
* @param ac_id 活动Id
* @param user_id 砍价用户
* @return CommonResult 砍价结果
*/
@ -398,142 +414,177 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
@Transactional
public CommonResult doCutPrice(Integer ac_id, Integer user_id) {
// 参数校验
if (ac_id == null || ac_id <= 0) {
return CommonResult.failed(I18nUtil._("活动ID无效"));
}
if (user_id == null || user_id <= 0) {
return CommonResult.failed(I18nUtil._("用户ID无效"));
if (ac_id == null || ac_id <= 0 || user_id == null || user_id <= 0) {
return CommonResult.failed(I18nUtil._("活动或用户编号无效!"));
}
// 获取砍价记录
ShopActivityCutprice shopActivityCutprice = get(ac_id);
if (shopActivityCutprice == null) {
return CommonResult.failed(I18nUtil._("抱歉,砍价记录已失效!"));
ShopActivityCutprice cutprice = get(ac_id);
if (cutprice == null) {
return CommonResult.failed(I18nUtil._("未找到砍价记录!"));
}
// 检查活动状态
if (shopStoreActivityBaseService == null) {
return CommonResult.failed(I18nUtil._("系统繁忙,请稍后再试!"));
}
ShopStoreActivityBase activityBase = shopStoreActivityBaseService.get(cutprice.getActivity_id());
if (activityBase == null) {
updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已失效啦!"));
}
Integer activity_id = shopActivityCutprice.getActivity_id();
// 检查活动状态是否正常
ShopStoreActivityBase shopStoreActivityBase = shopStoreActivityBaseService.get(shopActivityCutprice.getActivity_id());
if (shopStoreActivityBase == null) {
// 活动不存在更新砍价订单状态为已取消
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已失效!"));
}
// 活动状态不是正常状态
if (!ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_NORMAL)) {
// 如果活动已结束或已关闭更新砍价订单状态为已取消
if (ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_FINISHED)
|| ObjectUtil.equal(shopStoreActivityBase.getActivity_state(), StateCode.ACTIVITY_STATE_CLOSED)) {
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
if (!ObjectUtil.equal(activityBase.getActivity_state(), StateCode.ACTIVITY_STATE_NORMAL)) {
if (ObjectUtil.equal(activityBase.getActivity_state(), StateCode.ACTIVITY_STATE_FINISHED)
|| ObjectUtil.equal(activityBase.getActivity_state(), StateCode.ACTIVITY_STATE_CLOSED)) {
updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Canceled);
}
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已失效"));
return CommonResult.failed(I18nUtil._("抱歉,砍价活动已结束啦!"));
}
Date now = new Date();
Long expired_at = shopActivityCutprice.getExpired_at();
Long expiredAt = cutprice.getExpired_at();
// 检查砍价订单是否过期
if (CheckUtil.isNotEmpty(expired_at) && expired_at < now.getTime()) {
// 砍价订单已过期更新状态为已过期
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
return CommonResult.failed(I18nUtil._("砍价已过期,下次早点来!"));
// 检查砍价记录是否过期
if (CheckUtil.isNotEmpty(expiredAt) && expiredAt < now.getTime()) {
updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
return CommonResult.failed(I18nUtil._("抱歉,砍价已过期啦!"));
}
// 检查活动是否已结束活动时间已过
if (!shopStoreActivityBaseService.isActivityTimeValid(shopStoreActivityBase, now)) {
// 活动已结束更新砍价订单状态为已过期
updateCutPriceState(shopActivityCutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
return CommonResult.failed(I18nUtil._("砍价已过期,下次早点来!"));
// 检查活动时间是否有效
try {
if (!shopStoreActivityBaseService.isActivityTimeValid(activityBase, now)) {
updateCutPriceState(cutprice.getAc_id(), null, CommonConstant.CutPrice_Order_State_Expired);
return CommonResult.failed(I18nUtil._("活动已结束!"));
}
// 需要检查活动有效期和活动商品库存是否足够
Pair<Boolean, String> 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<ShopActivityCutpriceHistory> 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<ShopActivityCutpriceHistory> 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<ShopActivit
params.eq("activity_id", activity_id);
ShopStoreActivityBase storeActivityBase = shopStoreActivityBaseService.getOne(params);
if (storeActivityBase == null) {
return Pair.of(false, I18nUtil._("抱歉,系统未找到活动记录。"));
return Pair.of(false, I18nUtil._("抱歉,未找到活动记录。"));
}
// 2. 检查活动是否在有效期内
if (!shopStoreActivityBaseService.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime(), new Date())) {
return Pair.of(false, I18nUtil._("该活动已过期,请检查"));
if (!DateTimeUtils.isActivityTimeValid(storeActivityBase.getActivity_starttime(), storeActivityBase.getActivity_endtime(), new Date())) {
return Pair.of(false, I18nUtil._("抱歉,该活动已过期。"));
}
// 3. 检查用户是否有发起的砍价记录
@ -581,7 +632,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
queryWrapper.orderByDesc("ac_id");
ShopActivityCutprice shopActivityCutprice = getOne(queryWrapper);
if (shopActivityCutprice == null) {
return Pair.of(false, I18nUtil._("系统未找到您发起的砍价记录。"));
return Pair.of(false, I18nUtil._("未找到您发起的砍价记录。"));
}
// 4. 检查是否已砍到最低价
@ -594,7 +645,7 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
BigDecimal subPrice = NumberUtil.sub(currPrice, mixSalePrice);
// 当前价格已经等于或低于底价可以立即出手下单
if (NumberUtil.isGreater(subPrice, BigDecimal.ZERO)) {
return Pair.of(false, String.format(I18nUtil._("还剩%.2f元可达活动底价,请继续加油。"), subPrice));
return Pair.of(false, String.format(I18nUtil._("还剩%.2f元就到底价,继续加油。"), subPrice));
}
// 5. 检查是否存在砍价历史记录
QueryWrapper<ShopActivityCutpriceHistory> queryWrapperHistory = new QueryWrapper<>();
@ -627,35 +678,45 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl<ShopActivit
return Pair.of(false, I18nUtil._("活动信息不能为空"));
}
if (shopStoreActivityBase.getActivity_id() == null || shopStoreActivityBase.getActivity_id() <= 0) {
return Pair.of(false, I18nUtil._("活动ID无效"));
}
//activity_state 活动状态(ENUM):0-未开启;1-正常;2-已结束;3-管理员关闭;4-商家关闭
if (shopStoreActivityBase.getActivity_state() != null
&& !CommonConstant.Enable.equals(shopStoreActivityBase.getActivity_state())) {
return Pair.of(false, I18nUtil._("活动已结束,下次再参与!"));
}
// 检查活动是否过期
boolean isActivityTimeValid = shopStoreActivityBaseService.isActivityTimeValid(
boolean isActivityTimeValid = DateTimeUtils.isActivityTimeValid(
shopStoreActivityBase.getActivity_starttime(),
shopStoreActivityBase.getActivity_endtime(),
new Date()
);
if (!isActivityTimeValid) {
return Pair.of(false, I18nUtil._("该活动已过期,下次早点来!"));
return Pair.of(false, I18nUtil._("该活动已过期,下次再参与"));
}
// 检查活动商品库存
Integer productCount = shopStoreActivityBase.getProduct_count();
if (CheckUtil.isEmpty(productCount) || productCount <= 0) {
return Pair.of(false, I18nUtil._("活动商品库存不足,请稍后再来。"));
return Pair.of(false, I18nUtil._("活动商品未给库存,请联系店长!"));
}
// 查询已占用库存的砍价订单数量
QueryWrapper<ShopActivityCutprice> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("activity_id", shopStoreActivityBase.getActivity_id())
.in("state", Arrays.asList(
CommonConstant.CutPrice_Order_State_Finished,
CommonConstant.CutPrice_Order_State_CutFinished,
CommonConstant.CutPrice_Order_State_ING
));
long recordCount = count(queryWrapper);
// QueryWrapper<ShopActivityCutprice> 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._("活动商品不足,稍后再参与!"));
}
// 所有检查通过

View File

@ -65,6 +65,9 @@ public class StoreValidTimeJod extends QuartzJobBean {
}
page++;
}
// 准备筹备中的店铺到期转正常营业
storeBaseService.batchUpdateStoreBizStateToOpening();
}
}

View File

@ -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<ShopStoreActivityBase> activityBaseList;
QueryWrapper<ShopStoreActivityBase> 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<String, Object> activityBaseMap = Convert.toMap(String.class, Object.class, storeActivityBase);
Boolean result = transactionTemplate.execute(status -> {
storeActivityBase.setActivity_state(StateCode.ACTIVITY_STATE_NORMAL);
Map<String, Object> 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<String, Object> 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();
}
}

View File

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

View File

@ -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位置数据

View File

@ -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<Boolean, String> innerSignFlowCreateByFile(String mchMobile);
Pair<Boolean, String> 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);
}

View File

@ -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<EsignCo
@Resource
private EsignContractService esignContractService;
@Lazy
@Resource
private ShopStoreBaseService shopStoreBaseService;
@Resource
private OssService ossService;
@ -86,112 +95,136 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl<EsignCo
/**
* 根据双方营业执照号码(或个人身份证)填充合同模版生成合同文件地址
* <p>
* 后台管理员审核商家通过之后触发调用这个方法生成未签署合同文件预备签署
* 拉卡拉审核通过之后触发调用这个方法生成未签署合同文件预备签署
*
* @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<String, Object>() {{
put("componentKey", "plat_contracts");
put("componentValue", "《平台商户入驻服务框架协议》和《小发同城服务费结算》");
}});
// 签署时间
for (int i = 1; i <= 3; i++) {
int finalI = i;
// 10. 填充合同组件数据
// 平台合同名称
list.add(new HashMap<String, Object>() {{
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<String, Object>() {{
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<String, Object>() {{
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<String, Object>() {{
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<String, Object>() {{
put("componentKey", "mch_company" + finalI);
@ -199,233 +232,242 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl<EsignCo
}});
}
//特殊甲方收款账方企业是公司名个人是个人实名
// 特殊处理甲方收款账户方企业是公司名个人是个人实名
if (!CommonConstant.MCH_ENTITY_TYPE_QY.equals(shopMchEntry.getEntity_type())) {
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_company17");
put("componentValue", shopMchEntry.getContact_name());
}});
}
}
// 甲方法人姓名甲方是个人的时候没有法人直接用个人实名
for (int i = 1; i <= 4; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_legal_person_name" + finalI);
put("componentValue", legalPersonName);
}});
}
// 甲方法人手机号
for (int i = 1; i <= 3; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_legal_person_mobile" + finalI);
put("componentValue", LegalPersonMobile);
}});
}
// 甲方身份证号码
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_legal_person_id_number1");
put("componentValue", legalPersonIdNumber);
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_store_name1");
put("componentValue", shopMchEntry.getStore_name());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "rule_no");
put("componentValue", 3);
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_ratio");
put("componentValue", shopMchEntry.getSplit_ratio());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "settlement_method");
put("componentValue", shopMchEntry.getSettlement_method());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_address1");
put("componentValue", shopMchEntry.getStore_address());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_bank1");
put("componentValue", shopMchEntry.getBank_name());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_account_number1");
put("componentValue", shopMchEntry.getAccount_number());
}});
// 乙方公司名称
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_company1");
put("componentValue", platCompany + "和代理商");
}});
for (int i = 2; i <= 5; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_company" + finalI);
put("componentValue", platCompany);
}});
}
for (int i = 1; i <= 2; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_mobile" + finalI);
put("componentValue", finalEsignPlatformInfo.getLegal_person_mobile());
}});
}
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", finalEsignPlatformInfo.getEmail());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_bank1");
put("componentValue", finalEsignPlatformInfo.getRec_acc_bank_name());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_account_number1");
put("componentValue", finalEsignPlatformInfo.getRec_acc_card_no());
}});
// 代理商相关
if (distributor != null) {
// 有代理商的时候才填充代理商的信息
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company1");
put("componentValue", distributor.getLicense_company());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_mobile1");
put("componentValue", distributor.getLegal_person_mobile());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company2");
put("componentValue", distributor.getLicense_company());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_bank1");
put("componentValue", distributor.getRec_acc_bank_name());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_account_number1");
put("componentValue", distributor.getRec_acc_card_no());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_sign_date1");
put("componentValue", today);
}});
} else {
// 有代理商的时候才填充代理商的信息
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_mobile1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company2");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_bank1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_account_number1");
put("componentValue", "");
}});
}
fillJson.put("components", list);
String jsonParma = fillJson.toString();
//生成签名鉴权方式的的header
Map<String, String> 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<String, Object>() {{
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<String, Object>() {{
put("componentKey", "mch_legal_person_mobile" + finalI);
put("componentValue", legalPersonMobile);
}});
}
// 甲方身份证号码
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_legal_person_id_number1");
put("componentValue", legalPersonIdNumber);
}});
// 甲方店铺名称
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_store_name1");
put("componentValue", shopMchEntry.getStore_name());
}});
// 规则编号
list.add(new HashMap<String, Object>() {{
put("componentKey", "rule_no");
put("componentValue", 3);
}});
// 分账比例
BigDecimal finalSplitRatio = splitRatio;
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_ratio");
put("componentValue", finalSplitRatio);
}});
// 结算方式
list.add(new HashMap<String, Object>() {{
put("componentKey", "settlement_method");
put("componentValue", shopMchEntry.getSettlement_method());
}});
// 甲方地址
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_address1");
put("componentValue", shopMchEntry.getStore_address());
}});
// 甲方银行
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_bank1");
put("componentValue", shopMchEntry.getBank_name());
}});
// 甲方账户号
list.add(new HashMap<String, Object>() {{
put("componentKey", "mch_account_number1");
put("componentValue", shopMchEntry.getAccount_number());
}});
// 乙方公司名称
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_company1");
put("componentValue", platCompany + "和代理商");
}});
// 其他乙方公司名称
for (int i = 2; i <= 5; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_company" + finalI);
put("componentValue", platCompany);
}});
}
// 乙方手机号
for (int i = 1; i <= 2; i++) {
int finalI = i;
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_mobile" + finalI);
put("componentValue", esignPlatformInfo.getLegal_person_mobile());
}});
}
// 乙方邮箱重复填充但保持原逻辑
for (int i = 0; i < 6; i++) {
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_email1");
put("componentValue", esignPlatformInfo.getEmail());
}});
}
// 乙方银行
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_bank1");
put("componentValue", esignPlatformInfo.getRec_acc_bank_name());
}});
// 乙方账户号
list.add(new HashMap<String, Object>() {{
put("componentKey", "plat_account_number1");
put("componentValue", esignPlatformInfo.getRec_acc_card_no());
}});
// 11. 处理代理商相关数据
if (distributor != null) {
// 有代理商的时候填充代理商的信息
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company1");
put("componentValue", distributor.getLicense_company());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_mobile1");
put("componentValue", distributor.getLegal_person_mobile());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company2");
put("componentValue", distributor.getLicense_company());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_bank1");
put("componentValue", distributor.getRec_acc_bank_name());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_account_number1");
put("componentValue", distributor.getRec_acc_card_no());
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_sign_date1");
put("componentValue", today);
}});
log.debug("[合同生成] 已填充代理商信息: {}", distributor.getLicense_company());
} else {
// 无代理商时填充默认值
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_mobile1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_company2");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
put("componentKey", "distr_bank1");
put("componentValue", "");
}});
list.add(new HashMap<String, Object>() {{
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<String, String> 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<String, JSONArray> signPositionMap = getSignPosition(templateId, fileId);
if (signPositionMap != null) {
if (signPositionMap.get("mch") != null) {
@ -441,22 +483,34 @@ public class EsignContractFillingFileServiceImpl extends BaseServiceImpl<EsignCo
}
}
// 17. 保存合同填充记录并预创建签署流程
if (esignContractFillingFileService.trySaveRecord(esignContractFillingFile)) {
successCnt += 1;
log.debug("[合同生成] 合同记录保存成功, fileId: {}", fileId);
// 预新增一个签署合同记录(要不要异步执行)
esignContractService.preCreateSignFlow(esignContractFillingFile.getMobile(), esignContractFillingFile.getDoc_template_id());
// 预新增一个签署合同记录
boolean preCreateResult = esignContractService.preCreateSignFlow(
esignContractFillingFile.getMobile(), esignContractFillingFile.getDoc_template_id());
log.debug("[合同生成] 预创建签署流程结果: {}, 商家手机号: {}, 模板ID: {}",
preCreateResult, esignContractFillingFile.getMobile(), esignContractFillingFile.getDoc_template_id());
} else {
log.error("[合同生成] 保存合同记录失败, fileId: {}", fileId);
}
} catch (EsignDemoException e) {
throw new RuntimeException(e);
} catch (Exception e) {
log.error("[合同生成] 处理合同模板时出现异常, templateId: {}", template.getStr("template_id"), e);
// 继续处理下一个模版不中断整个流程
}
}
return successCnt > 0;
boolean result = successCnt > 0;
log.info("[合同生成] 商家合同文件全部生成完毕, storeId: {}, 总模板数: {}, 成功处理数: {}, 最终结果: {}",
storeId, templates.size(), successCnt, result);
return result;
}
/**
* 获取模版的甲方与乙方印章XY位置数据
*

View File

@ -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<EsignContractMappe
@Resource
private EsignPlatformInfoService esignPlatformInfoService;
@Resource
private ShopStoreBaseService shopStoreBaseService;
@Lazy
@Resource
private LakalaApiService lakalaApiService;
@Lazy
@Resource
private LklLedgerMemberService lklLedgerMemberService;
@Resource
private OssService ossService;
@ -169,8 +156,8 @@ public class EsignContractServiceImpl extends BaseServiceImpl<EsignContractMappe
* @return
*/
@Override
public CommonResult signFlowCreateByFile(String mchMobile) {
Pair<Boolean, String> ret = innerSignFlowCreateByFile(mchMobile);
public CommonResult signFlowCreateByFile(Integer storeId) {
Pair<Boolean, String> ret = innerSignFlowCreateByFile(storeId);
if (!ret.getFirst()) {
return CommonResult.failed(ret.getSecond());
}
@ -179,21 +166,25 @@ public class EsignContractServiceImpl extends BaseServiceImpl<EsignContractMappe
}
@Override
public Pair<Boolean, String> innerSignFlowCreateByFile(String mchMobile) {
//String userId = "0";
if (StrUtil.isBlank(mchMobile)) {
public Pair<Boolean, String> 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<EsignContractMappe
//发起接口请求
EsignHttpResponse createByDocTemplate = EsignHttpHelper.doCommHttp(serverUrl, apiAddr, requestType, jsonParams, header, debug);
log.debug("发起合同签署流程返回消息:{},{}", createByDocTemplate.getStatus(), createByDocTemplate.getBody());
log.info("发起合同签署流程返回消息:{},{}", createByDocTemplate.getStatus(), createByDocTemplate.getBody());
if (createByDocTemplate.getStatus() != 200) {
if (createByDocTemplate.getBody() != null) {
JSONObject resBody = JSONUtil.parseObj(createByDocTemplate.getBody());
@ -254,108 +245,168 @@ public class EsignContractServiceImpl extends BaseServiceImpl<EsignContractMappe
log.debug("签署流程结束通知:body >>> {}", 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<EsignContract>().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<EsignContract>().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<Boolean, String> 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<EsignContractMappe
/**
* 根据商家注册手机号获取一条合同信息
*
* @param mchMobile
* @return
* @param mchMobile 商家注册手机号
* @return EsignContract 合同信息对象若未找到则返回null
*/
@Override
public EsignContract getEsignContractByMchMobile(String mchMobile) {
QueryWrapper<EsignContract> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("mch_mobile", mchMobile);
queryWrapper.eq("status", CommonConstant.Enable);
List<EsignContract> esignContractList = this.list(queryWrapper);
if (CollectionUtil.isEmpty(esignContractList)) {
// 参数校验
if (StrUtil.isBlank(mchMobile)) {
log.warn("查询合同信息失败:商家手机号为空");
return null;
}
return esignContractList.get(0);
try {
QueryWrapper<EsignContract> 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<EsignContract> 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<EsignContract> 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<EsignContract> queryWrapper = new QueryWrapper<>();

View File

@ -149,12 +149,7 @@ public class EsignPlatformInfoServiceImpl extends BaseServiceImpl<EsignPlatformI
queryWrapper.eq("license_number", licenseNumber);
}
List<EsignPlatformInfo> esignPlatformInfos = list(queryWrapper);
if (CollectionUtil.isEmpty(esignPlatformInfos)) {
return null;
}
return esignPlatformInfos.get(0);
return findOne(queryWrapper);
}
/**

View File

@ -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<JSONObject> receiveCompleteNotify(HttpServletRequest request) {
// 完整地址 https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify
JSONObject resp = lakalaPayService.receiveCompleteNotify(request);

View File

@ -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<Boolean, String> 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());
}

View File

@ -54,27 +54,9 @@ public class MqMessageServiceImpl extends BaseServiceImpl<MqMessageMapper, MqMes
return;
}
/*
String jsonString = null;
try {
ObjectMapper objectMapper = new ObjectMapper();
jsonString = objectMapper.writeValueAsString(data);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
MessageProperties properties = new MessageProperties();
properties.setMessageId(uuid);
Message message = new Message(jsonString.getBytes(StandardCharsets.UTF_8), properties);
rabbitTemplate.convertAndSend(exchange, routing_key, message, new CorrelationData(uuid));
*/
// 使用 MessageBuilder 确保设置 messageId
Message message = MessageBuilder.withBody(data.toString().getBytes(StandardCharsets.UTF_8))
.setHeader("messageId", uuid) // 添加自定义头部存储消息ID
// .setContentType("text/plain") // 明确指定内容类型
.setMessageId(uuid) // 设置标准 messageId 属性
.build();

View File

@ -2,6 +2,7 @@ package com.suisung.mall.shop.number.service;
import com.suisung.mall.common.modules.number.ShopNumberSeq;
import com.suisung.mall.core.web.service.IBaseService;
import org.springframework.data.util.Pair;
import java.util.List;
@ -17,6 +18,14 @@ public interface ShopNumberSeqService extends IBaseService<ShopNumberSeq> {
String createNextSeq(String prefix);
/**
* 创建下一个编号(键值对)
*
* @param prefix 前缀
* @return 序号和完整编号
*/
Pair<Long, String> createNextSeqPair(String prefix);
Long createNextNo(String prefix);
List<Long> batchCreateNextNo(String seqName, int batchSize);

View File

@ -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<ShopNumberSeqMappe
return null;
}
/**
* 得到下一个编号键值对
* 方法走到这里会产生串行化集群部署这里不能使用单机锁
*
* @param prefix
* @return
*/
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@DistributedLock(
key = "CREATENEXTSEQ_LOCK", // 锁的key
waitTime = 3, // 等待3秒
leaseTime = 10, // 锁持有10秒
errorMsg = "生成ID繁忙请稍后重试" // 自定义错误消息
)
public synchronized Pair<Long, String> 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<ShopNumberSeqMappe
boolean isLocked = false;
int start = 0;
try {
isLocked = lock.tryLock(2, 30, TimeUnit.SECONDS);
isLocked = lock.tryLock(2, 30, TimeUnit.SECONDS);
if (!isLocked) {
// 获取锁失败可以根据业务逻辑进行重试或抛出异常
throw new RuntimeException("系统繁忙,请稍后再试");
}
log.info("成功获得锁:{}",lockKey);
log.info("成功获得锁:{}", lockKey);
if (null != redisService.get(RedisKey.STOREDATASPECID)) {
start = (Integer) redisService.get(RedisKey.STOREDATASPECID);
redisService.set(RedisKey.STOREDATASPECID, start + batchSize);
@ -305,12 +344,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
} catch (Exception e) {
throw new ApiException(e);
}finally {
} finally {
// 5. 最终检查并释放锁确保锁一定被释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("成功释放锁:{}",lockKey);
log.info("成功释放锁:{}", lockKey);
}
}
@ -322,7 +361,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
* @return
*/
@Override
public List<Integer> getBatchSpecItemId(int batchSize) {
public List<Integer> getBatchSpecItemId(int batchSize) {
// 定义锁的key这个key在所有服务实例中必须一致
String lockKey = "LOCK:" + RedisKey.STOREDATASPECITEMID;
// 2. 获取分布式锁对象
@ -330,12 +369,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
boolean isLocked = false;
int start = 0;
try {
isLocked=lock.tryLock(2,30,TimeUnit.SECONDS);
isLocked = lock.tryLock(2, 30, TimeUnit.SECONDS);
if (!isLocked) {
// 获取锁失败可以根据业务逻辑进行重试或抛出异常
throw new ApiException("系统繁忙,请稍后再试");
}
log.info("成功获得锁:{}",lockKey);
log.info("成功获得锁:{}", lockKey);
if (null != redisService.get(RedisKey.STOREDATASPECITEMID)) {
start = (Integer) redisService.get(RedisKey.STOREDATASPECITEMID);
redisService.set(RedisKey.STOREDATASPECITEMID, start + batchSize);
@ -355,12 +394,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
} catch (InterruptedException e) {
throw new ApiException(e);
}finally {
} finally {
// 5. 最终检查并释放锁确保锁一定被释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("成功释放锁:{}",lockKey);
log.info("成功释放锁:{}", lockKey);
}
}
@ -383,7 +422,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
// 获取锁失败可以根据业务逻辑进行重试或抛出异常
throw new RuntimeException("系统繁忙,请稍后再试");
}
log.info("成功获得锁:{}",lockKey);
log.info("成功获得锁:{}", lockKey);
int start = 0;
if (null != redisService.get(RedisKey.STOREDATACCOUNTBASEID)) {
start = (Integer) redisService.get(RedisKey.STOREDATACCOUNTBASEID);
@ -403,12 +442,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("getBatchUserAccountBaseId获取锁时被中断", e);
}finally {
} finally {
// 5. 最终检查并释放锁确保锁一定被释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("成功释放锁:{}",lockKey);
log.info("成功释放锁:{}", lockKey);
}
}
@ -421,7 +460,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
* @return
*/
@Override
public List<Integer> getBatchLibraryProductId(int batchSize) {
public List<Integer> getBatchLibraryProductId(int batchSize) {
// 定义锁的key这个key在所有服务实例中必须一致
String lockKey = "LOCK:" + RedisKey.STOREDATALIBRARYID;
// 2. 获取分布式锁对象
@ -434,7 +473,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
// 获取锁失败可以根据业务逻辑进行重试或抛出异常
throw new RuntimeException("系统繁忙,请稍后再试");
}
log.info("成功获得锁:{}",lockKey);
log.info("成功获得锁:{}", lockKey);
if (null != redisService.get(RedisKey.STOREDATALIBRARYID)) {
start = (Integer) redisService.get(RedisKey.STOREDATALIBRARYID);
redisService.set(RedisKey.STOREDATALIBRARYID, start + batchSize);
@ -454,12 +493,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
} finally {
// 5. 最终检查并释放锁确保锁一定被释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("成功释放锁:{}",lockKey);
log.info("成功释放锁:{}", lockKey);
}
}
@ -478,12 +517,12 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
boolean isLocked = false;
long start = 0;
try {
isLocked=lock.tryLock(2,30,TimeUnit.SECONDS);
isLocked = lock.tryLock(2, 30, TimeUnit.SECONDS);
if (!isLocked) {
// 获取锁失败可以根据业务逻辑进行重试或抛出异常
throw new ApiException("系统繁忙,请稍后再试");
}
log.info("成功获得锁:{}",lockKey);
log.info("成功获得锁:{}", lockKey);
QueryWrapper<ShopPageBase> 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<ShopNumberSeqMappe
return LongStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
} catch (InterruptedException e) {
throw new ApiException(e);
}finally {
} finally {
// 5. 最终检查并释放锁确保锁一定被释放
if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
lock.unlock();
}
log.info("成功释放锁:{}",lockKey);
log.info("成功释放锁:{}", lockKey);
}
}
/**
* 初始化主键
*/
private void syncPrimaryKey(){
List<ShopNumberSeq> shopNumberSeqList=new ArrayList<>();
QueryWrapper<ShopProductBase> baseWrapper=new QueryWrapper<>();
private void syncPrimaryKey() {
List<ShopNumberSeq> shopNumberSeqList = new ArrayList<>();
QueryWrapper<ShopProductBase> 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<ShopNumberSeq> 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<ShopProductItem> itemQuery=new QueryWrapper<>();
QueryWrapper<ShopProductItem> 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<ShopNumberSeq> 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<ShopNumberSeqMappe
}
}

View File

@ -30,7 +30,6 @@ public class MessageListener {
listener(dataStr, channel, message);
}
// @RabbitHandler
public void listener(String data, Channel channel, Message message) {
MsgTO msgTO = JSONUtil.toBean(data, MsgTO.class);
String messageId = message.getMessageProperties().getMessageId();

View File

@ -69,8 +69,7 @@ public class OrderPayedListener {
String dataStr = new String(data, StandardCharsets.UTF_8);
listener(dataStr, channel, message);
}
// @RabbitHandler
public void listener(String data, Channel channel, Message message) throws IOException, InterruptedException {
// String messageId = message.getMessageProperties().getMessageId();
if (StrUtil.isBlank(data)) {
@ -115,28 +114,20 @@ public class OrderPayedListener {
// 检查订单是否已经处理过幂等性检查
if (isOrderPaid(orderInfoOld)) {
logger.info("[订单支付监听] 订单已支付无需重复处理订单ID: {}", orderId);
logger.debug("[订单支付监听] 订单已支付无需重复处理订单ID: {}", orderId);
flag = true;
continue;
}
if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY) {
// 待支付状态
logger.info("[订单支付监听] 处理待支付订单. 订单ID: {}", orderId);
flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId));
if (StateCode.PAYMENT_TYPE_OFFLINE == orderInfoOld.getPayment_type_id().intValue()) {
logger.debug("[订单支付监听] 处理线下支付订单. 订单ID: {}", orderId);
ShopOrderInfo orderInfo = new ShopOrderInfo();
orderInfo.setOrder_id(orderId);
orderInfo.setOrder_is_paid(StateCode.ORDER_PAID_STATE_YES);
flag = shopOrderInfoService.edit(orderInfo);
} else {
//判断是否线下支付
if (StateCode.PAYMENT_TYPE_OFFLINE == orderInfoOld.getPayment_type_id().intValue()) {
//线下支付直接处理订单支付状态 不处理订单状态
logger.info("[订单支付监听] 处理线下支付订单. 订单ID: {}", orderId);
ShopOrderInfo orderInfo = new ShopOrderInfo();
orderInfo.setOrder_id(orderId);
orderInfo.setOrder_is_paid(StateCode.ORDER_PAID_STATE_YES);
flag = shopOrderInfoService.edit(orderInfo);
} else {
logger.info("[订单支付监听] 处理其他支付订单. 订单ID: {}", orderId);
flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId));
}
logger.debug("[订单支付监听] 处理支付订单. 订单ID: {}", orderId);
flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId));
}
logger.info("[订单支付监听] 订单ID: {},支付异步通知回调处理是否成功: {} ", flag, orderId);

View File

@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.suisung.mall.common.modules.order.ShopOrderReturn;
import com.suisung.mall.common.pojo.vo.OrderPrintVO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@ -30,4 +31,13 @@ public interface ShopOrderReturnMapper extends BaseMapper<ShopOrderReturn> {
List<Map<String, Object>> statisticCountSeller(@Param("end") Date end, @Param("days") int days, @Param("store_id") int store_id);
IPage<Map> getReturnGroupByOrderId(Page<Map> page, @Param("map") Map params);
/**
* 根据退货订单id获取退货订单打印信息
*
* @param orderId
* @param returnId
* @return
*/
OrderPrintVO fetchReturnOrderPrintInfo(@Param("orderId") String orderId, @Param("returnId") String returnId);
}

View File

@ -522,7 +522,7 @@ public interface ShopOrderBaseService extends IBaseService<ShopOrderBase> {
* @param payState 支付状态参考StateCode.ORDER_PAID_STATE_YES
* @return
*/
Map getOrderPrintInfo(Integer storeId, String orderId, Integer payState);
Map fetchOrderPrintInfo(Integer storeId, String orderId, Integer payState);
/**

View File

@ -143,4 +143,13 @@ public interface ShopOrderInfoService extends IBaseService<ShopOrderInfo> {
* @return
*/
List<BookingArgDTO> genBookingOrderArgList(String storeId);
/**
* 获取某个活动订单成功数量
*
* @param activityId 活动ID
* @param activityTypeId 活动类型ID 可选参数
* @return
*/
long fetchActivityOrderSuccessCount(String activityId, String activityTypeId);
}

View File

@ -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<ShopOrderItem> {
* @param orderId
* @return
*/
List<ShopStoreOrderProductPrintVO> selectOrderItemPrintInfo(String orderId);
List<OrderItemPrintVO> selectOrderItemPrintInfo(String orderId);
}

View File

@ -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<ShopOrderReturn> {
CommonResult doRefundForMch(JSONObject params);
/**
* 根据退货订单id获取退货订单打印信息
*
* @param orderId
* @param returnId
* @return
*/
OrderPrintVO fetchReturnOrderPrintInfo(String orderId, String returnId);
}

View File

@ -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<ShopOrderBaseMappe
cartData.put("gb_id", gbId); // 拼团ID, 为0在创建拼团
}
// 判断是否砍价购买
Integer acId = getParameter("ac_id", Integer.class);
// 判断是否为砍价活动
Boolean isCutPriceActivity = shopStoreActivityBaseService.isCutPriceActivity(activityId);
if (CheckUtil.isNotEmpty(acId) && isCutPriceActivity) {
cartData.put("ac_id", acId);
// 校验砍价活动用户是否可以立即购买了
Pair<Boolean, String> canDoOrderCutPriceActivity = shopActivityCutpriceService.canDoOrderCutPriceActivity(acId, userId);
if (!canDoOrderCutPriceActivity.getFirst()) {
logger.error(canDoOrderCutPriceActivity.getSecond());
throw new ApiException(I18nUtil._("砍价活动未达标,请继续努力,再提交订单。"));
}
}
// 重要邮费检测和计算
dealWithCalFee(calFreight, cartData, chainId, isEdu);
@ -1621,22 +1607,29 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
}
// // 预约订单检测
// Integer bookingState = Convert.toInt(getParameter("booking_state"));
// if (CheckUtil.isNotEmpty(bookingState) && CommonConstant.Order_Booking_State_YY.equals(bookingState)) {
// String bookingBeginTime = getParameter("booking_begin_time");
// String bookingEndTime = getParameter("booking_end_time");
// Long bookingAt = getParameter("booking_at", 0L);
// Pair<Boolean, String> 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<Boolean, String> 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<String> orderIdRow = addOrder(cartData, true, false, null);
@ -1662,6 +1655,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
// cartData.put("packing_fee", packingFee);
// }
// 判断是否拼团购买
if (gbId != null) {
QueryWrapper<ShopActivityGroupbookingHistory> historyQueryWrapper = new QueryWrapper<>();
@ -1673,10 +1667,6 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
}
// 判断是否砍价购买
if (acId != null) {
}
PayUserResource userResRow = payService.getPayUserResource(userId);
if (userResRow != null) {
cartData.put("user_money", userResRow.getUser_money());
@ -5032,7 +5022,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
log.info("[确认收货] 处理订单: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
} else {
not_eligible_orders.add(order_row.getOrder_id());
log.warn("[确认收货] 订单状态不满足收货条件: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
log.warn("[确认收货] 订单状态不满足确认收货条件: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
}
}
@ -5198,11 +5188,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
/**
* 延迟 消息跳转路径设置接口
* 延迟 消息跳转路径设置接口最好使用消息队列延迟1秒消费
*
* @param orderIds
*/
private void processOrderJumpPathAsync(List<String> orderIds) {
public void processOrderJumpPathAsync(List<String> orderIds) {
if (CollectionUtils.isEmpty(orderIds)) {
return;
}
@ -5211,15 +5201,15 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
for (String orderId : orderIds) {
try {
// 控制API调用频率
Thread.sleep(300); // 根据实际API限制调整
Thread.sleep(1000); // 根据实际API限制调整
wxOrderShippingService.setMsgJumpPath(orderId);
log.info("[异步处理] 订单跳转路径设置入口 processOrderJumpPathAsync订单ID: {}", orderId);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("[异步处理] 线程中断订单ID: {}", orderId);
log.error("[异步处理] 线程中断订单ID: {}", orderId);
break;
} catch (Exception e) {
log.warn("[异步处理] 消息跳转路径设置失败订单ID: {}, 错误: {}", orderId, e.getMessage());
// 继续处理下一个订单
}
}
}, executor);
@ -5299,17 +5289,17 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
throw new ApiException(I18nUtil._("无符合取消条件的订单!"));
}
ShopOrderBase shopOrderBase = new ShopOrderBase();
shopOrderBase.setOrder_id(order_id);
shopOrderBase.setOrder_state_id(StateCode.ORDER_STATE_CANCEL);
if (!shopOrderBaseService.edit(shopOrderBase)) {
ShopOrderBase shopOrderBaseUpd = new ShopOrderBase();
shopOrderBaseUpd.setOrder_id(order_id);
shopOrderBaseUpd.setOrder_state_id(StateCode.ORDER_STATE_CANCEL);
if (!shopOrderBaseService.edit(shopOrderBaseUpd)) {
throw new ApiException(ResultCode.FAILED);
}
ShopOrderInfo orderInfo = new ShopOrderInfo();
orderInfo.setOrder_id(order_id);
orderInfo.setOrder_state_id(StateCode.ORDER_STATE_CANCEL);
if (!shopOrderInfoService.edit(orderInfo)) {
ShopOrderInfo orderInfoUpd = new ShopOrderInfo();
orderInfoUpd.setOrder_id(order_id);
orderInfoUpd.setOrder_state_id(StateCode.ORDER_STATE_CANCEL);
if (!shopOrderInfoService.edit(orderInfoUpd)) {
throw new ApiException(ResultCode.FAILED);
}
@ -5323,6 +5313,26 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
shopOrderDataService.edit(shopOrderData);
}
// 取消顺丰同城配送订单
// === 7. 特殊场景顺丰同城配送且全单退的时候才触发顺丰同城订单取消 ===
if (shopOrderInfo.getDelivery_type_id() != null
&& StateCode.DELIVERY_TYPE_SAME_CITY == shopOrderInfo.getDelivery_type_id().intValue()) {
try {
logger.warn("开始取消顺丰同城配送订单orderId: {}, delivery_type_id: {}", order_id, shopOrderInfo.getDelivery_type_id());
ThirdApiRes sfResult = sfExpressApiService.cancelOrder(order_id, 313, "用户或商家取消订单。");
if (sfResult != null && !sfResult.getError_code().equals(0)) {
log.error("顺丰同城取消订单返回错误orderId: {}, errorCode: {}, errorMsg: {}",
order_id, sfResult.getError_code(), sfResult.getError_msg());
} else {
log.info("顺丰同城订单取消成功orderId: {}", order_id);
}
} catch (Exception e) {
log.error("顺丰同城取消订单异常orderId: {}", order_id, e);
// 可以考虑添加补偿机制或异步重试
}
}
// 积分退还 order_resource_ext1 默认为积分
ShopOrderData order_data_row = shopOrderDataService.get(order_id);
@ -6411,7 +6421,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
type_code = StrUtil.isBlank(type_code) ? "DD" : type_code;
String xid = RootContext.getXID();
RootContext.unbind();
String order_id = shopNumberSeqService.createNextSeq(type_code);
Pair<Long, String> 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<ShopOrderItem> item_rows = new ArrayList();
@ -6656,6 +6668,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
info_row.setOrder_id(order_id);
info_row.setOrder_pickup_num(seqNo); //重要配送员的取单号
info_row.setOrder_title(product_item_name); // 订单标题
if (fixOrderVo != null && fixOrderVo.isFix_price() && StrUtil.isNotEmpty(fixOrderVo.getOrder_title())) {
info_row.setOrder_title(fixOrderVo.getOrder_title()); // 订单标题
@ -6700,6 +6713,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
info_row.setCoupon_type_id(shopProductIndex.getCoupon_type_id());
}
// 保存活动id和类型
info_row.setActivity_id(Convert.toStr(cart_data.get("activity_id"), "0"));
info_row.setActivity_type_id(Convert.toStr(cart_data.get("activity_type_id"), "0"));
// 预约订单检测
Integer bookingState = Convert.toInt(getParameter("booking_state"));
if (CheckUtil.isNotEmpty(bookingState) && CommonConstant.Order_Booking_State_YY.equals(bookingState)) {
@ -8777,8 +8794,16 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
return data;
}
/**
* 获取订单打印信息
*
* @param storeId
* @param orderId
* @param payState
* @return
*/
@Override
public Map getOrderPrintInfo(Integer storeId, String orderId, Integer payState) {
public Map fetchOrderPrintInfo(Integer storeId, String orderId, Integer payState) {
if (StrUtil.isBlank(orderId)) {
logger.error("缺少必要参数orderId:{}");
return null;
@ -8790,14 +8815,14 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
// 订单商品列表
List<ShopStoreOrderProductPrintVO> shopStoreOrderProductPrintVOList = shopOrderItemService.selectOrderItemPrintInfo(orderId);
if (CollUtil.isEmpty(shopStoreOrderProductPrintVOList)) {
List<OrderItemPrintVO> orderItemPrintVOList = shopOrderItemService.selectOrderItemPrintInfo(orderId);
if (CollUtil.isEmpty(orderItemPrintVOList)) {
return null;
}
// 重构实体类处理标题数量价格适应长度
List<ShopStoreOrderProductPrintVO> orderItems = new ArrayList<>();
for (ShopStoreOrderProductPrintVO ent : shopStoreOrderProductPrintVOList) {
List<OrderItemPrintVO> orderItems = new ArrayList<>();
for (OrderItemPrintVO ent : orderItemPrintVOList) {
orderItems.add(ent.rebuild());
}

View File

@ -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<ShopOrderInfoMappe
QueryWrapper<ShopOrderInfo> 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<ShopOrderInfoMappe
/**
* 已支付的订单生成取单号打票机并打印订单
*
* @param storeId
* @param orderId
* @param storeId 商家ID
* @param orderId 订单ID
* @return 取货单号
*/
// @Transactional
@ -402,11 +403,22 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
}
try {
// 生成取单号
Long orderPickupNum = genTodayPickupNum(storeId);
if (orderPickupNum == null || orderPickupNum <= 0) {
logger.error("生成取单号失败: storeId={}", storeId);
return 0L;
// 生成取单号废弃
// Long orderPickupNum = genTodayPickupNum(storeId);
// if (orderPickupNum == null || orderPickupNum <= 0) {
// logger.error("生成取单号失败: storeId={}", storeId);
// return 0L;
// }
Long orderPickupNum = 0L;
// 如果订单中尚未设置取单号则从订单ID中提取
if (CheckUtil.isEmpty(orderInfoOld.getOrder_pickup_num())) {
// 从订单ID中提取最后的数字部分作为取单号
// 例如订单ID为 DD_20250510_11则提取出 11 作为取单号
orderPickupNum = Convert.toLong(StrUtil.subAfter(orderId, "_", true));
} else {
// 如果已有取单号则使用现有的
orderPickupNum = orderInfoOld.getOrder_pickup_num();
}
// 更新订单信息
@ -431,6 +443,7 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
}
}
/**
* 根据订单Id订单状态配送方式退款状态 获取订单的数量
*
@ -1231,6 +1244,49 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
return bookingArgList;
}
/**
* 获取某个活动订单成功数量
*
* @param activityId 活动ID
* @param activityTypeId 活动类型ID 可选参数
* @return 活动订单成功数量
*/
@Override
public long fetchActivityOrderSuccessCount(String activityId, String activityTypeId) {
// 参数校验
if (StrUtil.isBlank(activityId)) {
logger.warn("活动Id无效");
return 0L;
}
LambdaQueryWrapper<ShopOrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShopOrderInfo::getActivity_id, activityId);
// 如果activityTypeId不为空则添加条件
if (StrUtil.isNotBlank(activityTypeId)) {
queryWrapper.eq(ShopOrderInfo::getActivity_type_id, activityTypeId);
}
// 添加订单状态条件
List<Integer> 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获取有效店铺的有效营业时间段

View File

@ -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<ShopOrderItemMappe
}
@Override
public List<ShopStoreOrderProductPrintVO> selectOrderItemPrintInfo(String orderId) {
public List<OrderItemPrintVO> selectOrderItemPrintInfo(String orderId) {
// 输入验证
if (StrUtil.isBlank(orderId)) {
return Collections.emptyList();
@ -238,7 +238,7 @@ public class ShopOrderItemServiceImpl extends BaseServiceImpl<ShopOrderItemMappe
}
return mList.stream().map(map -> {
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")));

View File

@ -268,7 +268,9 @@ public class ShopOrderLogisticsServiceImpl extends BaseServiceImpl<ShopOrderLogi
logisticsType = 3;
}
if (deliveryType != null && deliveryType.equals(StateCode.DELIVERY_TYPE_SELF_PICK_UP)) {
// 门店自提商家送货上门
if (deliveryType != null &&
(deliveryType.equals(StateCode.DELIVERY_TYPE_SELF_PICK_UP) || deliveryType.equals(StateCode.DELIVERY_TYPE_IN_STORE_SERVICE))) {
logisticsType = 4;
}

View File

@ -35,7 +35,9 @@ import com.suisung.mall.common.modules.product.ShopProductInfo;
import com.suisung.mall.common.modules.product.ShopProductItem;
import com.suisung.mall.common.modules.store.ShopStoreBase;
import com.suisung.mall.common.modules.store.ShopStoreShippingAddress;
import com.suisung.mall.common.pojo.vo.OrderPrintVO;
import com.suisung.mall.common.service.MessageService;
import com.suisung.mall.common.service.impl.CommonService;
import com.suisung.mall.common.utils.*;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.base.service.ShopBaseProductUnitService;
@ -55,9 +57,9 @@ import com.suisung.mall.shop.product.service.ShopProductBaseService;
import com.suisung.mall.shop.product.service.ShopProductIndexService;
import com.suisung.mall.shop.product.service.ShopProductInfoService;
import com.suisung.mall.shop.product.service.ShopProductItemService;
import com.suisung.mall.shop.sfexpress.service.SFExpressApiService;
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
import com.suisung.mall.shop.store.service.ShopStoreConfigService;
import com.suisung.mall.shop.store.service.ShopStorePrinterService;
import com.suisung.mall.shop.store.service.ShopStoreShippingAddressService;
import com.suisung.mall.shop.sync.service.SyncThirdDataService;
import io.seata.spring.annotation.GlobalTransactional;
@ -195,13 +197,17 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
@Autowired
private LklOrderSeparateService lklOrderSeparateService;
@Lazy
@Autowired
private ShopStorePrinterService shopStorePrinterService;
@Lazy
@Autowired
private LklOrderDrawService lklOrderDrawService;
@Lazy
@Autowired
private SFExpressApiService sfExpressApiService;
private CommonService commonService;
@Autowired
private MessageService messageService;
@ -617,6 +623,9 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
data.put("return", orderReturn);
data.put("items", shopOrderReturnItems);
// 打票机打印退款订单到店(异步执行)
shopStorePrinterService.printShopStoreReturnOrder(return_id);
// 发送消息通知
if (needSendMsg != null && needSendMsg) {
// 退款提醒商家
@ -981,20 +990,25 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
try {
String remark = "同城配送异常自动退款!";
QueryWrapper<ShopOrderReturn> 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<ShopOrderReturn> 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<ShopOrderReturnM
Integer store_id = Optional.ofNullable(shopOrderReturn.getStore_id())
.orElseGet(() -> Convert.toInt(getCurrentUser().getStore_id()));
List<String> return_ids = Convert.toList(String.class, shopOrderReturn.getReturn_id());
String returnId = shopOrderReturn.getReturn_id();
List<String> return_ids = Convert.toList(String.class, returnId);
List<ShopOrderReturn> orderReturns = gets(return_ids);
if (!CheckUtil.checkDataRights(store_id, orderReturns, ShopOrderReturn::getStore_id)) {
@ -1415,6 +1431,9 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
}
}
// 打票机打印退款订单到店(异步执行)
shopStorePrinterService.printShopStoreReturnOrder(returnId);
// 3. 通知买家
messageService.sendNoticeMsg(
shopOrderReturn.getBuyer_user_id(),
@ -1580,12 +1599,12 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
QueryWrapper<ShopOrderReturnItem> returnItemQueryWrapper = new QueryWrapper<>();
returnItemQueryWrapper.in("return_id", return_ids);
List<ShopOrderReturnItem> returnItems = orderReturnItemService.find(returnItemQueryWrapper);
QueryWrapper<ShopOrderReturn> shopOrderReturnQueryWrapper= new QueryWrapper<>();
QueryWrapper<ShopOrderReturn> shopOrderReturnQueryWrapper = new QueryWrapper<>();
shopOrderReturnQueryWrapper.in("return_id", return_ids);
List<ShopOrderReturn> shopOrderReturnList= shopOrderReturnService.find(shopOrderReturnQueryWrapper);
Map<String,Long> shopOrderReturnMap=new HashMap<>();
if(!shopOrderReturnList.isEmpty()){
shopOrderReturnMap=shopOrderReturnList.stream().collect(Collectors.toMap(ShopOrderReturn::getReturn_id, s->s.getUpdated_at().getTime()));
List<ShopOrderReturn> shopOrderReturnList = shopOrderReturnService.find(shopOrderReturnQueryWrapper);
Map<String, Long> 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<ShopOrderReturnM
// RMK 第三方数据同步相关redis 新增返还思迅库存
Map<String, Integer> 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<ShopOrderReturnM
// RMK 第三方数据同步相关redis 新增返还思迅库存
Map<String, Integer> 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<ShopOrderReturnM
// shopOrderReturn.setReturn_finish_time(now);
//
ShopOrderReturn updateRefundStatus = new ShopOrderReturn();
updateRefundStatus.setReturn_is_paid(1); // 0-退货未完成1-退货完成
updateRefundStatus.setReturn_is_paid(CommonConstant.Enable); // 0-退货未完成1-退货完成
updateRefundStatus.setReturn_finish_time(now);
QueryWrapper<ShopOrderReturn> updateWrapper = new QueryWrapper<>();
@ -2564,7 +2583,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
}
/**
* 对已存在部分退款的订单进行剩余商品的全部退款(前提是未发货之前)
* 对已存在部分退款的订单进行剩余商品的全部退款也适合全部退款(前提是未发货之前)
* 该方法用于处理订单中部分商品已经申请退款后对剩余商品进行整单退款的场景
*
* @param orderId 订单ID
@ -2612,7 +2631,6 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
QueryWrapper<ShopOrderItem> itemQueryWrapper = new QueryWrapper<>();
itemQueryWrapper.eq("order_id", orderId);
List<ShopOrderItem> 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<ShopOrderReturnM
if (lklOrderSeparateService.isOrderSeparated(orderId)) {
// TODO 后期如果已经分账的订单一定强硬退款可能需要拉卡拉撤销分账再退款已提现的真的没有办法退款了
return CommonResult.failed("订单已三方结清,无法退款");
return CommonResult.failed("订单已三方结清,无法退款");
}
List<ShopOrderItem> orderItems = shopOrderItemService.find(new QueryWrapper<ShopOrderItem>().eq("order_id", orderId));
@ -2983,16 +3001,16 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
throw new ApiException("退款审核失败!");
}
// === 7. 特殊场景同城配送订单取消 ===
if (orderInfo.getDelivery_type_id() != null
&& StateCode.DELIVERY_TYPE_SAME_CITY == orderInfo.getDelivery_type_id().intValue()
&& !isPartialRefund) {
try {
sfExpressApiService.cancelOrder(orderId, 313, refundOrder.getReturn_store_message());
} catch (Exception e) {
log.error("顺丰同城取消订单失败", e);
}
}
// // === 7. 特殊场景顺丰同城配送且全单退的时候才触发顺丰同城订单取消 === RMK 在ShopOrderBaseServiceImpl.cancel取消订单方法里执行了
// if (orderInfo.getDelivery_type_id() != null
// && StateCode.DELIVERY_TYPE_SAME_CITY == orderInfo.getDelivery_type_id().intValue()
// && !isPartialRefund) {
// try {
// sfExpressApiService.cancelOrder(orderId, 313, refundOrder.getReturn_store_message());
// } catch (Exception e) {
// log.error("顺丰同城取消订单失败", e);
// }
// }
return CommonResult.success();
} catch (Exception e) {
@ -3008,6 +3026,37 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
}
}
/**
* 根据退货订单id获取退货订单打印信息
*
* @param orderId 订单ID 可选参数
* @param returnId 退货单ID 必填参数
* @return 退货订单打印信息对象
*/
@Override
public OrderPrintVO fetchReturnOrderPrintInfo(String orderId, String returnId) {
// 参数校验
if (StrUtil.isBlank(returnId)) {
return null;
}
try {
// 调用Mapper方法获取退货订单打印信息 ShopStoreOrderPrintVO
OrderPrintVO shopOrderPrintVO = shopOrderReturnMapper.fetchReturnOrderPrintInfo(orderId, returnId);
if (shopOrderPrintVO == null) {
return null;
}
// 设置配送方式名称
shopOrderPrintVO.setDelivery_type_name(CommonService.getDeliveryExpressName(shopOrderPrintVO.getDelivery_type_id()));
return shopOrderPrintVO;
} catch (Exception e) {
logger.error("获取退货订单打印信息失败, orderId: {}, returnId: {}", orderId, returnId, e);
return null;
}
}
/**
* 获取拒绝退货的详细原因

View File

@ -131,7 +131,7 @@ public class ShopPageAppServiceImpl extends BaseServiceImpl<ShopPageAppMapper, S
* @param subsite_id
* @param store_id
* @param tpl_id
* @param app_type
* @param app_type 69 107 3
*/
@Override
public void getModel(ModelAndView view, Integer subsite_id, Integer store_id, Integer tpl_id, Integer app_type) {
@ -152,7 +152,6 @@ public class ShopPageAppServiceImpl extends BaseServiceImpl<ShopPageAppMapper, S
String page_nav = pageBase.get("PageNav").toString();
String page_config = pageBase.get("PageConfig").toString();
//用户中心菜单
//用户中心菜单
Map menu = new HashMap();
String app_member_center = accountBaseConfigService.getConfig("app_member_center");

View File

@ -164,6 +164,14 @@ public interface SFExpressApiService {
*/
ThirdApiRes notifyProductReady(Map<String, Object> 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);
/**
* 商家自行配送发货
*

View File

@ -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<Boolean, String> 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<String, Object> 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<String, Object> 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<String> 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<String, Object> 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<ShopOrderItem> 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<String, Integer> 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<String, Integer> 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("操作成功");

View File

@ -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 = "店铺主营分类(类目)")

View File

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

View File

@ -69,15 +69,15 @@ public interface ShopStoreActivityBaseService extends IBaseService<ShopStoreActi
*/
boolean isActivityTimeValid(ShopStoreActivityBase shopStoreActivityBase, Date checkTime);
/**
* 验证活动时间是否有效
*
* @param starTime 活动开始时间
* @param endTime 活动结束时间
* @param checkTime 待验证时间
* @return 时间是否有效
*/
boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime);
// /**
// * 验证活动时间是否有效
// *
// * @param starTime 活动开始时间
// * @param endTime 活动结束时间
// * @param checkTime 待验证时间
// * @return 时间是否有效
// */
// boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime);
Map listsMarketing();
@ -199,4 +199,21 @@ public interface ShopStoreActivityBaseService extends IBaseService<ShopStoreActi
Boolean isCutPriceActivity(Integer activity_id);
/**
* 修改活动状态
*
* @param activity_id
* @param activity_state
* @return
*/
boolean updateActivityState(Integer activity_id, Integer activity_state);
/**
* 判断活动商品是否售完
*
* @param activity_id
* @return
*/
boolean isActProdSoldOut(Integer activity_id);
}

View File

@ -255,6 +255,15 @@ public interface ShopStoreBaseService extends IBaseService<ShopStoreBase> {
*/
Date getLatestBizOpeningDate(String storeIds);
/**
* 批量更新店铺营业状态为营业中
* 将符合条件的"筹备中"状态店铺更新为"营业中"状态
*
* @return Boolean 更新是否成功
*/
Boolean batchUpdateStoreBizStateToOpening();
// Page<ShopStoreBase> getMobileStoreList(Integer page, Integer rows);
}

View File

@ -80,6 +80,14 @@ public interface ShopStorePrinterService extends IBaseService<ShopStorePrinter>
*/
Boolean printShopStoreOrder(Integer storeId, String orderId);
/**
* 退货成功后立即打印门店的退货信息
*
* @param returnId
* @return
*/
Boolean printShopStoreReturnOrder(String returnId);
/**
* 获取店铺所有有效打印机
*

View File

@ -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<ShopMchEntryMapper,
Map<String, Object> 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<ShopMchEntryMapper,
Map<String, Object> 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<ShopMchEntryMapper,
}
}
/**
* 根据店铺Id获取拉卡拉的审核状态和下载地址
*
* @param storeId
* @return
*/
@Override
public ShopMchEntry getLklContractStatusUrl(Integer storeId) {
// 参数校验
if (storeId == null) {
log.warn("查询合同状态和URL失败店铺ID为空");
return null;
}
try {
LambdaQueryWrapper<ShopMchEntry> 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;
}
}
}

View File

@ -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<ShopStoreA
ShopOrderBaseService shopOrderBaseService;
@Autowired
ShopUserVoucherService shopUserVoucherService;
@Lazy
@Autowired
ShopOrderInfoService shopOrderInfoService;
@Autowired
private ShopStoreActivityBaseMapper shopStoreActivityBaseMapper;
@Lazy
@ -134,6 +136,7 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
private ShopActivityGroupbookingService shopActivityGroupbookingService;
@Autowired
private ShopStoreActivityCodeService shopStoreActivityCodeService;
@Lazy
@Autowired
private ShopOrderDataService shopOrderDataService;
@Autowired
@ -2090,29 +2093,38 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
Date starTime = shopStoreActivityBase.getActivity_starttime();
Date endTime = shopStoreActivityBase.getActivity_endtime();
return isActivityTimeValid(starTime, endTime, checkTime);
return DateTimeUtils.isActivityTimeValid(starTime, endTime, checkTime);
}
/**
* 验证活动时间是否有效
*
* @param starTime 活动开始时间
* @param endTime 活动结束时间
* @param checkTime 待验证时间
* @return 时间是否有效
*/
@Override
public boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime) {
if (starTime == null || endTime == null) {
return false;
}
// /**
// * 验证活动时间是否有效
// *
// * @param starTime 活动开始时间
// * @param endTime 活动结束时间
// * @param checkTime 待验证时间
// * @return 时间是否有效
// */
// @Override
// public boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime) {
// logger.debug("检查活动时间有效性 - 开始时间: {}, 结束时间: {}, 检查时间: {}", starTime, endTime, checkTime);
//
// if (starTime == null || endTime == null) {
// logger.error("活动时间验证失败 - 开始时间或结束时间为null. 开始时间: {}, 结束时间: {}", starTime, endTime);
// return false;
// }
//
// if (checkTime == null) {
// checkTime = new Date();
// logger.warn("检查时间为空,使用当前时间: {}", checkTime);
// }
//
// boolean isValid = !checkTime.before(starTime) && !checkTime.after(endTime);
// logger.debug("活动时间验证结果: {} (检查时间>=开始时间, 检查时间<=结束时间)",
// 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<ShopStoreA
}
}
/**
* 修改活动状态
*
* @param activity_id 活动ID
* @param activity_state 活动状态
* @return boolean 是否更新成功
*/
@Override
public boolean updateActivityState(Integer activity_id, Integer activity_state) {
// 参数校验
if (activity_id == null || activity_id <= 0) {
logger.warn("活动ID无效: {}", activity_id);
return false;
}
if (activity_state == null) {
logger.warn("活动状态不能为空: activity_id={}", activity_id);
return false;
}
try {
// 直接使用LambdaUpdateWrapper提高性能避免创建额外实体对象
boolean result = update(new UpdateWrapper<ShopStoreActivityBase>()
.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<ShopStoreA
.ge("activity_item_endtime", activity.get("activity_starttime"));
List<ShopStoreActivityItem> item_row = shopStoreActivityItemService.find(itemQueryWrapper);
List<Long> 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<Long> 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<Integer> activity_id_row = item_row.stream().map(s -> s.getActivity_id()).distinct().collect(Collectors.toList());
List<Long> used_id_row = new ArrayList<>();
@ -5553,13 +5659,14 @@ public class ShopStoreActivityBaseServiceImpl extends BaseServiceImpl<ShopStoreA
activity_rule.put("product_image", product_row.getProduct_image());
activity_rule.put("item_unit_price", item_row.getItem_unit_price());
// cut_hour - 砍价有效期小时
Integer cut_hour = getParameter("cut_hour", 72);
// product_count - 参与活动商品总数
Integer product_count = getParameter("product_count", 3);
Integer cut_hour = getParameter("cut_hour", 72);// cut_hour - 砍价有效期小时
Integer product_count = getParameter("product_count", 3);// product_count - 参与活动商品总数
activity_rule.put("product_count", product_count); // 参与活动的商品总数
activity_rule.put("cut_hour", cut_hour); // 砍价的有效期小时
data.put("product_count", product_count);
data.put("cut_hour", cut_hour);
} else if (ObjectUtil.equal(activity_type_id, StateCode.ACTIVITY_TYPE_GIFTBAG)) {
//a+b组合套餐
String activity_bag_category = getParameter("activity_bag_category");//礼包分类

View File

@ -33,6 +33,7 @@ import com.suisung.mall.common.modules.account.AccountUserInfo;
import com.suisung.mall.common.modules.account.AccountUserSns;
import com.suisung.mall.common.modules.base.*;
import com.suisung.mall.common.modules.distribution.ShopDistributionPlantformUser;
import com.suisung.mall.common.modules.esign.EsignContract;
import com.suisung.mall.common.modules.invoicing.InvoicingCustomerLevel;
import com.suisung.mall.common.modules.invoicing.InvoicingWarehouseBase;
import com.suisung.mall.common.modules.page.ShopPageBase;
@ -48,6 +49,7 @@ import com.suisung.mall.common.pojo.dto.StoreBizTimeInfoDTO;
import com.suisung.mall.common.service.impl.BaiduMapServiceImpl;
import com.suisung.mall.common.utils.*;
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
import com.suisung.mall.shop.activity.service.ShopActivityCutpriceService;
import com.suisung.mall.shop.base.service.AccountBaseConfigService;
import com.suisung.mall.shop.base.service.ShopBaseProductTagService;
import com.suisung.mall.shop.base.service.ShopBaseStoreCategoryService;
@ -55,6 +57,7 @@ import com.suisung.mall.shop.base.service.ShopBaseStoreGradeService;
import com.suisung.mall.shop.config.BaiduUtil;
import com.suisung.mall.shop.distribution.service.ShopDistributionPlantformUserService;
import com.suisung.mall.shop.entity.LocationBean;
import com.suisung.mall.shop.esign.service.EsignContractService;
import com.suisung.mall.shop.invoicing.service.InvoicingCustomerLevelService;
import com.suisung.mall.shop.invoicing.service.InvoicingWarehouseBaseService;
import com.suisung.mall.shop.page.service.ShopPageBaseService;
@ -139,6 +142,11 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
@Lazy
@Autowired
private ShopStoreAnalyticsService shopStoreAnalyticsService;
@Lazy
@Autowired
private ShopActivityCutpriceService shopActivityCutpriceService;
@Autowired
private AccountBaseConfigService accountBaseConfigService;
@Autowired
@ -178,6 +186,11 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
@Lazy
@Autowired
private ShopMchEntryService shopMchEntryService;
@Lazy
@Autowired
private EsignContractService esignContractService;
@Autowired
private ThreadPoolExecutor executor;
@ -1104,18 +1117,44 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
if (CheckUtil.isNotEmpty(activity_type_id)) {
queryWrapper.eq("activity_type_id", activity_type_id);
} else {
queryWrapper.in("activity_type_id", StateCode.ACTIVITY_TYPE_BARGAIN,
queryWrapper.in("activity_type_id",
StateCode.ACTIVITY_TYPE_BARGAIN,
StateCode.ACTIVITY_TYPE_GIFT,
StateCode.ACTIVITY_TYPE_LIMITED_DISCOUNT,
StateCode.ACTIVITY_TYPE_DISCOUNT_PACKAGE,
StateCode.ACTIVITY_TYPE_DIY_PACKAGE,
StateCode.ACTIVITY_TYPE_REDUCTION);
StateCode.ACTIVITY_TYPE_REDUCTION,
StateCode.ACTIVITY_TYPE_CUTPRICE);
}
// 为执行排序设置默认排序
queryWrapper.orderByDesc("activity_id");
Page<ShopStoreActivityBase> lists = shopStoreActivityBaseService.lists(queryWrapper, 1, 500);
Page<ShopStoreActivityBase> lists = shopStoreActivityBaseService.lists(queryWrapper, 1, 200);
// Optimized version with improved code quality and readability
List<ShopStoreActivityBase> 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<Boolean, String> 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<ShopStoreBaseMappe
List<Map> items = (List<Map>) data.get("items");
//经营期限
List<Integer> store_ids = items.stream().map(s -> Convert.toInt(s.get("store_id"))).collect(Collectors.toList());
List<ShopStoreInfo> store_info_rows = shopStoreInfoService.gets(store_ids);
List<Integer> 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<ShopStoreBaseMappe
if (CollUtil.isNotEmpty(subsite_ids)) {
subsite_rows = shopPlantformSubsiteService.gets(subsite_ids);
}
//店铺统计
List<ShopStoreAnalytics> store_analytics_rows = shopStoreAnalyticsService.gets(store_ids);
List<ShopPlantformSubsite> finalSubsite_rows = subsite_rows;
@ -1493,6 +1534,25 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
} else {
s.put("subsite_name", I18nUtil._("全国站"));
}
// E签宝和拉卡拉的合同文件下载地址
String lkl_contract_file_url = "";
String esign_contract_file_url = "";
Integer esign_contract_flow_status = -1;//合同签署状态-1预备数据阶段0-等待签署1-已部分签署2-已完成所有签署方完成签署;3-已撤销发起方撤销签署任务;5-已过期签署截止日到期后触发;7-已拒签签署方拒绝签署
ShopMchEntry entry = shopMchEntryService.getLklContractStatusUrl(_store_id);
if (ObjectUtil.isNotEmpty(entry)) {
lkl_contract_file_url = entry.getContract_download_url();
}
EsignContract esignContract = esignContractService.getEsignContractStatusUrl(_store_id);
if (ObjectUtil.isNotEmpty(esignContract)) {
esign_contract_file_url = esignContract.getSigned_contract_url();
esign_contract_flow_status = esignContract.getSign_flow_status();
}
s.put("lkl_contract_file_url", lkl_contract_file_url);
s.put("esign_contract_file_url", esign_contract_file_url);
s.put("esign_contract_flow_status", esign_contract_flow_status);
});
data.put("items", accountService.fixUserAvatar((List<Map>) data.get("items"), true));
@ -1526,6 +1586,24 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
}
}
// E签宝和拉卡拉的合同文件下载地址
String lkl_contract_file_url = "";
String esign_contract_file_url = "";
Integer esign_contract_flow_status = -1; //合同签署状态-1预备数据阶段0-等待签署1-已部分签署2-已完成所有签署方完成签署;3-已撤销发起方撤销签署任务;5-已过期签署截止日到期后触发;7-已拒签签署方拒绝签署
ShopMchEntry entry = shopMchEntryService.getLklContractStatusUrl(store_id);
if (ObjectUtil.isNotEmpty(entry)) {
lkl_contract_file_url = entry.getContract_download_url();
}
EsignContract esignContract = esignContractService.getEsignContractStatusUrl(store_id);
if (ObjectUtil.isNotEmpty(esignContract)) {
esign_contract_file_url = esignContract.getSigned_contract_url();
esign_contract_flow_status = esignContract.getSign_flow_status();
}
row.put("lkl_contract_file_url", lkl_contract_file_url);
row.put("esign_contract_file_url", esign_contract_file_url);
row.put("esign_contract_flow_status", esign_contract_flow_status);
// 火星坐标系GCJ02经纬度 转出 百度坐标系BD09经纬度因为数据库保存的经纬度统一是GCJ02经纬度所以需要转换
gcj02ToBd09Gps(row);
@ -4404,6 +4482,29 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
}
}
/**
* 批量更新店铺营业状态为营业中
* 将符合条件的"筹备中"状态店铺更新为"营业中"状态
*
* @return Boolean 更新是否成功
*/
@Override
public Boolean batchUpdateStoreBizStateToOpening() {
try {
UpdateWrapper<ShopStoreBase> 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<ShopStoreBase> getMobileStoreList(Integer page, Integer rows) {

View File

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

View File

@ -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<ShopStorePrinte
@Autowired
private FeieUtil feieUtil;
@Autowired
private FreeMakerUtils freeMakerUtils;
@Autowired
private ShopStorePrinterTemplateService shopStorePrinterTemplateService;
@Autowired
private ShopOrderBaseService shopOrderBaseService;
@Autowired
private ShopStorePrinterLogService shopStorePrinterLogService;
@Autowired
private ShopOrderInfoServiceImpl shopOrderInfoServiceImpl;
private ShopOrderReturnService shopOrderReturnService;
@Override
public CommonResult shopStorePrinterPageList(String keyword, Integer pageNum, Integer pageSize) {
@ -341,7 +342,7 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl<ShopStorePrinte
public Boolean printShopStoreOrder(Integer storeId, String orderId) {
logger.debug("#### 调用飞鹅打票机的打印操作开始 ####");
logger.debug("#### 店铺:{},打印订单:{} ####", storeId, orderId);
// 获取订单包含所有所需的字段参考实体类ShopStoreOrderPrintVO ShopStoreOrderProductPrintVO
// 获取订单包含所有所需的字段参考实体类ShopStoreOrderPrintVO OrderItemPrintVO
if (StrUtil.isBlank(orderId)) {
logger.error("订单为空,无法打印小票。");
@ -349,7 +350,7 @@ public class ShopStorePrinterServiceImpl extends BaseServiceImpl<ShopStorePrinte
}
// 获取打印的订单信息判断订单支付状态已支付
Map<String, Object> binding = shopOrderBaseService.getOrderPrintInfo(storeId, orderId, StateCode.ORDER_PAID_STATE_YES);
Map<String, Object> 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<ShopStorePrinte
return true;
}
/**
* 异步打印店铺退货订单的小票信息
* <p>
* 该方法通过调用飞鹅打票机接口完成指定订单的退货小票打印功能主要流程包括
* 1. 校验订单号是否合法
* 2. 获取订单及店铺相关信息
* 3. 查询对应店铺绑定的打印机列表与打印模板
* 4. 渲染打印内容并发送至所有绑定的打印机
* 5. 记录打印日志
* </p>
*
* @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<ShopStorePrinter> 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<OrderItemPrintVO> orderItems = orderPrintVO.getOrder_items();
if (CollUtil.isEmpty(orderItems)) {
logger.error("订单{}商品信息为空,无法打印小票。", orderId);
return false;
}
List<OrderItemPrintVO> 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<String> 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<Pair<String, String>> respList = feieUtil.printContentByList(printerSnList, printContent);
if (CollUtil.isEmpty(respList)) {
// 所有打印机均未能响应视为失败
logger.error("订单{}信息打印,调用飞鹅打印机打印失败。", orderId);
return false;
}
// 遍历每台打印机的响应结果记录打印日志
for (Pair<String, String> 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<ShopStorePrinter> selectPrinterList(Integer storeId) {
if (storeId == null) {

View File

@ -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<ShopSto
QueryWrapper<ShopStorePrinterTemplate> 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<ShopSto
queryWrapper.clear();
queryWrapper.eq("store_id", 0)
.eq("category", category)
.eq("status", CommonConstant.Enable)
.orderByAsc("template_id");
shopStorePrinterTemplate = findOne(queryWrapper);

View File

@ -48,9 +48,14 @@ public class ShopStoreSfOrderServiceImpl extends BaseServiceImpl<ShopStoreSfOrde
if (StrUtil.isBlank(shopOrderId)) {
return null;
}
QueryWrapper<ShopStoreSfOrder> wrapper = new QueryWrapper<>();
wrapper.eq("shop_order_id", shopOrderId);
return getOne(wrapper);
if (StrUtil.startWith(shopOrderId, "JS")) { // 顺丰同城的订单号标志
return getBySfOrderId(shopOrderId);
} else {
QueryWrapper<ShopStoreSfOrder> wrapper = new QueryWrapper<>();
wrapper.eq("shop_order_id", shopOrderId);
return getOne(wrapper);
}
}
/**

View File

@ -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<ShopStoreOrderProductPrintVO> productList, int titleLen, int numLen, int amountLen) {
public String genProductStr(List<OrderItemPrintVO> productList, int titleLen, int numLen, int amountLen) {
String resultStr = "";
resultStr += "<L>商品名称 数量 金额</L><BR>";
resultStr += "--------------------------------<BR>";

View File

@ -819,8 +819,8 @@
<!--总计优惠金额 order_discount_amount + order_voucher_price + order_points_fee + order_adjust_fee-->
(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 @@
</choose>
</if>
<!-- 2-物流配送订单 -->
<!-- 2-非同城配送 物流配送订单 -->
<if test="delivery != null and delivery == 2">
AND oi.delivery_type_id IN (1,2,3,4,10)
AND oi.delivery_type_id IN (1,2,3,4,5,10,15)
<!-- 订单状态筛选 -->
<choose>

View File

@ -86,4 +86,97 @@
</if>
</where>
</select>
<resultMap id="ReturnOrderPrintInfoResult" type="com.suisung.mall.common.pojo.vo.OrderPrintVO">
<!--订单对象映射-->
<result property="order_id" column="order_id"/>
<result property="return_id" column="return_id"/>
<result property="store_id" column="store_id"/>
<result property="store_name" column="store_name"/>
<result property="store_tel" column="store_tel"/>
<result property="order_pickup_num_str" column="order_pickup_num_str"/>
<result property="order_message" column="order_message"/>
<result property="seller_message" column="seller_message"/>
<result property="payment_time" column="payment_time"/>
<result property="order_items_count" column="order_items_count"/>
<result property="order_product_amount" column="order_product_amount"/>
<result property="order_shipping_fee" column="order_shipping_fee"/>
<result property="order_channel_name" column="order_channel_name"/>
<result property="payment_type_name" column="payment_type_name"/>
<result property="delivery_type_id" column="delivery_type_id"/>
<result property="delivery_type_name" column="delivery_type_name"/>
<result property="packing_fee" column="packing_fee"/>
<result property="basic_rights" column="basic_rights"/>
<result property="order_payment_amount" column="order_payment_amount"/>
<result property="return_refund_amount" column="return_refund_amount"/>
<result property="order_state" column="order_state"/>
<result property="return_buyer_message" column="return_buyer_message"/>
<result property="return_store_message" column="return_store_message"/>
<result property="return_add_time" column="return_add_time"/>
<result property="return_finish_time" column="return_finish_time"/>
<result property="return_flag_str" column="return_flag_str"/>
<result property="return_state" column="return_state"/>
<result property="buyer_user_name" column="buyer_user_name"/>
<result property="return_tel" column="return_tel"/>
<result property="da_mobile" column="da_mobile"/>
<result property="da_province" column="da_province"/>
<result property="da_city" column="da_city"/>
<result property="da_address" column="da_address"/>
<result property="cashier" column="cashier"/>
<!-- 集合类型映射 -->
<collection property="order_items" ofType="com.suisung.mall.common.pojo.vo.OrderItemPrintVO">
<result property="item_name" column="item_name"/>
<result property="order_item_quantity" column="order_item_quantity"/>
<result property="order_item_amount" column="order_item_amount"/>
<result property="s_name" column="s_name"/>
<result property="s_quantity" column="s_quantity"/>
<result property="s_amount" column="s_amount"/>
<result property="product_sn" column="product_sn"/>
</collection>
</resultMap>
<select id="fetchReturnOrderPrintInfo" resultMap="ReturnOrderPrintInfoResult">
select
sob.order_id,sob.store_id,sob.store_name,sob.buyer_user_id,
sob.buyer_user_name,sob.order_time,sob.order_payment_amount,sob.order_product_amount,
soi.order_title, soi.delivery_type_id, soi.payment_type_id, soi.payment_time,
LPAD(soi.order_pickup_num, 7, '0') as order_pickup_num_str,
CASE WHEN soi.booking_state = 2 THEN 2 ELSE 1 END AS booking_state, soi.booking_begin_time,
sod.order_message, sod.order_shipping_fee, sod.order_shipping_fee_amount, sod.delivery_time, sod.delivery_type_id,
(sod.order_discount_amount + sod.voucher_price + sod.order_points_fee + sod.order_adjust_fee) as total_discount_amount,
sod.packing_fee,
ssi.store_tel,
soda.da_province,soda.da_city,soda.da_address,soda.da_mobile, soda.order_id,
sor.return_id, sor.return_buyer_message, sor.return_store_message, sor.return_add_time, sor.return_finish_time,
CASE WHEN sor.return_flag != 1 THEN '仅退款' ELSE '退款退货' END AS return_flag_str,
sor.return_state_id, sor.return_refund_amount,
sor.return_tel,
soi_item.item_barcode as product_sn, soi_item.item_name,
sor_item.return_item_subtotal as order_item_amount, sor_item.return_item_num as order_item_quantity
from shop_order_base sob
INNER JOIN shop_order_info soi on sob.order_id=soi.order_id
INNER JOIN shop_order_data sod on sob.order_id=sod.order_id
INNER JOIN shop_store_base ssb on sob.store_id=ssb.store_id
INNER JOIN shop_store_info ssi on sob.store_id=ssi.store_id
INNER JOIN shop_order_delivery_address soda on sob.order_id=soda.order_id
INNER JOIN shop_order_item soi_item on soi_item.order_id=sob.order_id
INNER JOIN shop_order_return sor on sob.order_id=sor.order_id
INNER JOIN shop_order_return_item sor_item on sor_item.order_id=sob.order_id AND sor_item.return_id=sor.return_id
AND sor_item.order_item_id=soi_item.order_item_id
<where>
<if test="orderId!=null and orderId != ''">
and sob.order_id=#{orderId}
</if>
<if test="returnId!=null and returnId != ''">
and sor.return_id=#{returnId}
</if>
</where>
</select>
</mapper>

View File

@ -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'
<![CDATA[ AND lp.product_short_name <> '' ]]>
<![CDATA[ AND lp.sname <> '' ]]>
)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'
<![CDATA[ AND lp.product_short_name <> '' ]]>
<![CDATA[ AND lp.sname <> '' ]]>
)t
)temp
where

View File

@ -51,6 +51,48 @@
收银员:李小璐<BR>
格式化带变量模版:
<#if is_booking_order><CB><B>【预约订单】<BR></B></CB>
</#if><CB>${store_name}</CB><BR>
--------------------------------<BR>
<CB>#${order_pickup_num_str}</CB><BR>
<L>买家备注:${order_message!'-'}</L><BR>
<BOLD>配送时间:${payment_time?string('MM-dd HH:mm')}${delivery_time?string('HH:mm')}</BOLD><BR>
--------------------------------<BR>
订单编号:${order_id}<BR>
订单来源:微信小程序<BR>
支付方式:微信支付<BR>
配送来源:顺丰同城<BR>
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>
--------------------------------<BR>
<L>商品名称 数量 金额</L><BR>
--------------------------------<BR>
<#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR>
<#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR>
</#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR>
</#if><BR>
</#list>--------------------------------<BR>
商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>
商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>
运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR>
<#if packing_fee?? && (packing_fee > 0)>打包费:<BOLD>¥${packing_fee?string('0.00')}</BOLD><BR>
</#if>优惠金额:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>
实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR>
<#if seller_message?default("")?trim?length gt 1>
--------------------------------<BR>
<BOLD>商家备注:${seller_message!'-'}</BOLD><BR>
</#if>
--------------------------------<BR>
<BOLD>收货人:${buyer_user_name!''}</BOLD><BR>
<BOLD>收货人手机:${da_mobile!'-'}</BOLD><BR>
<BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>
--------------------------------<BR>
门店:${store_name}<BR>
门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>
收银员:${cashier!'店长'}<BR>
格式化的示例:
<CB>小发同城</CB><BR>--------------------------------<BR><CB>#00019232</CB><BR><L>买家备注:不用敲门,放在门口旁边的外卖箱,打个电话告知送达就行,谢谢!!!</L><BR><BOLD>配送时间2024-10-25 14:00-14:30</BOLD><BR>--------------------------------<BR>订单编号ES20231026111444527685<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间2024-10-25 14:00:23<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><L>可口可乐CocaC</L> <L><BOLD> x110 </BOLD></L><L> 8100.45</L><BR><L>ola经典美味汽水1.2</L><BR><L>5L/瓶</L><BR><BOLD>6970448170051</BOLD><BR><L>排骨约350g默 </L><L><BOLD> 1 </BOLD></L><L> 150.13 </L><BR><L>认砍小块)</L><BR><BOLD>6970448170053</BOLD><BR><L>新鲜虫草花1包约2 </L><L><BOLD> x11 </BOLD></L><L> 4.01 </L><BR><L>00g 韭菜1000g 鸡蛋</L><BR><L>2003克</L><BR><BOLD>6970448170054</BOLD><BR><L>冰红茶风味饮料 <L><BOLD> 1 </BOLD></L><L> 13.24 </L></L><BR><BOLD>6970448170055</BOLD><BR>--------------------------------<BR>商品总件数:<BOLD>3</BOLD><BR>商品总额:<BOLD>¥18.7</BOLD><BR>押金:<BOLD>¥500</BOLD><BR>运费:<BOLD>¥5.54</BOLD><BR>会员权益:<BOLD>-¥50</BOLD><BR>秒杀:<BOLD>-¥100</BOLD><BR>实付金额:<BOLD>¥428.9元</BOLD><BR>--------------------------------<BR><BOLD>商家备注:老顾客赠送一箱牛奶;玻璃瓶包装轻拿轻放!</BOLD><BR>--------------------------------<BR><BOLD>收货人:张三</BOLD><BR><BOLD>收货人手机13128778765</BOLD><BR><BOLD>收货地址北京市朝阳区朝阳路朝阳人民小区1号楼1栋1101</BOLD><BR>--------------------------------<BR>门店:岛内价生活超市<BR>门店电话:<BOLD>13665822542</BOLD><BR>收银员:李小璐<BR>

View File

@ -37,8 +37,45 @@
--------------------------------<BR>
操作员:李小明<BR>
格式化模版:
############################################
修改后的模版
<CB><B>用户退款订单</B></CB><BR>
<CB>${store_name}</CB><BR>
--------------------------------<BR>
<CB>#${order_pickup_num_str}</CB><BR>
<L>退款原因:${return_buyer_message!'-'}</L><BR>
<BOLD>配送时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}20-30分钟</BOLD><BR>
--------------------------------<BR>
订单编号:${order_id}<BR>
退单编号:${return_id}<BR>
订单来源:${order_channel_name!'微信小程序'}<BR>
支付方式:${payment_type_name!'微信支付'}<BR>
配送来源:${deliver_type_name!'顺丰同城'}<BR>
付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>
申请退款:${return_add_time?string('yyyy-MM-dd HH:mm:ss')}<BR>
确认退款:${return_finish_time?string('yyyy-MM-dd HH:mm:ss')}<BR>
--------------------------------<BR>
<C>******* 退款商品 ******</C><BR>
--------------------------------<BR>
<#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>
--------------------------------<BR>
实付金额:<BOLD>¥${order_payment_amount?string('0.00')}元</BOLD><BR>
配送费:<BOLD>¥${order_shipping_fee?string('0.00')}元</BOLD><BR>
申请退款:<BOLD>¥${return_refund_amount?string('0.00')}</BOLD><BR>
退款方式:<BOLD>${return_flag_str}</BOLD><BR>
商家审批备注:<BOLD>${return_store_message!'-'}</BOLD><BR>
--------------------------------<BR>
<BOLD>会员名称:${buyer_user_name!'微信用户'}</BOLD><BR>
<BOLD>会员手机:${return_tel!'-'}</BOLD><BR>
--------------------------------<BR>
操作员:${cashier!'-'}<BR>
原模版:
<CB>${store_name}</CB><BR>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>买家备注:${order_message!'-'}</L><BR><BOLD>配送时间:${delivery_time}</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>订单来源:微信小程序<BR>支付方式:微信支付<BR>配送来源:顺丰同城<BR>付款时间:${payment_time}<BR>--------------------------------<BR><L>商品名称 数量 金额</L><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>商品总件数:<BOLD>${order_items_count!0}</BOLD><BR>商品总额:<BOLD>¥${order_product_amount?string('0.00')}</BOLD><BR>押金:<BOLD>¥${(yajin!0)?string('0.00')}</BOLD><BR>运费:<BOLD>¥${order_shipping_fee?string('0.00')}</BOLD><BR>会员权益:<BOLD>-¥${(quanyi!0)?string('0.00')}</BOLD><BR>秒杀:<BOLD>-¥${(miaosha!0)?string('0.00')}</BOLD><BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}</BOLD><BR><#if seller_message?default("")?trim?length gt 1>--------------------------------<BR><BOLD>商家备注:${seller_message!'---'}</BOLD><BR></#if>--------------------------------<BR><BOLD>收货人:${buyer_user_name!''}</BOLD><BR><BOLD>收货人手机:${store_tel!''}</BOLD><BR><BOLD>收货地址:${da_province!'-'}${da_city!'-'}${da_address!'-'}</BOLD><BR>--------------------------------<BR>门店:${store_name}<BR>门店电话:<BOLD>${store_tel!'-'}</BOLD><BR>收银员:${cashier!'-'}<BR>
带参模版:
<CB><B>用户退款订单</B></CB><BR><CB>${store_name}</CB><BR>--------------------------------<BR><CB>#${order_pickup_num_str}</CB><BR><L>退款原因:${return_buyer_message!'-'}</L><BR><BOLD>配送时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}20-30分钟</BOLD><BR>--------------------------------<BR>订单编号:${order_id}<BR>退单编号:${return_id}<BR>订单来源:${order_channel_name!'微信小程序'}<BR>支付方式:${payment_type_name!'微信支付'}<BR>配送来源:${deliver_type_name!'顺丰同城'}<BR>付款时间:${payment_time?string('yyyy-MM-dd HH:mm:ss')}<BR>申请退款:${return_add_time?string('yyyy-MM-dd HH:mm:ss')}<BR>确认退款:${return_finish_time?string('yyyy-MM-dd HH:mm:ss')}<BR>--------------------------------<BR><C>******* 退款商品 ******</C><BR>--------------------------------<BR><#list order_items as item><L>${item.s_name}</L><L><BOLD>${item.s_quantity}</BOLD></L><L>${item.s_amount}</L><BR><#if item.s_name_segs??><#list item.s_name_segs as seg><L>${seg}</L><BR></#list></#if><BOLD><#if item.product_sn?default("")?trim?length gt 1>${item.product_sn}</BOLD><BR></#if></#list>--------------------------------<BR>实付金额:<BOLD>¥${order_payment_amount?string('0.00')}元</BOLD><BR>配送费:<BOLD>¥${order_shipping_fee?string('0.00')}元</BOLD><BR>申请退款:<BOLD>¥${return_refund_amount?string('0.00')}</BOLD><BR>退款方式:<BOLD>${return_flag_str}</BOLD><BR>商家审批备注:<BOLD>${return_store_message!'-'}</BOLD><BR>--------------------------------<BR><BOLD>会员名称:${buyer_user_name}</BOLD><BR><BOLD>会员手机:${return_tel}</BOLD><BR>--------------------------------<BR>操作员:${cashier!'-'}<BR>

View File

@ -185,7 +185,7 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl<SnsStoryBaseMapper,
}
if (snsStoryBase.getStory_enable() != null) {
queryWrapper.eq("story_enable", snsStoryBase.getStory_enable() );
queryWrapper.eq("story_enable", snsStoryBase.getStory_enable());
}
if (CheckUtil.isNotEmpty(snsStoryBase.getStory_index())) {
@ -201,7 +201,7 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl<SnsStoryBaseMapper,
}
@Override
public Map getStoryList(StorySearchDTO storySearchDTO) {
public Map getStoryList(StorySearchDTO storySearchDTO) {
QueryWrapper<SnsStoryBase> column_row = new QueryWrapper<>(); //查询获得相关的SnsStoryBase列表集合
if (storySearchDTO.getStory_type() != null) {
@ -266,44 +266,44 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl<SnsStoryBaseMapper,
}
UserDto user = ContextUtil.getCurrentUser();
if(user!=null){
if (user != null) {
//用户是否点赞
QueryWrapper<SnsStoryLike> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("user_id",user.getId());
QueryWrapper<SnsStoryLike> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", user.getId());
List<SnsStoryLike> snsStoryLikes = snsStoryLikeService.find(queryWrapper);
List<Integer> 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<SnsStoryCollection> snsStoryCollectionQueryWrapper=new QueryWrapper<>();
snsStoryCollectionQueryWrapper.eq("user_id",user.getId());
QueryWrapper<SnsStoryCollection> snsStoryCollectionQueryWrapper = new QueryWrapper<>();
snsStoryCollectionQueryWrapper.eq("user_id", user.getId());
List<SnsStoryCollection> snsStoryCollections = snsStoryCollectionService.find(snsStoryCollectionQueryWrapper);
List<Integer> 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<SnsUserFriend> friendQueryWrapper=new QueryWrapper<>();
friendQueryWrapper.eq("user_id",user.getId()).eq("friend_id",Convert.toInt(item.get("user_id")));
for (Map item : items) {
QueryWrapper<SnsUserFriend> 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<SnsStoryBaseMapper,
if (story_id == null) {
accountUserSns.setUser_story(accountUserSns.getUser_story() + 1);
if (!accountService.saveOrUpdateAccountUserSns(accountUserSns)) {
log.error(I18nUtil._("更新用户发帖数量失败bean[{}]"), accountUserSns.toString());
log.error(I18nUtil._("更新用户发帖数量失败bean[{}]"), accountUserSns);
throw new ApiException(I18nUtil._("更新用户发帖数量失败!"));
}
}
@ -406,28 +406,44 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl<SnsStoryBaseMapper,
//帖子类型不是视频类型
@Override
public Map getStory(Integer story_id) {
public Map getStory(Integer story_id) {
UserDto user = ContextUtil.getCurrentUser();
Integer user_id = 0;
Integer user_id = null;
if (user == null) {
throw new ApiException(ResultCode.NEED_LOGIN);
} else {
// 允许未登录用户访问
if (user != null) {
user_id = user.getId();
}
// 参数校验
if (story_id == null || story_id <= 0) {
throw new ApiException("无效的故事ID");
}
SnsStoryBase storyBase = get(story_id);
// 检查故事是否存在
if (storyBase == null) {
throw new ApiException("故事不存在");
}
Map row = Convert.toMap(String.class, Object.class, storyBase);
row = accountService.fixUserAvatar(row, true);
// 检查是否包含富文本
String story_content = Convert.toStr(row.get("story_content"));
if (StrUtil.isNotBlank(story_content)) {
Document document = Jsoup.parse(story_content);
String text = document.text();
// 如果不相等就是富文本
row.put("has_rich", story_content.length() != text.length());
try {
Document document = Jsoup.parse(story_content);
String text = document.text();
// 如果不相等就是富文本
row.put("has_rich", story_content.length() != text.length());
} catch (Exception e) {
// 解析失败时默认不是富文本
row.put("has_rich", false);
}
} else {
row.put("has_rich", false);
}
row.put("product_item_name", "");
@ -437,54 +453,108 @@ public class SnsStoryBaseServiceImpl extends BaseServiceImpl<SnsStoryBaseMapper,
String str_item_id = storyBase.getItem_id(); //获得帖子商品的item_id值
if (StrUtil.isNotBlank(str_item_id)) {
List<Long> item_ids = Convert.toList(Long.class, str_item_id);
Map product_row = shopService.getProductItemOne(item_ids.get(0));
try {
List<Long> 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<SnsStoryLike> 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<SnsStoryLike> 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<SnsStoryAdvertisement> advertisementQueryWrapper = new QueryWrapper<>();
advertisementQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 1);
row.put("adv_top", snsStoryAdvertisementService.findOne(advertisementQueryWrapper));
/*QueryWrapper<SnsStoryAdvertisement> advertisementQueryWrapper = new QueryWrapper<>();
advertisementQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 1);
row.put("adv_top", snsStoryAdvertisementService.findOne(advertisementQueryWrapper));
QueryWrapper<SnsStoryAdvertisement> advListQueryWrapper = new QueryWrapper<>();
advListQueryWrapper.eq("adv_enable", 1).eq("adv_is_top", 0);
row.put("adv_lists", snsStoryAdvertisementService.find(advListQueryWrapper));*/
QueryWrapper<SnsStoryAdvertisement> 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<SnsStoryBase> 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<SnsStoryBase> 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<SnsStoryBase> 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<SnsUserFriend> 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<SnsUserFriend> 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;
}

View File

@ -189,99 +189,170 @@ public class SnsStoryCategoryServiceImpl extends BaseServiceImpl<SnsStoryCategor
@Override
public Map listComment() {
Integer user_id = null;
UserDto user = ContextUtil.getCurrentUser();
if (user == null) {
throw new ApiUserException(I18nUtil._("用户信息异常!"));
if (user != null) {
user_id = user.getId();
}
Integer user_id = user.getId();
Integer page = getParameter("page", 1); //当前页码
Integer rows = getParameter("rows", 20);//每页记录条数
QueryWrapper<SnsStoryComment> 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<Map>) data.get("items"), false));
//读取评论回复
List<Map> items = (List<Map>) data.get("items");
if (CollUtil.isEmpty(items)) {
return new HashMap();
return data;
}
List<Integer> comment_id_row = items.stream().map(s -> Convert.toInt(s.get("comment_id"))).collect(Collectors.toList());
// 避免转换中的空值问题
List<Integer> 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<SnsStoryCommentHelpful> 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<SnsStoryCommentHelpful> snsStoryCommentHelpfuls = snsStoryCommentHelpfulService.find(queryWrapper);
List<SnsStoryCommentHelpful> snsStoryCommentHelpfuls = new ArrayList<>();
try {
snsStoryCommentHelpfuls = snsStoryCommentHelpfulService.find(queryWrapper);
} catch (Exception e) {
// 忽略查询异常
}
List<Map> comment_helpful_rows = Convert.toList(Map.class, snsStoryCommentHelpfuls);
if (comment_helpful_rows == null) {
comment_helpful_rows = new ArrayList<>();
}
QueryWrapper<SnsStoryCommentReply> snsStoryCommentReplyQueryWrapper = new QueryWrapper<>();
snsStoryCommentReplyQueryWrapper.in("comment_id", comment_id_row);
snsStoryCommentReplyQueryWrapper.eq("comment_reply_show_flag", 1);
snsStoryCommentReplyQueryWrapper.orderByDesc("comment_reply_time");
List<SnsStoryCommentReply> snsStoryCommentReplies = snsStoryCommentReplyService.find(snsStoryCommentReplyQueryWrapper);
List<Map> comment_reply_rows = Convert.toList(Map.class, snsStoryCommentReplies);
List<SnsStoryCommentReply> snsStoryCommentReplies = new ArrayList<>();
try {
snsStoryCommentReplies = snsStoryCommentReplyService.find(snsStoryCommentReplyQueryWrapper);
} catch (Exception e) {
// 忽略查询异常
}
List<Map> 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<Integer> comment_reply_id_row = comment_reply_rows.stream().map(s -> Convert
.toInt(s.get("comment_reply_id"))).collect(Collectors.toList());
List<Integer> 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<SnsStoryCommentReplyHelpful> 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<SnsStoryCommentReplyHelpful> 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<SnsStoryCommentReplyHelpful> storyCommentHelpfuls = new ArrayList<>();
try {
storyCommentHelpfuls = snsStoryCommentReplyHelpfulService.find(helpfulQueryWrapper);
} catch (Exception e) {
// 忽略查询异常
}
List<SnsStoryCommentReplyHelpful> storyCommentHelpfuls = snsStoryCommentReplyHelpfulService.find(helpfulQueryWrapper);
List<Map> comment_reply_helpful_rows = Convert.toList(Map.class, storyCommentHelpfuls);
List<Map> 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<SnsStoryCommentReply> wrapper1 = new QueryWrapper<>();
wrapper1.in("comment_id", comment_id_row);
wrapper1.eq("user_id", user_id);
List<SnsStoryCommentReply> 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 BaseServiceImpl<SnsStoryCategor
//评论回复是否点赞
for (Map comment_reply_row : commentLists) {
if (ObjectUtil.isNotNull(comment_reply_helpful_tmp_rows.get(comment_reply_row.get("comment_reply_id")))) {
comment_reply_row.put("IsFabulous", 1);
Integer reply_id = Convert.toInt(comment_reply_row.get("comment_reply_id"));
if (reply_id != null && ObjectUtil.isNotNull(comment_reply_helpful_tmp_rows.get(reply_id))) {
// 检查具体的点赞记录而不仅仅是容器
List helpfulList = (List) comment_reply_helpful_tmp_rows.get(reply_id);
if (CollUtil.isNotEmpty(helpfulList)) {
comment_reply_row.put("IsFabulous", 1);
} else {
comment_reply_row.put("IsFabulous", 0);
}
} else {
comment_reply_row.put("IsFabulous", 0);
}
}
//是否点赞
if (ObjectUtil.isNotNull(comment_reply_tmp_rows.get(item.get("comment_id")))) {
item.put("IsFabulous", 1);
} else {
item.put("IsFabulous", 0);
}
//是否点赞
if (ObjectUtil.isNotNull(comment_helpful_tmp_rows.get(item.get("comment_id")))) {
//是否点赞 - 修正重复判断逻辑
if (ObjectUtil.isNotNull(comment_helpful_tmp_rows.get(comment_id))) {
item.put("IsFabulous", 1);
} else {
item.put("IsFabulous", 0);
@ -317,6 +388,7 @@ public class SnsStoryCategoryServiceImpl extends BaseServiceImpl<SnsStoryCategor
return data;
}
@Override
public Map story() {
Integer user_id = Convert.toInt(getParameter("user_id"));
@ -401,7 +473,7 @@ public class SnsStoryCategoryServiceImpl extends BaseServiceImpl<SnsStoryCategor
AccountUserInfo user_info = accountService.getUserInfo(user_id);
Integer user_certification = user_info.getUser_certification();
if (true || user_certification == 1) {
if (true) {
/*
AccountUserType accountUserType = accountService.getAccountUserType(user_info.getUser_type_id());

View File

@ -581,7 +581,6 @@
<pushImage>true</pushImage>
<!--推送镜像仓库校验安全证书,无安全证书无法推送-->
<dockerCertPath>${docker.ca}</dockerCertPath>
<!--定义基础镜像-->
<!-- <baseImage>java:8</baseImage>-->
<baseImage>openjdk:8-jre</baseImage>
@ -600,11 +599,6 @@
<entryPoint>["sh", "-c", "mkdir -p /tmp /app/temp /root/nacos/naming/public &amp;&amp; chmod -R 777 /tmp /app/temp /root/nacos &amp;&amp; java -Djava.io.tmpdir=/app/temp -Dnacos.naming.cache.dir=/root/nacos/naming -jar -Xms256m -Xmx512m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:+UseContainerSupport -XX:MaxRAMPercentage=60.0 -XX:+UseSerialGC -XX:MinHeapFreeRatio=40 -XX:MaxHeapFreeRatio=60 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:./gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=10M -Dspring.profiles.active=${spring.profile} -Duser.timezone=Asia/Shanghai /${project.build.finalName}.jar"]
</entryPoint>
<!-- 添加额外的Dockerfile指令来清理不必要的文件 -->
<!-- <runs>-->
<!-- <run>rm -rf /root/.m2 &amp;&amp; rm -rf /tmp/* &amp;&amp; rm -rf /var/cache/*</run>-->
<!-- </runs>-->
<resources>
<resource>
<targetPath>/</targetPath>