diff --git a/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java b/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java index 0867d7a4..2a9078ad 100644 --- a/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java +++ b/mall-admin/src/main/java/com/suisung/mall/admin/oss/service/impl/OssServiceImpl.java @@ -30,6 +30,7 @@ import com.suisung.mall.common.pojo.dto.OssCallbackResultDTO; import com.suisung.mall.common.pojo.dto.OssPolicyResultDTO; import com.suisung.mall.common.utils.I18nUtil; import com.suisung.mall.common.utils.LogUtil; +import com.suisung.mall.common.utils.UploadUtil; import com.suisung.mall.common.utils.VideoUtil; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; @@ -169,6 +170,9 @@ public class OssServiceImpl implements OssService { * @return */ public Map upload(MultipartFile file, UserDto user, String dir, String uploadPath, String uploadName, String fileName) { + // 如果是图片,先压缩图片,再上传到 cos 对象存储 + file = UploadUtil.compressImageIfNeeded(file, 2048); + // 创建临时文件 creTempFile(file, dir, uploadName); String url = null; @@ -248,7 +252,7 @@ public class OssServiceImpl implements OssService { tempFile.delete(); logger.info("临时文件已删除: {}", uploadPath); } - + // 同时删除可能创建的封面文件 String coverPath = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg"); File coverFile = new File(coverPath); diff --git a/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java b/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java index 3e7a22e6..ec853b84 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java +++ b/mall-common/src/main/java/com/suisung/mall/common/api/StateCode.java @@ -17,6 +17,7 @@ public class StateCode { 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 Map DELIVERY_TYPE_MAP = new HashMap() { { @@ -450,6 +451,11 @@ public class StateCode { public static final int SF_ORDER_STATUS_EXCEPTION = 91; public static final int SF_ORDER_STATUS_CANCELING = 31; + // 退款状态:0-是无退款;1-是部分退款;2-是全部退款 + public static final int ORDER_REFUND_STATUS_NO = 0; + public static final int ORDER_REFUND_STATUS_PART = 1; + public static final int ORDER_REFUND_STATUS_ALL = 2; + static { DELIVERY_TIME_NOT_TIMER.put(1, I18nUtil._("不限时段")); DELIVERY_TIME_NOT_TIMER.put(15, I18nUtil._("上午")); diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java index 10666a79..2d95fff0 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchOrderInfoDTO.java @@ -42,6 +42,9 @@ public class MchOrderInfoDTO implements Serializable { // 物流轨迹信息(物流,已发货的才有数据) @ApiModelProperty(value = "物流轨迹信息") Map logistics_traces; + + // 退款订单详情 + MchReturnOrderDetailDTO return_order_detail; // 订单信息 @ApiModelProperty(value = "订单编号") @@ -95,16 +98,12 @@ public class MchOrderInfoDTO implements Serializable { private Integer is_deny_return; @ApiModelProperty(value = "异常订单操作流程,0-可操作自行发货,1-可操作订单完成,2-订单完成不可操作") private String operate_flag; - @ApiModelProperty(value = "订单配送预约状态:1-立即配送;2-预约配送") private Integer booking_state; - @ApiModelProperty(value = "预约送达起始时间,格式如:yyyy-MM-dd HH:mm:ss") private Date booking_begin_time; - @ApiModelProperty(value = "预约送达截止时间,格式如:yyyy-MM-dd HH:mm:ss") private Date booking_end_time; - @ApiModelProperty(value = "预订单到达时间戳(秒)") private Long booking_at; } diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderDetailDTO.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderDetailDTO.java new file mode 100644 index 00000000..93a3c5b6 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderDetailDTO.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.modules.order.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@ApiModel(value = "退款单详情主表实体类 对象", description = "商家版退款单详情主表实体类") +public class MchReturnOrderDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "退货单编号") + private String return_id; + + @ApiModelProperty(value = "订单编号") + private String order_id; + + @ApiModelProperty(value = "买家用户ID") + private Integer buyer_user_id; + + @ApiModelProperty(value = "买家店铺ID") + private Integer buyer_store_id; + + @ApiModelProperty(value = "退货原因ID") + private Integer return_reason_id; + + @ApiModelProperty(value = "退货原因名称") + private String return_reason_name; + + @ApiModelProperty(value = "买家退货留言") + private String return_buyer_message; + + @ApiModelProperty(value = "店铺ID") + private Integer store_id; + + @ApiModelProperty(value = "退货退款金额") + private BigDecimal return_refund_amount; + + @ApiModelProperty(value = "退货佣金费用") + private BigDecimal return_commision_fee; + + @ApiModelProperty(value = "退货状态ID") + private Integer return_state_id; + + @ApiModelProperty(value = "退货状态名称") + private String return_state_name; + + @ApiModelProperty(value = "退货联系电话") + private String return_tel; + + @ApiModelProperty(value = "退货年份") + private Integer return_year; + + @ApiModelProperty(value = "退货月份") + private Integer return_month; + + @ApiModelProperty(value = "退货日期") + private Integer return_day; + + @ApiModelProperty(value = "退货地址") + private String return_addr; + + @ApiModelProperty(value = "退货联系手机") + private Long return_mobile; + + @ApiModelProperty(value = "退货联系电话") + private String return_telephone; + + @ApiModelProperty(value = "退货联系人姓名") + private String return_contact_name; + + @ApiModelProperty(value = "分站ID") + private Integer subsite_id; + + @ApiModelProperty(value = "退货申请时间") + private Date return_add_time; + + @ApiModelProperty(value = "退货完成时间") + private Date return_finish_time; + + @ApiModelProperty(value = "是否已退款(ENUM): 0-未退款; 1-已退款") + private Integer return_is_paid; + + @ApiModelProperty(value = "退款渠道代码") + private String return_channel_code; + + @ApiModelProperty(value = "退款渠道交易ID") + private String return_channel_trans_id; + + @ApiModelProperty(value = "退款渠道处理时间") + private Date return_channel_time; + + @ApiModelProperty(value = "退款渠道状态(ENUM): 0-待退; 1-已退; 2-异常") + private Integer return_channel_flag; + + @ApiModelProperty(value = "支付交易号") + private String deposit_trade_no; + + @ApiModelProperty(value = "支付渠道ID") + private Integer payment_channel_id; + + @ApiModelProperty(value = "商家处理留言") + private String return_store_message; + + @ApiModelProperty(value = "平台退货状态ID") + private Integer plantform_return_state_id; + + @ApiModelProperty(value = "平台退货状态名称") + private String plantform_return_state_name; + + @ApiModelProperty(value = "退货类型(ENUM): 0-不用退货; 1-需要退货") + private Integer return_flag; + + @ApiModelProperty(value = "申请类型(ENUM): 1-退款申请; 2-退货申请; 3-虚拟退款") + private Integer return_type; + + @ApiModelProperty(value = "提交的退货退款金额") + private BigDecimal submit_return_refund_amount; + + @ApiModelProperty(value = "订单商品ID列表") + private List order_item_ids; + + @ApiModelProperty(value = "买家用户名") + private String buyer_user_name; + + @ApiModelProperty(value = "是否有合同类型") + private Boolean contract_type_ids; + + @ApiModelProperty(value = "尝试退款次数") + private Integer try_return_count; + + // 收货地址相关信息 + @ApiModelProperty(value = "收货省份") + private String da_province; + + @ApiModelProperty(value = "收货城市") + private String da_city; + + @ApiModelProperty(value = "收货区县") + private String da_county; + + @ApiModelProperty(value = "收货详细地址") + private String da_address; + + @ApiModelProperty(value = "收货人姓名") + private String da_name; + + @ApiModelProperty(value = "收货人手机号") + private String da_mobile; + + @ApiModelProperty(value = "收货人电话") + private String da_telephone; + + @ApiModelProperty(value = "收货省份ID") + private Integer da_province_id; + + @ApiModelProperty(value = "收货城市ID") + private Integer da_city_id; + + @ApiModelProperty(value = "收货区县ID") + private Integer da_county_id; + + @ApiModelProperty(value = "纬度") + private String da_latitude; + + @ApiModelProperty(value = "经度") + private String da_longitude; + + // 退款商品列表 + @ApiModelProperty(value = "退货商品详情列表") + private List items; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderItemDetailDTO.java b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderItemDetailDTO.java new file mode 100644 index 00000000..996c8885 --- /dev/null +++ b/mall-common/src/main/java/com/suisung/mall/common/modules/order/dto/MchReturnOrderItemDetailDTO.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit. + * Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan. + * Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna. + * Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus. + * Vestibulum commodo. Ut rhoncus gravida arcu. + */ + +package com.suisung.mall.common.modules.order.dto; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@ApiModel(value = "退款单商品详情实体对象", description = "退款单商品详情实体类") +public class MchReturnOrderItemDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @ApiModelProperty(value = "退货商品项ID") + private Long order_return_item_id; + + @ApiModelProperty(value = "退货单编号") + private String return_id; + + @ApiModelProperty(value = "订单商品项ID") + private Long order_item_id; + + @ApiModelProperty(value = "订单编号") + private String order_id; + + @ApiModelProperty(value = "退货商品数量") + private Integer return_item_num; + + @ApiModelProperty(value = "退货商品小计金额") + private BigDecimal return_item_subtotal; + + @ApiModelProperty(value = "退货原因ID") + private Integer return_reason_id; + + @ApiModelProperty(value = "退货商品备注") + private String return_item_note; + + @ApiModelProperty(value = "退货商品图片") + private String return_item_image; + + @ApiModelProperty(value = "退货商品状态ID") + private Integer return_state_id; + + @ApiModelProperty(value = "退货商品佣金费用") + private BigDecimal return_item_commision_fee; + + @ApiModelProperty(value = "退货商品商家备注") + private String return_item_store_remark; + + // 关联的订单商品信息 + @ApiModelProperty(value = "商品名称") + private String item_name; + + @ApiModelProperty(value = "产品名称") + private String product_name; + + @ApiModelProperty(value = "货品ID") + private Long item_id; + + @ApiModelProperty(value = "订单商品图片") + private String order_item_image; + + @ApiModelProperty(value = "单位ID") + private Integer unit_id; + + @ApiModelProperty(value = "单位名称") + private String unit_name; + + @ApiModelProperty(value = "订单商品单价") + private BigDecimal order_item_unit_price; + + @ApiModelProperty(value = "订单商品数量") + private Integer order_item_quantity; + + @ApiModelProperty(value = "订单商品总金额") + private BigDecimal order_item_amount; + + @ApiModelProperty(value = "订单商品总重量") + private BigDecimal order_item_weight_total; + + @ApiModelProperty(value = "订单商品备注") + private String order_item_note; + + @ApiModelProperty(value = "商品编号") + private String item_number; + + @ApiModelProperty(value = "产品编号") + private String product_number; +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java index c7386608..be215f5f 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java +++ b/mall-common/src/main/java/com/suisung/mall/common/pojo/dto/LklSeparateWithTotalAmountDTO.java @@ -33,8 +33,10 @@ import java.math.RoundingMode; @Slf4j public class LklSeparateWithTotalAmountDTO { - // 常量定义 + // 商家最低分账比例阈值 20% private static final BigDecimal MCH_RATIO_THRESHOLD = BigDecimal.valueOf(0.2); + // 默认平台比例 1% + private static final BigDecimal DEFAULT_PLAT_RATIO = BigDecimal.valueOf(0.01); // 基础金额属性 private Integer totalSeparateAmount; // 分账总金额(分) @@ -237,25 +239,21 @@ public class LklSeparateWithTotalAmountDTO { private Pair validateInputs() { // 校验totalSeparateAmount必须为有效值且大于0 if (totalSeparateAmount == null || totalSeparateAmount <= 0) { - log.error("分账计算:总分账金额小于等于0"); - return Pair.of(false, "分账计算:总分账金额小于等于0"); + return Pair.of(false, "分账计算:总分账金额必须大于0"); } // 校验必要参数 if (lklRatio == null || lklRatio.compareTo(BigDecimal.ZERO) <= 0) { - log.error("分账计算:拉卡拉分账比例小于等于0"); - return Pair.of(false, "分账计算:拉卡拉分账比例小于等于0"); + return Pair.of(false, "分账计算:拉卡拉分账比例必须大于0"); } if (mchRatio == null || mchRatio.compareTo(BigDecimal.ZERO) <= 0) { - log.error("分账计算:商户分账比例小于等于0"); - return Pair.of(false, "分账计算:商户分账比例小于等于0"); + return Pair.of(false, "分账计算:商户分账比例必须大于0"); } // 校验shippingFee不能大于等于totalSeparateAmount - if (shippingFee != null && shippingFee >= totalSeparateAmount) { - log.error("分账计算:分账总金额低于平台内部运费"); - return Pair.of(false, "分账计算:分账总金额低于平台内部运费"); + if (shippingFee != null && shippingFee > 0 && shippingFee >= totalSeparateAmount) { + return Pair.of(false, "分账计算:配送费用不能大于等于总金额"); } return Pair.of(true, ""); @@ -276,9 +274,6 @@ public class LklSeparateWithTotalAmountDTO { // 校验拉卡拉分账金额不能为负数 if (lklAmount < 0) { - // 记录详细日志 - String errorMsg = "拉卡拉分账金额计算结果为负数: " + lklAmount; - log.error(errorMsg); return Pair.of(false, "拉卡拉分账金额计算异常"); } @@ -287,9 +282,6 @@ public class LklSeparateWithTotalAmountDTO { // 校验可分账金额不能为负数 if (canSeparateAmount < 0) { - // 记录详细日志 - String errorMsg = "可分账金额计算结果为负数: " + canSeparateAmount; - log.error(errorMsg); return Pair.of(false, "可分账金额计算异常"); } @@ -316,9 +308,6 @@ public class LklSeparateWithTotalAmountDTO { // 校验实际可分账金额不能为负数 if (actualCanSeparateAmountB < 0) { - // 记录详细日志 - String errorMsg = "实际可分账金额计算结果为负数: " + actualCanSeparateAmountB; - log.error(errorMsg); return Pair.of(false, "实际可分账金额计算异常"); } @@ -333,7 +322,7 @@ public class LklSeparateWithTotalAmountDTO { private Pair calculateDefaultRatios() { // 如果平台比例无效,设置默认值0.01 if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) { - platRatio = new BigDecimal("0.01"); + platRatio = DEFAULT_PLAT_RATIO; } return Pair.of(true, ""); @@ -356,9 +345,7 @@ public class LklSeparateWithTotalAmountDTO { // 2. 校验实际可分账金额不能小于0 if (actualCanSeparateAmount < 0) { - String errorMsg = "实际可分账金额不能为负数"; - log.error(errorMsg); - return Pair.of(false, errorMsg); + return Pair.of(false, "实际可分账金额不能小于0"); } // 3. 初始化剩余金额为实际可分账金额 @@ -371,9 +358,7 @@ public class LklSeparateWithTotalAmountDTO { // 校验平台分账金额不能为负数 if (platAmount < 0) { - String errorMsg = "平台分账金额计算异常"; - log.error(errorMsg); - return Pair.of(false, errorMsg); + return Pair.of(false, "平台分账金额不能小于0"); } // 确保不超过剩余金额 @@ -398,9 +383,7 @@ public class LklSeparateWithTotalAmountDTO { // 校验二级代理商分账金额不能为负数 if (agent2ndAmount < 0) { - String errorMsg = "二级代理商分账金额计算异常"; - log.error(errorMsg); - return Pair.of(false, errorMsg); + return Pair.of(false, "二级代理商分账金额不能小于0"); } // 确保不超过剩余金额 @@ -418,9 +401,7 @@ public class LklSeparateWithTotalAmountDTO { // 校验一级代理商分账金额不能为负数 if (agent1stAmount < 0) { - String errorMsg = "一级代理商分账金额计算异常"; - log.error(errorMsg); - return Pair.of(false, errorMsg); + return Pair.of(false, "一级代理商分账金额不能小于0"); } // 确保不超过剩余金额 @@ -435,9 +416,7 @@ public class LklSeparateWithTotalAmountDTO { // 校验商家分账金额不能为负数 if (mchAmount < 0) { - String errorMsg = "商家分账金额计算异常"; - log.error(errorMsg); - return Pair.of(false, errorMsg); + return Pair.of(false, "商家分账金额不能小于0"); } return Pair.of(true, ""); @@ -450,16 +429,17 @@ public class LklSeparateWithTotalAmountDTO { * @return Pair Boolean表示是否成功,String为错误信息 */ private Pair calculateActualMchRatio() { - if (totalSeparateAmount != null && totalSeparateAmount > 0 && mchAmount != null) { + if (totalSeparateAmount != null && totalSeparateAmount > 0 && mchAmount != null && mchAmount >= 0) { // 计算实际比例,保留6位小数 mchRatio = BigDecimal.valueOf(mchAmount) .divide(BigDecimal.valueOf(totalSeparateAmount), 6, RoundingMode.HALF_UP); // 如果计算出的实际比例低于阈值,打印日志并返回错误 if (mchRatio.compareTo(MCH_RATIO_THRESHOLD) < 0) { - String errorMsg = "警告: 商家实际分账比例低于阈值,当前比例: " + mchRatio + ",阈值: " + MCH_RATIO_THRESHOLD; + String errorMsg = String.format("警告: 商家实际分账比例低于阈值,当前比例: %s,阈值: %s", + mchRatio.toPlainString(), MCH_RATIO_THRESHOLD.toPlainString()); log.warn(errorMsg); - return Pair.of(false, "商家分账低于阈值"); + return Pair.of(false, errorMsg); } } @@ -472,23 +452,28 @@ public class LklSeparateWithTotalAmountDTO { * @return Pair Boolean表示是否成功,String为错误信息 */ private Pair validateSeparateAmountTotal() { - long totalAmount = 0; - totalAmount += (lklAmount != null ? lklAmount : 0); - totalAmount += (platAmount != null ? platAmount : 0); - totalAmount += (agent2ndAmount != null ? agent2ndAmount : 0); - totalAmount += (agent1stAmount != null ? agent1stAmount : 0); - totalAmount += (mchAmount != null ? mchAmount : 0); + long totalAmount = getValueOrZero(lklAmount) + + getValueOrZero(platAmount) + + getValueOrZero(agent2ndAmount) + + getValueOrZero(agent1stAmount) + + getValueOrZero(mchAmount); - if (totalAmount > totalSeparateAmount) { - // 记录详细日志 - String errorMsg = "分账金额总和超过总金额,分账金额总和: " + totalAmount + ",总金额: " + totalSeparateAmount; - log.error(errorMsg); - return Pair.of(false, "分账金额总和超过总金额"); + if (totalAmount > getValueOrZero(totalSeparateAmount)) { + String errorMsg = String.format("分账金额总和(%d)超过总金额(%d)", + totalAmount, getValueOrZero(totalSeparateAmount)); + return Pair.of(false, errorMsg); } return Pair.of(true, ""); } + /** + * 安全获取Integer值,避免空指针 + */ + private int getValueOrZero(Integer value) { + return value != null ? value : 0; + } + /** * 生成对象的字符串表示 * @@ -520,22 +505,42 @@ public class LklSeparateWithTotalAmountDTO { * @return JSON格式字符串 */ public String toJSON() { - String sb = "{" + "\"totalSeparateAmount\":" + totalSeparateAmount + "," + - "\"canSeparateAmount\":" + canSeparateAmount + "," + - "\"refCanSeparateAmount\":" + refCanSeparateAmount + "," + - "\"shippingFee\":" + shippingFee + "," + - "\"lklRatio\":" + (lklRatio != null ? "\"" + lklRatio + "\"" : "null") + "," + - "\"mchRatio\":" + (mchRatio != null ? "\"" + mchRatio + "\"" : "null") + "," + - "\"platRatio\":" + (platRatio != null ? "\"" + platRatio + "\"" : "null") + "," + - "\"agent1stRatio\":" + (agent1stRatio != null ? "\"" + agent1stRatio + "\"" : "null") + "," + - "\"agent2ndRatio\":" + (agent2ndRatio != null ? "\"" + agent2ndRatio + "\"" : "null") + "," + - "\"lklAmount\":" + lklAmount + "," + - "\"mchAmount\":" + mchAmount + "," + - "\"platAmount\":" + platAmount + "," + - "\"agent1stAmount\":" + agent1stAmount + "," + - "\"agent2ndAmount\":" + agent2ndAmount + - "}"; - return sb; + StringBuilder sb = new StringBuilder("{"); + appendJsonField(sb, "totalSeparateAmount", totalSeparateAmount); + appendJsonField(sb, "canSeparateAmount", canSeparateAmount); + appendJsonField(sb, "refCanSeparateAmount", refCanSeparateAmount); + appendJsonField(sb, "shippingFee", shippingFee); + appendJsonField(sb, "lklRatio", lklRatio); + appendJsonField(sb, "mchRatio", mchRatio); + appendJsonField(sb, "platRatio", platRatio); + appendJsonField(sb, "agent1stRatio", agent1stRatio); + appendJsonField(sb, "agent2ndRatio", agent2ndRatio); + appendJsonField(sb, "lklAmount", lklAmount); + appendJsonField(sb, "mchAmount", mchAmount); + appendJsonField(sb, "platAmount", platAmount); + appendJsonField(sb, "agent1stAmount", agent1stAmount); + appendJsonField(sb, "agent2ndAmount", agent2ndAmount); + // 移除最后的逗号并添加结束括号 + if (sb.charAt(sb.length() - 1) == ',') { + sb.setLength(sb.length() - 1); + } + sb.append("}"); + return sb.toString(); + } + + /** + * 添加JSON字段的辅助方法 + */ + private void appendJsonField(StringBuilder sb, String fieldName, Object fieldValue) { + sb.append("\"").append(fieldName).append("\":"); + if (fieldValue instanceof BigDecimal) { + sb.append("\"").append(fieldValue).append("\""); + } else if (fieldValue == null) { + sb.append("null"); + } else { + sb.append(fieldValue); + } + sb.append(","); } /** @@ -544,11 +549,7 @@ public class LklSeparateWithTotalAmountDTO { * @return SeparateResult 包含成功状态、分账结果和错误信息的包装类 */ public SeparateResult getSeparateResult() { - SeparateResult result = new SeparateResult(); - result.setIsSuccess(true); - result.setData(this); - result.setErrMsg(this.errMsg); - return result; + return new SeparateResult(Boolean.TRUE, this, this.errMsg); } /** @@ -610,5 +611,4 @@ public class LklSeparateWithTotalAmountDTO { return new SeparateResult(Boolean.TRUE, data, errMsg != null ? errMsg : ""); } } - -} \ No newline at end of file +} diff --git a/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java b/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java index 5673d391..15693f67 100644 --- a/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java +++ b/mall-common/src/main/java/com/suisung/mall/common/utils/UploadUtil.java @@ -6,8 +6,11 @@ import org.apache.tika.Tika; import org.springframework.util.Base64Utils; import org.springframework.web.multipart.MultipartFile; +import javax.imageio.ImageIO; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.awt.*; +import java.awt.image.BufferedImage; import java.io.*; import java.net.*; import java.nio.file.Files; @@ -437,4 +440,250 @@ public class UploadUtil { } + /** + * 压缩图片文件以提高质量。 + * 如果文件大小超过500KB,则在保持宽高比的同时调整大小 + * 以提高清晰度并减小文件大小。 + * + * @param file 原始图片文件 + * @param pixelLimit 像素限制 + * @return 压缩后的图片文件,如果不需要压缩则返回原始文件 + */ + public static MultipartFile compressImageIfNeeded(MultipartFile file, Integer pixelLimit) { + if (file == null) { + return null; + } + + try { + // 检查文件是否为图片格式 + String contentType = file.getContentType(); + if (contentType == null || !contentType.startsWith("image/")) { + log.debug("文件不是图片格式,跳过压缩: {}", contentType); + return file; + } + + if (pixelLimit == null) { + pixelLimit = 2048; + } + + // 小文件直接返回 + if (file.getSize() <= 512000) { // 500KB + log.debug("文件大小在限制范围内 ({} 字节),无需压缩", file.getSize()); + return file; + } + + // 将MultipartFile转换为BufferedImage + BufferedImage originalImage = ImageIO.read(file.getInputStream()); + if (originalImage == null) { + log.warn("读取图片文件失败,跳过压缩"); + return file; + } + + int originalWidth = originalImage.getWidth(); + int originalHeight = originalImage.getHeight(); + + // 小图片直接返回 + if (originalWidth <= pixelLimit && originalHeight <= pixelLimit) { + log.debug("图片尺寸在限制范围内 ({}x{}),无需调整大小", originalWidth, originalHeight); + return file; + } + + // 计算保持宽高比的新尺寸 + double aspectRatio = (double) originalWidth / originalHeight; + int newWidth, newHeight; + + // 目标尺寸:最长边最大为pixelLimit像素 + if (originalWidth > originalHeight) { + newWidth = Math.min(pixelLimit, originalWidth); + newHeight = (int) (newWidth / aspectRatio); + } else { + newHeight = Math.min(pixelLimit, originalHeight); + newWidth = (int) (newHeight * aspectRatio); + } + + // 确保最小尺寸 + newWidth = Math.max(newWidth, 1); + newHeight = Math.max(newHeight, 1); + + log.debug("调整图片尺寸从 {}x{} 到 {}x{}", originalWidth, originalHeight, newWidth, newHeight); + + // 获取图片格式并执行高质量渐进式缩放 + String format = getImageFormat(file.getOriginalFilename()); + BufferedImage resizedImage = progressiveResize(originalImage, newWidth, newHeight, format); + + // 转换回MultipartFile + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(resizedImage, format, baos); + byte[] compressedBytes = baos.toByteArray(); + + log.info("图片从 {} 字节压缩到 {} 字节", file.getSize(), compressedBytes.length); + + return new InMemoryMultipartFile( + file.getName(), + file.getOriginalFilename(), + file.getContentType(), + compressedBytes + ); + + } catch (Exception e) { + log.error("压缩图片时出错,使用原始文件: {}", e.getMessage(), e); + return file; + } + } + + /** + * 渐进式调整图片大小以提高质量 + * + * @param originalImage 原始图片 + * @param targetWidth 目标宽度 + * @param targetHeight 目标高度 + * @param format 图片格式 + * @return 调整大小后的图片,质量更好 + */ + private static BufferedImage progressiveResize(BufferedImage originalImage, int targetWidth, int targetHeight, String format) { + int currentWidth = originalImage.getWidth(); + int currentHeight = originalImage.getHeight(); + + // 多步缩小以提高质量 + BufferedImage resizedImage = originalImage; + + while (currentWidth > targetWidth * 2 || currentHeight > targetHeight * 2) { + currentWidth = Math.max(targetWidth, currentWidth / 2); + currentHeight = Math.max(targetHeight, currentHeight / 2); + + resizedImage = scaleImage(resizedImage, currentWidth, currentHeight, format); + } + + // 最终调整到目标尺寸 + return scaleImage(resizedImage, targetWidth, targetHeight, format); + } + + /** + * 使用高质量渲染提示缩放图片 + * + * @param originalImage 原始图片 + * @param targetWidth 目标宽度 + * @param targetHeight 目标高度 + * @param format 图片格式 + * @return 缩放后的图片 + */ + private static BufferedImage scaleImage(BufferedImage originalImage, int targetWidth, int targetHeight, String format) { + int imageType = getImageTypeForFormat(format); + BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, imageType); + Graphics2D g2d = scaledImage.createGraphics(); + + // 高质量渲染设置 + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); + g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); + + g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null); + g2d.dispose(); + + return scaledImage; + } + + /** + * 根据文件扩展名获取图片格式,支持更多格式 + * + * @param filename 原始文件名 + * @return 图片格式 (jpg, png, gif 等) + */ + private static String getImageFormat(String filename) { + if (StrUtil.isBlank(filename)) { + return "jpg"; + } + + String extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); + switch (extension) { + case "png": + return "png"; + case "gif": + return "gif"; + case "bmp": + return "bmp"; + case "wbmp": + return "wbmp"; + case "jpeg": + case "jpg": + return "jpeg"; + default: + return "jpeg"; + } + } + + /** + * 根据格式确定适当的图片类型以保留透明度 + * + * @param format 图片格式 + * @return BufferedImage类型 + */ + private static int getImageTypeForFormat(String format) { + if ("png".equalsIgnoreCase(format) || "gif".equalsIgnoreCase(format)) { + return BufferedImage.TYPE_INT_ARGB; // 保留透明度 + } + return BufferedImage.TYPE_INT_RGB; // JPEG等格式的默认值 + } + + + /** + * Simple MultipartFile implementation for in-memory data + */ + private static class InMemoryMultipartFile implements MultipartFile { + private final String name; + private final String originalFilename; + private final String contentType; + private final byte[] content; + + public InMemoryMultipartFile(String name, String originalFilename, String contentType, byte[] content) { + this.name = name; + this.originalFilename = originalFilename; + this.contentType = contentType; + this.content = content; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalFilename() { + return originalFilename; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public boolean isEmpty() { + return content == null || content.length == 0; + } + + @Override + public long getSize() { + return content.length; + } + + @Override + public byte[] getBytes() throws IOException { + return content; + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(content); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + new FileOutputStream(dest).write(content); + } + } } diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java index 5a24d642..ba64148c 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/activity/service/impl/ShopActivityCutpriceServiceImpl.java @@ -330,13 +330,17 @@ public class ShopActivityCutpriceServiceImpl extends BaseServiceImpl queryWrapper = new QueryWrapper<>(); queryWrapper.eq("order_id", orderId); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java index c9a6337a..c934f2d2 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/lakala/service/impl/LklTkServiceImpl.java @@ -136,85 +136,124 @@ public class LklTkServiceImpl { * 身份证反面:ID_CARD_BEHIND; * 营业执照:BUSINESS_LICENCE; * 银行卡:BANK_CARD - * @return + * @return 上传结果及OCR识别信息 */ public CommonResult uploadOcrImg(MultipartFile file, String imgType) { - UserDto currentUser = getCurrentUser(); - if (currentUser == null) { - currentUser = new UserDto(); - } - + // 参数校验 if (file == null || StrUtil.isBlank(imgType)) { + logger.warn("上传文件参数缺失: imgType={}", imgType); return CommonResult.failed("上传文件或图片类型不能为空"); } - CommonResult ossImgInfo = ossService.uploadFile(file, currentUser); - if (ossImgInfo == null) { - return CommonResult.failed("上传文件失败"); - } - - if (ossImgInfo.getStatus() != ResultCode.SUCCESS.getStatus() || ossImgInfo.getData() == null) { - return CommonResult.failed(ossImgInfo.getMsg()); - } - - String imgURL = JSONUtil.parseObj(ossImgInfo.getData()).getStr("url"); - - String authorization = getLklTkAuthorization(); - if (StrUtil.isBlank(authorization)) { - return CommonResult.failed("获取拉卡拉token失败"); - } - - JSONObject header = new JSONObject(); - header.put("Authorization", authorization); - - - String fileBase64 = UploadUtil.multipartFileToBase64(file); - if (StrUtil.isBlank(fileBase64)) { - return CommonResult.failed("解析文件转换失败"); - } - // Base64Utils.encodeToString(file.getBytes()); - - JSONObject requestBody = new JSONObject(); - requestBody.put("fileBase64", fileBase64); - requestBody.put("imgType", imgType); - requestBody.put("sourcechnl", "0"); // 来源: 0:PC,1:安卓,2:IOS - requestBody.put("isOcr", "true"); - - String urlPath = "/sit/htkregistration/file/base/upload"; - if (isLklProd) { - // 生产环境启用 - urlPath = "/registration/file/base/upload"; - } - try { - ResponseEntity updResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, requestBody, JSONObject.class); - if (ObjectUtil.isEmpty(updResponse) - || updResponse.getStatusCode() != HttpStatus.OK - || ObjectUtil.isEmpty(updResponse.getBody())) { + UserDto currentUser = getCurrentUser(); + if (currentUser == null) { + currentUser = new UserDto(); + } + + // Compress image if needed before processing + MultipartFile processedFile = UploadUtil.compressImageIfNeeded(file, 2048); + if (processedFile == null) { + logger.warn("上传文件压缩失败: filename={}, imgType={}", + file.getOriginalFilename(), imgType); + return CommonResult.failed("服务繁忙,请重试!"); + } + + // 上传文件到OSS + CommonResult ossImgInfo = ossService.uploadFile(processedFile, currentUser); + if (ossImgInfo == null) { + logger.error("上传文件到OSS失败,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); + return CommonResult.failed("上传文件失败"); + } + + if (ossImgInfo.getStatus() != ResultCode.SUCCESS.getStatus() || ossImgInfo.getData() == null) { + logger.error("OSS上传响应异常,filename={}, imgType={}, status={}", + processedFile.getOriginalFilename(), imgType, ossImgInfo.getStatus()); + return CommonResult.failed(ossImgInfo.getMsg()); + } + + String imgURL = JSONUtil.parseObj(ossImgInfo.getData()).getStr("url"); + logger.debug("文件上传OSS成功,filename={}", processedFile.getOriginalFilename()); + + // 获取拉卡拉认证信息 + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + logger.error("获取拉卡拉token失败,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); + return CommonResult.failed("获取拉卡拉token失败"); + } + + // 构造请求头 + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + // 文件转Base64 + String fileBase64 = UploadUtil.multipartFileToBase64(processedFile); + if (StrUtil.isBlank(fileBase64)) { + logger.error("文件转换Base64失败,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); + return CommonResult.failed("解析文件转换失败"); + } + + // 构造请求体 + JSONObject requestBody = new JSONObject(); + requestBody.put("fileBase64", fileBase64); + requestBody.put("imgType", imgType); + requestBody.put("sourcechnl", "0"); // 来源: 0:PC,1:安卓,2:IOS + requestBody.put("isOcr", "true"); + + // 构造请求路径 + String urlPath = "/sit/htkregistration/file/base/upload"; + if (isLklProd) { + urlPath = "/registration/file/base/upload"; + } + + // 发送请求并获取响应 + ResponseEntity updResponse = RestTemplateHttpUtil.sendPostBodyBackEntity( + buildLklTkUrl(urlPath), header, requestBody, JSONObject.class); + + logger.info("调用拉卡拉文件上传接口,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); + + // 检查响应有效性 + if (ObjectUtil.isEmpty(updResponse) || + updResponse.getStatusCode() != HttpStatus.OK || + ObjectUtil.isEmpty(updResponse.getBody())) { + logger.error("拉卡拉文件上传响应数据异常,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); return CommonResult.failed("上传文件返回值有误"); } - // {batchNo,status,url,showUrl,result{} } + // 提取上传结果 JSONObject updObj = updResponse.getBody(); String batchNo = updObj.getStr("batchNo"); if (StrUtil.isBlank(batchNo)) { + logger.error("拉卡拉文件上传返回批次号为空,filename={}, imgType={}", + processedFile.getOriginalFilename(), imgType); return CommonResult.failed("上传文件返回值有误"); } updObj.put("cosURL", imgURL); + logger.info("拉卡拉文件上传成功,filename={}, imgType={}, batchNo={}", + processedFile.getOriginalFilename(), imgType, batchNo); return CommonResult.success(updObj); + } catch (Exception e) { - logger.error("上传文件失败: ", e.getMessage()); + logger.error("上传文件过程发生异常,filename={}, imgType={}", + file != null ? file.getOriginalFilename() : "unknown", imgType, e); return CommonResult.failed("上传文件失败:" + e.getMessage()); } } + /** * 根据上传的图片的批次号,获取 OCR 识别结果 * - * @param batchNo - * @param imgType * ID_CARD_FRONT 身份证正⾯ + * @param batchNo 批次号 + * @param imgType 图片类型 + * * ID_CARD_FRONT 身份证正⾯ * * ID_CARD_BEHIND 身份证反⾯ * * BUSINESS_LICENCE 营业执照照⽚ * * BANK_CARD 银行卡(企业对公不需要传) @@ -227,55 +266,70 @@ public class LklTkServiceImpl { * * SETTLE_ID_CARD_FRONT 结算人身份证人像面 * * SETTLE_ID_CARD_BEHIND 结算人身份证国徽面 * * LETTER_OF_AUTHORIZATION 法人授权涵 - * @return + * @return OCR识别结果 */ public CommonResult imgOcrResult(String batchNo, String imgType) { + // 参数校验 if (StrUtil.isBlank(batchNo) || StrUtil.isBlank(imgType)) { + logger.warn("OCR识别参数缺失: batchNo={}, imgType={}", batchNo, imgType); return CommonResult.failed("批次号或图片类型不能为空"); } - // 调用 OCR 识别接口 - String authorization = getLklTkAuthorization(); - if (StrUtil.isBlank(authorization)) { - return CommonResult.failed("获取拉卡拉token失败"); - } - - JSONObject header = new JSONObject(); - header.put("Authorization", authorization); - - - JSONObject ocrRequestBody = new JSONObject(); - ocrRequestBody.put("batchNo", batchNo); - ocrRequestBody.put("imgType", imgType); - logger.info("ocr请求参数:{}", ocrRequestBody); - - String urlPath = "/sit/htkregistration/ocr/result"; - if (isLklProd) { - // 生产环境启用 - urlPath = "/registration/ocr/result"; - } - try { - ResponseEntity ocrResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, ocrRequestBody, JSONObject.class); - if (ObjectUtil.isEmpty(ocrResponse) - || ocrResponse.getStatusCode() != HttpStatus.OK - || ObjectUtil.isEmpty(ocrResponse.getBody())) { + // 获取认证信息 + String authorization = getLklTkAuthorization(); + if (StrUtil.isBlank(authorization)) { + logger.error("获取拉卡拉token失败,batchNo={}, imgType={}", batchNo, imgType); + return CommonResult.failed("获取拉卡拉token失败"); + } + + // 构造请求头 + JSONObject header = new JSONObject(); + header.put("Authorization", authorization); + + // 构造请求体 + JSONObject ocrRequestBody = new JSONObject(); + ocrRequestBody.put("batchNo", batchNo); + ocrRequestBody.put("imgType", imgType); + logger.info("调用OCR识别接口,batchNo={}, imgType={}", batchNo, imgType); + + // 构造请求路径 + String urlPath = "/sit/htkregistration/ocr/result"; + if (isLklProd) { + urlPath = "/registration/ocr/result"; + } + + // 发送请求并获取响应 + ResponseEntity ocrResponse = RestTemplateHttpUtil.sendPostBodyBackEntity( + buildLklTkUrl(urlPath), header, ocrRequestBody, JSONObject.class); + + // 检查响应有效性 + if (ObjectUtil.isEmpty(ocrResponse) || + ocrResponse.getStatusCode() != HttpStatus.OK || + ObjectUtil.isEmpty(ocrResponse.getBody())) { + logger.error("OCR识别响应数据异常,batchNo={}, imgType={}", batchNo, imgType); return CommonResult.failed("OCR响应数据有误"); } - JSONObject ocrObj = ocrResponse.getBody().get("result", JSONObject.class); - logger.info("ocr返回结果:{}", ocrResponse); + // 提取OCR结果 + JSONObject result = ocrResponse.getBody(); + JSONObject ocrObj = result.get("result", JSONObject.class); + logger.info("OCR识别成功,batchNo={}, imgType={}", batchNo, imgType); + if (ObjectUtil.isEmpty(ocrObj)) { + logger.warn("OCR识别返回结果为空,batchNo={}, imgType={}", batchNo, imgType); return CommonResult.failed("OCR返回结果有误"); } return CommonResult.success(ocrObj); + } catch (Exception e) { - logger.error("OCR识别失败: ", e.getMessage()); + logger.error("OCR识别过程发生异常,batchNo={}, imgType={}", batchNo, imgType, e); return CommonResult.failed("OCR识别失败:" + e.getMessage()); } } + /** * (商户进件前)请求获取token * diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java index 94f5b898..6e7ea33f 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderBaseServiceImpl.java @@ -385,8 +385,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl jumpPathOrderIds = new ArrayList<>(); // 处理符合条件的订单 for (ShopOrderBase order_row : order_rows) { // 只处理符合条件的订单 @@ -5076,7 +5080,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl receive_id_row.contains(order.getOrder_id())) @@ -5176,10 +5186,39 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl orderIds) { + if (CollectionUtils.isEmpty(orderIds)) { + return; + } + + CompletableFuture.runAsync(() -> { + for (String orderId : orderIds) { + try { + // 控制API调用频率 + Thread.sleep(300); // 根据实际API限制调整 + wxOrderShippingService.setMsgJumpPath(orderId); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + log.warn("[异步处理] 线程中断,订单ID: {}", orderId); + break; + } catch (Exception e) { + log.warn("[异步处理] 消息跳转路径设置失败,订单ID: {}, 错误: {}", orderId, e.getMessage()); + // 继续处理下一个订单 + } + } + }, executor); + } + /** * 订单确认收货(定时任务用途) @@ -7834,8 +7873,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl s.getOrder_item_commission_fee()).reduce(BigDecimal::add).get(); diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java index 830ef748..d1b6066e 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/order/service/impl/ShopOrderReturnServiceImpl.java @@ -1063,6 +1063,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl s.getOrder_item_id().equals(orderReturnItem.getOrder_item_id())).findFirst().orElse(null); @@ -1150,45 +1151,91 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl(); + } Map return_row = Convert.toMap(String.class, Object.class, shopOrderReturn); + // 2. 获取退货单商品明细 QueryWrapper itemQueryWrapper = new QueryWrapper<>(); itemQueryWrapper.eq("return_id", return_id); List shopOrderReturnItems = orderReturnItemService.find(itemQueryWrapper); List return_item_rows = Convert.toList(Map.class, shopOrderReturnItems); - List order_item_ids = shopOrderReturnItems.stream().map(s -> s.getOrder_item_id()).distinct().collect(Collectors.toList()); + List order_item_ids = shopOrderReturnItems.stream() + .map(ShopOrderReturnItem::getOrder_item_id) + .distinct() + .collect(Collectors.toList()); + // 3. 获取原订单详情 String order_id = shopOrderReturn.getOrder_id(); Map order_row = shopOrderBaseService.getOrderDetail(order_id, null); + if (order_row == null) { + logger.warn("原订单详情不存在: order_id={}", order_id); + return return_row; + } + + // 4. 获取退货原因列表 List return_reason_rows = orderReturnReasonService.find(new QueryWrapper<>()); + // 5. 获取订单收货地址信息 ShopOrderDeliveryAddress deliveryAddress = orderDeliveryAddressService.get(order_id); - Map order_delivery = Convert.toMap(String.class, Object.class, deliveryAddress); - return_row.putAll(order_delivery); + if (deliveryAddress != null) { + Map order_delivery = Convert.toMap(String.class, Object.class, deliveryAddress); + return_row.putAll(order_delivery); + } + // 6. 获取订单商品相关信息 List order_row_items = (List) order_row.get("items"); + if (CollUtil.isEmpty(order_row_items)) { + logger.warn("订单商品列表为空: order_id={}", order_id); + return_row.put("items", new ArrayList<>()); + return return_row; + } - List item_ids = order_row_items.stream().map(s -> Convert.toLong(s.get("item_id"))).distinct().collect(Collectors.toList()); - List product_items = shopProductItemService.gets(item_ids); + List item_ids = order_row_items.stream() + .map(s -> Convert.toLong(s.get("item_id"))) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); - List product_ids = order_row_items.stream().map(s -> Convert.toInt(s.get("product_id"))).distinct().collect(Collectors.toList()); - List product_infos = shopProductInfoService.gets(product_ids); + List product_items = CollUtil.isEmpty(item_ids) ? new ArrayList<>() : + shopProductItemService.gets(item_ids); - List product_indexs = shopProductIndexService.gets(product_ids); + List product_ids = order_row_items.stream() + .map(s -> Convert.toInt(s.get("product_id"))) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); - // 退货商品数据 - Map return_item_temp_rows = new HashMap(); + List product_infos = CollUtil.isEmpty(product_ids) ? new ArrayList<>() : + shopProductInfoService.gets(product_ids); + + List product_indexs = CollUtil.isEmpty(product_ids) ? new ArrayList<>() : + shopProductIndexService.gets(product_ids); + + // 7. 构建退货商品临时数据映射 + Map return_item_temp_rows = new HashMap<>(); for (Map item : order_row_items) { Long order_item_id = Convert.toLong(item.get("order_item_id")); + // 只处理当前退货单相关的订单商品 if (order_item_ids.contains(order_item_id)) { Map order_item = new HashMap(); Long item_id = Convert.toLong(item.get("item_id")); Integer product_id = Convert.toInt(item.get("product_id")); + // 复制订单商品基本信息 order_item.put("item_name", item.get("item_name")); order_item.put("product_name", item.get("product_name")); order_item.put("item_id", item_id); @@ -1201,55 +1248,113 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl productItemOpl = product_items.stream().filter(s -> ObjectUtil.equal(item_id, Convert.toInt(s.get("item_id")))).findFirst(); - Map productItem = productItemOpl.orElseGet(HashMap::new); - order_item.put("item_number", productItem.get("item_number")); + // 关联SKU信息 + if (CollUtil.isNotEmpty(product_items)) { + Optional productItemOpl = product_items.stream() + .filter(s -> ObjectUtil.equal(item_id, Convert.toLong(s.get("item_id")))) + .findFirst(); + Map productItem = productItemOpl.orElse(new HashMap<>()); + order_item.put("item_number", productItem.get("item_number")); + } - Optional productInfoOpl = product_infos.stream().filter(s -> ObjectUtil.equal(product_id, s.getProduct_id())).findFirst(); - ShopProductInfo productInfo = productInfoOpl.orElseGet(ShopProductInfo::new); - order_item.put("product_number", productInfo.getProduct_number()); + // 关联商品信息 + if (CollUtil.isNotEmpty(product_infos)) { + Optional productInfoOpl = product_infos.stream() + .filter(s -> ObjectUtil.equal(product_id, s.getProduct_id())) + .findFirst(); + ShopProductInfo productInfo = productInfoOpl.orElse(new ShopProductInfo()); + order_item.put("product_number", productInfo.getProduct_number()); + } return_item_temp_rows.put(order_item_id, order_item); } } + // 8. 合并退货商品详细信息 for (Map return_item_row : return_item_rows) { Long order_item_id = Convert.toLong(return_item_row.get("order_item_id")); - Map return_item_temp_row = (Map) return_item_temp_rows.get(order_item_id); - return_item_row.putAll(ObjectUtil.defaultIfNull(return_item_temp_row, new HashMap())); + Map return_item_temp_row = return_item_temp_rows.get(order_item_id); + // 合并订单商品信息到退货商品中 + if (return_item_temp_row != null) { + return_item_row.putAll(return_item_temp_row); + } + + // 处理退货图片信息 String return_item_image = (String) return_item_row.get("return_item_image"); if (StrUtil.isNotBlank(return_item_image)) { - return_item_row.put("return_item_image", Convert.toList(String.class, return_item_image)); + // 如果是逗号分隔的图片字符串,转换为列表 + if (return_item_image.contains(",")) { + return_item_row.put("return_item_image", + Arrays.asList(return_item_image.split(","))); + } else { + return_item_row.put("return_item_image", + Collections.singletonList(return_item_image)); + } } else { return_item_row.put("return_item_image", new ArrayList<>()); } } + // 9. 处理合同类型信息 if (CollUtil.isNotEmpty(product_indexs)) { - return_row.put("contract_type_ids", StrUtil.isNotEmpty(product_indexs.get(0).getContract_type_ids())); + ShopProductIndex firstIndex = product_indexs.get(0); + if (firstIndex != null && StrUtil.isNotEmpty(firstIndex.getContract_type_ids())) { + return_row.put("contract_type_ids", true); + } else { + return_row.put("contract_type_ids", false); + } + } else { + return_row.put("contract_type_ids", false); } + // 10. 添加买家信息 return_row.put("buyer_user_name", order_row.get("buyer_user_name")); - BigDecimal return_item_subtotal = shopOrderReturnItems.stream().map(s -> s.getReturn_item_subtotal()).reduce(BigDecimal.ZERO, BigDecimal::add); + // 11. 计算退货总金额 + BigDecimal return_item_subtotal = shopOrderReturnItems.stream() + .map(ShopOrderReturnItem::getReturn_item_subtotal) + .filter(Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); return_row.put("submit_return_refund_amount", return_item_subtotal); return_row.put("order_item_ids", order_item_ids); + // 12. 处理退货原因名称 Integer return_reason_id = Convert.toInt(return_row.get("return_reason_id")); - Optional reasonOpl = return_reason_rows.stream().filter(s -> ObjectUtil.equal(return_reason_id, s.getReturn_reason_id())).findFirst(); - ShopOrderReturnReason returnReason = reasonOpl.orElseGet(ShopOrderReturnReason::new); - return_row.put("return_reason_name", returnReason.getReturn_reason_name()); + if (return_reason_id != null && CollUtil.isNotEmpty(return_reason_rows)) { + Optional reasonOpl = return_reason_rows.stream() + .filter(s -> ObjectUtil.equal(return_reason_id, s.getReturn_reason_id())) + .findFirst(); + ShopOrderReturnReason returnReason = reasonOpl.orElse(new ShopOrderReturnReason()); + return_row.put("return_reason_name", returnReason.getReturn_reason_name()); + } else { + return_row.put("return_reason_name", ""); + } + + // 13. 设置退货商品列表和状态名称 return_row.put("items", return_item_rows); - return_row.put("return_state_name", stateCodeService.getText(Convert.toInt(return_row.get("return_state_id")), "1")); + Integer return_state_id = Convert.toInt(return_row.get("return_state_id")); + if (return_state_id != null) { + return_row.put("return_state_name", + stateCodeService.getText(return_state_id, "1")); + } else { + return_row.put("return_state_name", ""); + } + + // 14. 处理平台退货状态名称 Integer plantform_return_state_id = Convert.toInt(return_row.get("plantform_return_state_id")); - return_row.put("plantform_return_state_name", getPlantformReturnStateName(plantform_return_state_id)); - + if (plantform_return_state_id != null) { + return_row.put("plantform_return_state_name", + getPlantformReturnStateName(plantform_return_state_id)); + } else { + return_row.put("plantform_return_state_name", ""); + } return return_row; } + /** * 退货单审核(商家同意退款退库) * 处理逻辑: @@ -1503,7 +1608,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl stockDeltaMap = new HashMap<>(); String item_src_id = productItem.getItem_src_id(); - stockDeltaMap.put(item_src_id + "-" + shopOrderItem.getOrder_id()+"-"+shopOrderItem.getItem_unit_price(), returnNum); - syncThirdDataService.incrProductStockToRedis(stockDeltaMap,returnItem.getReturn_item_subtotal()); + stockDeltaMap.put(item_src_id + "-" + shopOrderItem.getOrder_id() + "-" + shopOrderItem.getItem_unit_price(), returnNum); + syncThirdDataService.incrProductStockToRedis(stockDeltaMap, returnItem.getReturn_item_subtotal()); logger.info("退货返回给思迅,存入redis成功,item_src_id:{},订单号:{},数量:{}", item_src_id, shopOrderReturn.getOrder_id(), returnNum); } else { logger.warn("退货数量为空,无法增加库存,订单项ID: {}", orderItemId); @@ -1594,7 +1699,8 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl store_biz_state = shopStoreBaseService.getStoreBizState(Convert.toInt(product_base_row.get("store_id"))); - product_base_row.put("store_biz_state", store_biz_state.getFirst()); - } +// if (product_base_row != null) { +// // 获取店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外) ;2-停业中;3-开业(或活动)筹备中; +// Pair store_biz_state = shopStoreBaseService.getStoreBizState(Convert.toInt(product_base_row.get("store_id"))); +// product_base_row.put("store_biz_state", store_biz_state.getFirst()); +// } //虚拟销量 // todo 是否为商家后台访问 diff --git a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java index b51d3660..cddf2a7d 100644 --- a/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java +++ b/mall-shop/src/main/java/com/suisung/mall/shop/store/service/impl/ShopMchEntryServiceImpl.java @@ -338,7 +338,12 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(1); tmplArgs.put("name", record.getStore_name()); // 商家店铺名 - shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); + + // 【桂平发发网络】通知管理员有商家申请入驻 + if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs)) { + shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); // 【小发同城】通知管理员有商家申请入驻 + } + } log.info("商家入驻申请提交成功,recordId: {},手机号: {}", record.getId(), loginMobile); @@ -465,8 +470,12 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl tmplArgs = new HashMap<>(1); tmplArgs.put("name", mchName); // 商家公司名称 - // 尊敬的管理员,商家 ${name},提交了入驻我们平台的申请,请及时对相关资质材料予以审核,以便推进后续流程。 - shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); + + // 【桂平发发网络】通知管理员有商家申请入驻 + if (!shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_498535058", tmplArgs)) { + // 尊敬的管理员,商家 ${name},提交了入驻我们平台的申请,请及时对相关资质材料予以审核,以便推进后续流程。 + shopMessageTemplateService.aliyunSmsSend(mobileAndLicenseNumber.getFirst(), "SMS_486545331", tmplArgs); // 【小发同城】通知管理员有商家申请入驻 + } } return CommonResult.success(); diff --git a/mall-shop/src/main/resources/bootstrap-prod.yml b/mall-shop/src/main/resources/bootstrap-prod.yml index 648b70c5..54ec6388 100644 --- a/mall-shop/src/main/resources/bootstrap-prod.yml +++ b/mall-shop/src/main/resources/bootstrap-prod.yml @@ -184,7 +184,7 @@ feieyun: sf-express: # 顺丰同城 api 接口配置 dev_id: 1715091463 - # 桂平顺丰平台商家id 2273453450129 2269768012593 + # 桂平顺丰平台商家id 2273453450129 2269768012593 2281834525297 supplier_id: 2281834525297 appid: 1715091463 appkey: 47466ae69c530f831395e1bc405639fb diff --git a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml index f706dc58..b18acf8f 100644 --- a/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml +++ b/mall-shop/src/main/resources/mapper/order/ShopOrderBaseMapper.xml @@ -592,6 +592,7 @@ + @@ -656,10 +657,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + SELECT + ob.order_id, + ob.order_time, + + + CASE + WHEN oi.delivery_type_id IS NOT NULL AND oi.delivery_type_id = 16 AND #{expireSeconds} IS NOT NULL AND + #{expireSeconds} > 0 + THEN oi.order_time + #{expireSeconds}*1000 + ELSE oi.order_time + 864000000 + END as arrival_time, + + ob.order_product_amount, + ob.order_payment_amount, + ob.currency_id, + ob.order_state_id, + ob.operate_flag, + ST_Distance_Sphere( + POINT(sb.store_longitude, sb.store_latitude), + POINT(oda.da_longitude, oda.da_latitude) + ) as distance, + oi.order_title, + oi.order_pickup_num, + oi.delivery_type_id, + oi.buyer_user_id, + oi.order_picked_time, + oi.order_is_received, + oi.kind_id, + IF((SELECT count(*) FROM shop_order_base WHERE buyer_user_id = oi.buyer_user_id AND order_state_id IN + (2011,2012, + 2013, 2014, 2020, 2030, 2040))>1,2,1) + AS is_new_buyer, + oi.payment_time, + oi.booking_state, + oi.booking_begin_time, + oi.booking_end_time, + oi.booking_at, + od.order_shipping_fee, + IFNULL(od.order_shipping_fee_inner, 0) as order_shipping_fee_inner, + IFNULL(od.lkl_fee, 0) as lkl_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) + as order_income_amount, + (od.platform_fee+lkl_fee) as platform_fee, + od.packing_fee, + od.order_message, + sb.store_id, + sb.store_name, + sb.store_logo, + sb.store_latitude, + sb.store_longitude, + sb.store_area, + sb.store_address, + sb.store_grade_id, + sbsg.store_grade_name, + oit.item_id, + oit.product_id, + oit.item_name, + oit.order_item_quantity, + oit.item_unit_price, + oit.order_item_amount, + IF(oit.order_item_return_num > 0, oit.order_item_return_num, 0) AS order_item_return_num, + IF(oit.order_item_return_agree_amount > 0, oit.order_item_return_agree_amount, 0.00) AS + order_item_return_agree_amount, + spi.item_barcode, + oit.order_item_image, + oit.spec_info, + oda.da_name, + oda.da_mobile, + oda.da_latitude, + oda.da_longitude, + oda.da_province, + oda.da_city, + oda.da_county, + oda.da_address, + osf.sf_order_id, + osf.operator_name, + osf.operator_phone, + osf.rider_lng, + osf.rider_lat, + osf.order_status, + osf.status_desc, + osf.cancel_code, + osf.cancel_reason, + osf.push_time, + osf.h5_url, + osf.feed, + + + sor.return_id, + sor.order_id as return_order_id, + sor.buyer_user_id as return_buyer_user_id, + sor.buyer_store_id as return_buyer_store_id, + sor.return_reason_id, + sorr.return_reason_name, + sor.return_buyer_message, + sor.store_id as return_store_id, + sor.return_refund_amount, + sor.return_commision_fee, + sor.return_state_id, + sc1.state_code_name as return_state_name, + sor.return_tel, + sor.return_year, + sor.return_month, + sor.return_day, + sor.return_addr, + sor.return_mobile, + sor.return_telephone, + sor.return_contact_name, + sor.subsite_id as return_subsite_id, + sor.return_add_time, + sor.return_finish_time, + sor.return_is_paid, + sor.return_channel_code, + sor.return_channel_trans_id, + sor.return_channel_time, + sor.return_channel_flag, + sor.deposit_trade_no as return_deposit_trade_no, + sor.payment_channel_id as return_payment_channel_id, + sor.return_store_message, + sor.plantform_return_state_id, + sc2.state_code_name as plantform_return_state_name, + sor.return_flag, + sor.return_type, + sor.return_refund_amount as submit_return_refund_amount, + ob.buyer_user_name as return_buyer_user_name, + sor.try_return_count, + + + sorda.da_province as return_da_province, + sorda.da_city as return_da_city, + sorda.da_county as return_da_county, + sorda.da_address as return_da_address, + sorda.da_name as return_da_name, + sorda.da_mobile as return_da_mobile, + sorda.da_telephone as return_da_telephone, + sorda.da_province_id as return_da_province_id, + sorda.da_city_id as return_da_city_id, + sorda.da_county_id as return_da_county_id, + sorda.da_latitude as return_da_latitude, + sorda.da_longitude as return_da_longitude, + + + sori.order_return_item_id, + sori.return_id as item_return_id, + sori.order_item_id as return_order_item_id, + sori.order_id as item_order_id, + sori.return_item_num, + sori.return_item_subtotal, + sori.return_reason_id as item_return_reason_id, + sori.return_item_note, + sori.return_item_image, + sori.return_state_id as item_return_state_id, + sori.return_item_commision_fee, + sori.return_item_store_remark, + soi.item_name as return_item_name, + spb.product_name as return_product_name, + soi.item_id as return_item_id, + soi.order_item_image as return_order_item_image, + spb.unit_id as return_unit_id, + sbpu.unit_name as return_unit_name, + soi.order_item_unit_price as return_order_item_unit_price, + soi.order_item_quantity as return_order_item_quantity, + soi.order_item_amount as return_order_item_amount, + soi.order_item_note as return_order_item_note, + spi.item_number as return_item_number, + spb.product_number as return_product_number + + + + + FROM shop_order_base ob + JOIN shop_order_info oi ON ob.order_id = oi.order_id + JOIN shop_order_data od ON ob.order_id = od.order_id + JOIN shop_store_base sb ON ob.store_id = sb.store_id + JOIN shop_order_delivery_address oda ON ob.order_id = oda.order_id + JOIN shop_order_item oit ON ob.order_id = oit.order_id + LEFT JOIN shop_store_sf_order osf ON ob.order_id = osf.shop_order_id + LEFT JOIN shop_product_item spi ON oit.item_id = spi.item_id + LEFT JOIN shop_base_store_grade sbsg ON sb.store_grade_id = sbsg.store_grade_id + LEFT JOIN shop_order_return sor ON ob.order_id = sor.order_id + LEFT JOIN shop_order_return_reason sorr ON sor.return_reason_id = sorr.return_reason_id + LEFT JOIN shop_base_state_code sc1 ON sor.return_state_id = sc1.state_code_id + LEFT JOIN shop_base_state_code sc2 ON sor.plantform_return_state_id = sc2.state_code_id + LEFT JOIN shop_order_delivery_address sorda ON sor.order_id = sorda.order_id + LEFT JOIN shop_order_return_item sori ON sor.return_id = sori.return_id + LEFT JOIN shop_order_item soi ON sori.order_item_id = soi.order_item_id + LEFT JOIN shop_product_base spb ON soi.product_id = spb.product_id + LEFT JOIN shop_base_product_unit sbpu ON spb.unit_id = sbpu.unit_id + + + @@ -693,17 +1004,17 @@ AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030,2040) - AND (oi.order_time + #{expireSeconds}*1000) =]]> UNIX_TIMESTAMP() * 1000 + AND (oi.order_time + #{expireSeconds}*1000) ]]> UNIX_TIMESTAMP() * 1000 - AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030,2040) - AND (oi.order_time + #{expireSeconds}*1000) UNIX_TIMESTAMP() * 1000 + AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030) + AND (oi.order_time + #{expireSeconds}*1000) UNIX_TIMESTAMP() * 1000 AND od.order_refund_status IN (1, 2) - AND ob.order_state_id = 2070 + @@ -765,145 +1076,6 @@ - - - SELECT - ob.order_id, - ob.order_time, - - CASE - WHEN oi.delivery_type_id IS NOT NULL AND oi.delivery_type_id = 16 AND #{expireSeconds} IS NOT NULL AND - #{expireSeconds} > 0 - THEN oi.order_time + #{expireSeconds}*1000 - ELSE oi.order_time + 864000000 - END as arrival_time, - ob.order_product_amount, - ob.order_payment_amount, - ob.currency_id, - ob.order_state_id, - ob.operate_flag, - ST_Distance_Sphere( - POINT(sb.store_longitude, sb.store_latitude), - POINT(oda.da_longitude, oda.da_latitude) - ) as distance, - oi.order_title, - oi.order_pickup_num, - oi.delivery_type_id, - oi.buyer_user_id, - oi.order_picked_time, - oi.order_is_received, - oi.kind_id, - IF((SELECT count(*) FROM shop_order_base WHERE buyer_user_id = oi.buyer_user_id AND order_state_id IN - (2011,2012, - 2013, 2014, 2020, 2030, 2040))>1,2,1) - AS is_new_buyer, - oi.payment_time, - oi.booking_state, - oi.booking_begin_time, - oi.booking_end_time, - oi.booking_at, - od.order_shipping_fee, - IFNULL(od.order_shipping_fee_inner, 0) as order_shipping_fee_inner, - IFNULL(od.lkl_fee, 0) as lkl_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) - as order_income_amount, - (od.platform_fee+lkl_fee) as platform_fee, - od.packing_fee, - od.order_message, - sb.store_id, - sb.store_name, - sb.store_logo, - sb.store_latitude, - sb.store_longitude, - sb.store_area, - sb.store_address, - sb.store_grade_id, - sbsg.store_grade_name, - oit.item_id, - oit.product_id, - oit.item_name, - oit.order_item_quantity, - oit.item_unit_price, - oit.order_item_amount, - IF(oit.order_item_return_num > 0, oit.order_item_return_num, 0) AS order_item_return_num, - IF(oit.order_item_return_agree_amount > 0, oit.order_item_return_agree_amount, 0.00) AS - order_item_return_agree_amount, - spi.item_barcode, - oit.order_item_image, - oit.spec_info, - oda.da_name, - oda.da_mobile, - oda.da_latitude, - oda.da_longitude, - oda.da_province, - oda.da_city, - oda.da_county, - oda.da_address, - osf.sf_order_id, - osf.operator_name, - osf.operator_phone, - osf.rider_lng, - osf.rider_lat, - osf.order_status, - osf.status_desc, - osf.cancel_code, - osf.cancel_reason, - osf.push_time, - osf.h5_url, - osf.feed - - - - - - - - ", "[msallowcapture^='']", "[*^$]=", "*(?:''|\"\")", "[selected]", "*(?:value|", "[id~=", "-]", "~=", "input", "name", "[name='']", "*name", "*=", ":checked", "a#", "+*", ".#.+[+~]", "\\\f", "[\\r\\n\\f]", "", "type", "hidden", "D", "[name=d]", "*[*^$|!~]?=", ":enabled", ":disabled", "*,:x", ",.*:", "matchesSelector", "matches", "webkitMatchesSelector", "mozMatchesSelector", "oMatchesSelector", "msMatchesSelector", "disconnectedMatch", "[s!='']:x", "!=", "compareDocumentPosition", "contains", "sortDetached", "unshift", "attr", "specified", "escape", "error", "Syntax error, unrecognized expression: ", "uniqueSort", "detectDuplicates", "sortStable", "getText", "textContent", "firstChild", "nodeValue", "nth", "pseudos", "selectors", "previousSibling", "even", "odd", "CHILD", "(^|", "(", "|$)", "class", "=", "^=", "$=", "|=", "-", "last", "of-type", "only", "lastChild", "uniqueID", "setFilters", "unsupported pseudo: ", "unsupported lang: ", "lang", "xml:lang", "location", "hash", "activeElement", "hasFocus", "href", "tabIndex", "checked", "option", "selected", "selectedIndex", "empty", "button", "dir", "next", "first", "relative", "filters", "tokenize", "preFilter", "compile", "selector", "0", "select", "needsContext", "", "type|href|height|width", "", "defaultValue", "expr", ":", "unique", "isXMLDoc", "escapeSelector", "match", ":not(", "ready", "makeArray", "<", ">", "jquery", "parseHTML", "index", "prevAll", "get", "add", "contentDocument", "template", "content", "Until", "reverse", "promise", "fail", "done", "then", "Callbacks", "once", "stopOnFalse", "memory", "has", "inArray", "fireWith", "notify", "progress", "resolve", "once memory", "resolved", "reject", "rejected", "pending", "Deferred", "With", "Thenable self-resolution", "notifyWith", "resolveWith", "exceptionHook", "stackTrace", "rejectWith", "getStackHook", "setTimeout", "disable", "lock", "fire", "state", "console", "warn", "jQuery.Deferred exception: ", "message", "stack", "readyException", "removeEventListener", "DOMContentLoaded", "load", "catch", "readyWait", "isReady", "complete", "readyState", "loading", "doScroll", "toUpperCase", "ms-", "expando", "uid", "defineProperty", "cache", "set", "isEmptyObject", "data-", "-$&", "true", "false", "null", "parse", "hasData", "access", "remove", "hasDataAttrs", "fx", "queue", "_queueHooks", "inprogress", "stop", "dequeue", "queueHooks", "none", "display", "style", "css", "source", "^(?:([+-])=|)(", ")([a-z%]*)$", "Top", "Right", "Bottom", "Left", "getRootNode", "cur", "cssNumber", "px", "unit", "start", "end", "body", "block", "show", "hide", "createDocumentFragment", "radio", "t", "checkClone", "cloneNode", "", "noCloneChecked", "", "", "
", "", "
", "", "
", "", "
", "globalEval", "tbody", "tfoot", "colgroup", "caption", "thead", "th", "td", "optgroup", "", "_default", "htmlPrefilter", "createTextNode", "focus", "guid", "off", "event", "isTrigger", "delegateType", "special", "stopPropagation", "stopImmediatePropagation", "preventDefault", "trigger", "Event", "handler", "events", "create", "handle", "triggered", "dispatch", ".", "bindType", "delegateCount", "setup", "global", "(^|\\.)", "\\.(?:.*\\.|)", "(\\.|$)", "origType", "namespace", "**", "teardown", "removeEvent", "handle events", "fix", "delegateTarget", "preDispatch", "handlers", "isPropagationStopped", "currentTarget", "elem", "isImmediatePropagationStopped", "rnamespace", "handleObj", "data", "result", "postDispatch", "target", "click", "originalEvent", "a", "returnValue", "isDefaultPrevented", "defaultPrevented", "relatedTarget", "timeStamp", "now", "isSimulated", "addProp", "focusin", "focusout", "mouseover", "mouseout", "pointerover", "pointerout", "table", "tr", "children", "/", "true/", "html", "clone", "src", "module", "_evalUrl", "noModule", "cleanData", "textarea", "insertBefore", "append", "replaceChild", "prepend", "before", "after", "replaceWith", "getComputedStyle", "opener", ")(?!px)[a-z%]+$", "getPropertyValue", "pixelBoxStyles", "width", "minWidth", "maxWidth", "cssText", "position:absolute;left:-11111px;width:60px;margin-top:1px;padding:0;border:0", "position:relative;display:block;box-sizing:border-box;overflow:scroll;margin:auto;border:1px;padding:1px;width:60%;top:1%", "1%", "marginLeft", "right", "60%", "position", "absolute", "offsetWidth", "round", "backgroundClip", "content-box", "clearCloneStyle", "position:absolute;left:-11111px;border-collapse:separate", "border:1px solid", "height", "1px", "9px", "borderTopWidth", "borderBottomWidth", "offsetHeight", "Webkit", "Moz", "ms", "cssProps", "400", "max", "border", "margin", "padding", "Width", "ceil", "offset", "boxSizingReliable", "border-box", "boxSizing", "auto", "reliableTrDimensions", "inline", "getClientRects", "opacity", "1", "cssHooks", "background", "inherit", "setProperty", "normal", "getBoundingClientRect", "scrollboxSize", "reliableMarginLeft", "left", "Tween", "prop", "easing", "options", "propHooks", "duration", "pos", "step", "scrollTop", "scrollLeft", "cos", "PI", "swing", "requestAnimationFrame", "interval", "tick", "tweeners", "prefilters", "always", "startTime", "tweens", "run", "opts", "specialEasing", "props", "expand", "bind", "timer", "Animation", "createTween", "fxshow", "unqueued", "toggle", "overflow", "overflowX", "overflowY", "inline-block", "float", "speed", "speeds", "old", "animate", "finish", "timers", "anim", "delay", "clearTimeout", "checkbox", "checkOn", "optSelected", "radioValue", "removeAttr", "attrHooks", "bool", "propFix", "tabindex", "htmlFor", "readOnly", "maxLength", "cellSpacing", "cellPadding", "rowSpan", "colSpan", "useMap", "frameBorder", "contentEditable", "addClass", "removeClass", "toggleClass", "hasClass", "__className__", "val", "valHooks", "select-one", "on", "onfocusin", "noBubble", "parentWindow", "simulate", "parseXML", "parseFromString", "DOMParser", "text/xml", "parsererror", "Invalid XML: ", "\n", "param", "[", "&", "serializeArray", "elements", "\r\n", "*/", "dataTypes", "flatOptions", "ajaxSettings", "GET", "protocol", "application/x-www-form-urlencoded; charset=UTF-8", "text/plain", "text/html", "application/xml, text/xml", "application/json, text/javascript", "responseXML", "responseText", "responseJSON", "ajaxSetup", "context", "statusCode", "canceled", ", ", "mimeType", "status", "abort", "url", "//", "method", "dataType", "crossDomain", "host", "processData", "traditional", "active", "ajaxStart", "hasContent", "contentType", "application/x-www-form-urlencoded", "?", "_=", "ifModified", "lastModified", "setRequestHeader", "If-Modified-Since", "etag", "If-None-Match", "Content-Type", "Accept", "accepts", "; q=0.01", "headers", "beforeSend", "success", "ajaxSend", "async", "timeout", "send", "No Transport", "contents", "getResponseHeader", "converters", "json", "text script", "responseFields", "dataFilter", "* ", "throws", "No conversion from ", " to ", "Last-Modified", "HEAD", "nocontent", "notmodified", "statusText", "ajaxSuccess", "ajaxError", "ajaxComplete", "ajaxStop", "post", "ajax", "ajaxPrefilter", "content-type", "firstElementChild", "wrapInner", "wrapAll", "not", "parent", "visible", "xhr", "XMLHttpRequest", "cors", "withCredentials", "ajaxTransport", "open", "username", "password", "xhrFields", "overrideMimeType", "X-Requested-With", "onload", "onerror", "onabort", "ontimeout", "onreadystatechange", "responseType", "response", "getAllResponseHeaders", "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript", "scriptAttrs", " - + @@ -60,18 +60,18 @@
-
+
{{(index+1)}}. {{item.PageTitle?item.PageTitle:'第'+(index+1)+'项'}} @@ -80,12 +80,12 @@ 积分 - - +
-
+
首页
- +
社区
+ class="weui-switch" type="checkbox" v-model="item.IsSns" + :SnsPageId="item.Id" v-on:change="setSnsPage"/>
B端首页
+ class="weui-switch" type="checkbox" v-model="item.StoreIndex" + :StoreIndexId="item.Id" v-on:change="setStoreIndex"/>
资讯
+ class="weui-switch" type="checkbox" v-model="item.IsArticle" + :PointPageId="item.Id" :data-page-id="item.Id" + data-page-type="IsArticle" v-on:change="setDefaultPage"/>
积分
+ class="weui-switch" type="checkbox" v-model="item.IsPoint" + :PointPageId="item.Id" :data-page-id="item.Id" + data-page-type="IsPoint" v-on:change="setDefaultPage"/>
@@ -352,14 +352,12 @@
- + - +
小程序 @@ -371,14 +369,13 @@
-
用户中心 - + style="font-size: 14px;">
@@ -387,7 +384,7 @@
-
-
    -
  • -
    - +
  • +
    +
    -

    轮播

    +

    轮播

  • -
  • -
    - +
  • +
    +
    -

    图片

    +

    图片

  • -
  • -
    - +
  • +
    +
    -

    图片组

    +

    图片组

  • -
  • -
    - +
  • +
    +
    -

    商品列表

    +

    商品列表

  • -
  • -
    - +
  • +
    +
    -

    功能入口

    +

    功能入口

  • -
  • -
    - +
  • +
    +
    -

    辅助空白

    +

    辅助空白

  • -
  • -
    - +
  • +
    +
    -

    搜索

    +

    搜索

  • -
  • -
    - +
  • +
    +
    -

    电话

    +

    电话

  • - -
    - + +
    +
    -

    背景

    +

    背景

    -
  • -
    - +
  • +
    +
    -

    客服

    +

    客服

  • -
  • -
    - +
  • +
    +
    -

    视频

    +

    视频

  • - +

    视频

    -
  • -
    - +
  • +
    +
    -

    动态表单

    +

    动态表单

  • - +

    动态表单

    -
  • -
    - +
  • +
    +
    -

    通告栏

    +

    通告栏

  • -
  • -
    - +
  • +
    +
    -

    标题

    +

    标题

  • -
  • -
    - +
  • +
    +
    -

    营销活动

    +

    营销活动

  • -
  • -
    - +
  • +
    +
    -

    优惠券

    +

    优惠券

  • -
  • -
    - +
  • +
    +
    -

    公众号关注

    +

    公众号关注

  • -
  • -
    - +
  • +
    +
    -

    猜你喜欢

    +

    猜你喜欢

  • -
  • -
    - +
  • +
    +
    -

    附近店铺

    +

    附近店铺

  • -
  • -
    - +
  • +
    +
    -

    秒杀商品

    +

    秒杀商品

  • -
  • -
    - +
  • +
    +
    -

    分类商品

    +

    分类商品

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

    测试组件

    +

    测试组件

  • - +
-
-
+

{{StoreName}}

@@ -609,7 +605,7 @@
- @@ -617,7 +613,7 @@
- @@ -626,17 +622,16 @@
--> -
-
@@ -647,13 +642,13 @@
+ style="background:url(${URL_STATIC}/shop/static/diy/img/photo.png);background-size: cover;">
+ style="background:url(${URL_STATIC}/shop/static/diy/img/photo.png);background-size: cover;">
@@ -677,19 +672,19 @@
-
+
  • 自定义装修区域
-
+
  • 常用功能
  • - {{item.name}}
    @@ -697,13 +692,13 @@
-
+
  • 特色功能
  • - {{item.name}}
    @@ -712,7 +707,7 @@
-
-
+
- - - - - - - - - -
+ }"> + + + + + + + + + +
    -
  • 编辑 +
  • 编辑
  • -
  • 删除
-
+ borderBottomRightRadius: (item.borderBottomRightRadius||0) + 'px'}">

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

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

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

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

- + v-if="item.eltm7.data.length>0" v-for="items in item.eltm7.data" + v-bind:style="{width:(100/item.eltm7.column)+'%',paddingTop:item.eltm7.paddingTop+'px',paddingBottom:item.eltm7.paddingBottom+'px',paddingLeft:item.eltm7.paddingLeft+'px',paddingRight:item.eltm7.paddingRight+'px',backgroundColor:item.bgColor}">
- +

功能入口

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