Merge branch 'main' into dev

This commit is contained in:
liyj 2025-11-18 16:23:41 +08:00
commit 1a9375086c
22 changed files with 48543 additions and 23061 deletions

View File

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

View File

@ -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<Integer, String> 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._("上午"));

View File

@ -42,6 +42,9 @@ public class MchOrderInfoDTO implements Serializable {
// 物流轨迹信息(物流已发货的才有数据)
@ApiModelProperty(value = "物流轨迹信息")
Map<String, Object> 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;
}

View File

@ -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<Long> 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<MchReturnOrderItemDetailDTO> items;
}

View File

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

View File

@ -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<Boolean, String> 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<Boolean, String> 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> Boolean表示是否成功String为错误信息
*/
private Pair<Boolean, String> 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> Boolean表示是否成功String为错误信息
*/
private Pair<Boolean, String> 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 : "");
}
}
}
}

View File

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

View File

@ -330,13 +330,17 @@ 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 * 60000L);
cutprice_row.setExpired_at(now.getTime() + cut_hour * 3600000L);
// log.info("砍价失效时间戳:{}", cutprice_row.getExpired_at());
cutprice_row.setAc_datetime(now);
cutprice_row.setOrder_id("");
cutprice_row.setUpdated_at(now);
// 保存砍价记录
if (!saveOrUpdate(cutprice_row)) {

View File

@ -65,7 +65,7 @@ public class UpdateActivityStatusJob extends QuartzJobBean {
page++;
}
// 更新砍价订单的过期状态
// 活动时间到更新砍价订单的过期状态
shopActivityCutpriceService.autoUpdateCutPriceStateJob();
}

View File

@ -86,7 +86,6 @@ public class LklReceiveNotifyLogServiceImpl extends BaseServiceImpl<LklReceiveNo
try {
log.debug("[LklReceiveNotifyLog] 开始查询拉卡拉确认收货通知记录,订单号={}", orderId);
QueryWrapper<LklReceiveNotifyLog> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("order_id", orderId);

View File

@ -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<JSONObject> 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<JSONObject> 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<JSONObject> 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<JSONObject> 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
*

View File

@ -385,8 +385,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
@Autowired
private ThreadPoolExecutor executor;
@Autowired
private DataSourceTransactionManager transactionManager;
@Autowired
private TransactionDefinition transactionDefinition;
@ -5053,6 +5055,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
log.info("[确认收货] 符合条件的订单数量: eligibleCount={}, totalChecked={}", receive_id_row.size(), order_rows.size());
// 收集需要处理跳转路径的订单ID
List<String> jumpPathOrderIds = new ArrayList<>();
// 处理符合条件的订单
for (ShopOrderBase order_row : order_rows) {
// 只处理符合条件的订单
@ -5076,7 +5080,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
/// 服务号确认收货通知跳转链接设置
wxOrderShippingService.setMsgJumpPath(order_id);
jumpPathOrderIds.add(order_row.getOrder_id());
// wxOrderShippingService.setMsgJumpPath(order_id);
BigDecimal order_points_add = order_data_row.getOrder_points_add();
BigDecimal order_double_points_add = order_data_row.getOrder_double_points_add();
@ -5126,6 +5131,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
}
// 异步批量处理延迟跳转路径设置
if (!jumpPathOrderIds.isEmpty()) {
processOrderJumpPathAsync(jumpPathOrderIds);
}
// 修改订单状态, 随机去一个订单获取店铺编号
ShopOrderBase shopOrderBase = order_rows.stream()
.filter(order -> receive_id_row.contains(order.getOrder_id()))
@ -5176,10 +5186,39 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
}
}
log.info("[确认收货] 处理完成: processedOrders={}", receive_id_row.size());
return true;
}
/**
* 延迟 消息跳转路径设置接口
*
* @param orderIds
*/
private void processOrderJumpPathAsync(List<String> 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<ShopOrderBaseMappe
data_row.setOrder_redpacket_price(BigDecimal.ZERO); // 红包抵扣订单金额
data_row.setOrder_resource_ext1(order_resource_ext1_use_current);
data_row.setOrder_resource_ext2(order_resource_ext2_use);
data_row.setOrder_refund_status(0); // 退款状态:0-是无退款;1-是部分退款;2-是全部退款
data_row.setOrder_return_status(0); // 退货状态(ENUM):0-是无退货;1-是部分退货;2-是全部退货
data_row.setOrder_refund_status(StateCode.ORDER_REFUND_STATUS_NO); // 退款状态:0-是无退款;1-是部分退款;2-是全部退款
data_row.setOrder_return_status(StateCode.ORDER_REFUND_STATUS_NO); // 退货状态(ENUM):0-是无退货;1-是部分退货;2-是全部退货
data_row.setOrder_return_num(0); // 退货数量
BigDecimal order_item_commission_fee = item_rows.stream().map(s -> s.getOrder_item_commission_fee()).reduce(BigDecimal::add).get();

View File

@ -1063,6 +1063,7 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
if (shopOrderData == null) {
throw new ApiException(I18nUtil._("订单详细信息为空!"));
}
for (ShopOrderReturnItem orderReturnItem : orderReturnItems) {
ShopOrderItem order_item_row = order_item_rows.stream().filter(s -> s.getOrder_item_id().equals(orderReturnItem.getOrder_item_id())).findFirst().orElse(null);
@ -1150,45 +1151,91 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
return true;
}
/**
* 获取退款详情数据
*
* @param return_id 退货单ID
* @return 退货单详情信息
*/
@Override
public Map getReturnDetail(String return_id) {
// 1. 获取退货单基础信息
ShopOrderReturn shopOrderReturn = get(return_id);
if (shopOrderReturn == null) {
logger.warn("退货单不存在: return_id={}", return_id);
return new HashMap<>();
}
Map return_row = Convert.toMap(String.class, Object.class, shopOrderReturn);
// 2. 获取退货单商品明细
QueryWrapper<ShopOrderReturnItem> itemQueryWrapper = new QueryWrapper<>();
itemQueryWrapper.eq("return_id", return_id);
List<ShopOrderReturnItem> shopOrderReturnItems = orderReturnItemService.find(itemQueryWrapper);
List<Map> return_item_rows = Convert.toList(Map.class, shopOrderReturnItems);
List<Long> order_item_ids = shopOrderReturnItems.stream().map(s -> s.getOrder_item_id()).distinct().collect(Collectors.toList());
List<Long> 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<ShopOrderReturnReason> 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<Map> order_row_items = (List<Map>) 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<Long> item_ids = order_row_items.stream().map(s -> Convert.toLong(s.get("item_id"))).distinct().collect(Collectors.toList());
List<Map> product_items = shopProductItemService.gets(item_ids);
List<Long> item_ids = order_row_items.stream()
.map(s -> Convert.toLong(s.get("item_id")))
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<Integer> product_ids = order_row_items.stream().map(s -> Convert.toInt(s.get("product_id"))).distinct().collect(Collectors.toList());
List<ShopProductInfo> product_infos = shopProductInfoService.gets(product_ids);
List<Map> product_items = CollUtil.isEmpty(item_ids) ? new ArrayList<>() :
shopProductItemService.gets(item_ids);
List<ShopProductIndex> product_indexs = shopProductIndexService.gets(product_ids);
List<Integer> 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<ShopProductInfo> product_infos = CollUtil.isEmpty(product_ids) ? new ArrayList<>() :
shopProductInfoService.gets(product_ids);
List<ShopProductIndex> product_indexs = CollUtil.isEmpty(product_ids) ? new ArrayList<>() :
shopProductIndexService.gets(product_ids);
// 7. 构建退货商品临时数据映射
Map<Long, 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<ShopOrderReturnM
order_item.put("order_item_amount", item.get("order_item_amount"));
order_item.put("order_item_note", item.get("order_item_note"));
Optional<Map> 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<Map> 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<ShopProductInfo> 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<ShopProductInfo> 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<ShopOrderReturnReason> 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<ShopOrderReturnReason> 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<ShopOrderReturnM
if (productItem == null) {
logger.error("该订单商品表-SKU表信息不存在商品项ID: {}", itemId);//说明这个itemid已经不存在了不对库存操作
continue;
// throw new ApiException(I18nUtil._("该订单商品表-SKU表信息不存在"));
// throw new ApiException(I18nUtil._("该订单商品表-SKU表信息不存在"));
}
// 增加库存
@ -1526,8 +1631,8 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
// RMK 第三方数据同步相关redis 新增返还思迅库存
Map<String, Integer> 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<ShopOrderReturnM
ShopOrderData shopOrderData = new ShopOrderData();
shopOrderData.setOrder_id(order_id);
shopOrderData.setOrder_refund_status(2); //退款状态:0-是无退款;1-是部分退款;2-是全部退款
shopOrderData.setOrder_refund_status(StateCode.ORDER_REFUND_STATUS_ALL); //退款状态:0-是无退款;1-是部分退款;2-是全部退款
shopOrderData.setOrder_return_status(StateCode.ORDER_REFUND_STATUS_ALL);
if (!shopOrderDataService.edit(shopOrderData)) {
logger.error("修改订单退款状态失败订单ID: {}", order_id);
throw new ApiException(ResultCode.FAILED);
@ -1671,7 +1777,9 @@ public class ShopOrderReturnServiceImpl extends BaseServiceImpl<ShopOrderReturnM
logger.debug("订单部分商品退款订单ID: {}", order_id);
ShopOrderData orderData = new ShopOrderData();
orderData.setOrder_id(order_id);
orderData.setOrder_refund_status(CommonConstant.Enable);
// 退款状态:0-是无退款;1-是部分退款;2-是全部退款
orderData.setOrder_refund_status(StateCode.ORDER_REFUND_STATUS_PART);
orderData.setOrder_return_status(StateCode.ORDER_REFUND_STATUS_PART);
if (!shopOrderDataService.edit(orderData)) {
logger.error("部分退款失败订单ID: {}", order_id);
throw new ApiException(I18nUtil._("部分退款失败!"));

View File

@ -27,6 +27,7 @@ import com.suisung.mall.common.pojo.dto.OssPolicyResultDTO;
import com.suisung.mall.common.utils.CheckUtil;
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.shop.base.service.AccountBaseConfigService;
import com.suisung.mall.shop.page.service.OssService;
import com.suisung.mall.shop.page.utis.VideoUtil;
@ -144,7 +145,7 @@ public class OssServiceImpl implements OssService {
throw new ApiException(String.format(I18nUtil._("允许上传格式为:【%s】"), StringUtils.join(allowExtList, ",")));
}
Map result = upload(file, user, dir, uploadPath, uploadName, fileName);
Map result = upload(file, user, dir, uploadPath, uploadName);
String url = (String) result.get("media_url");
String thumb = (String) result.get("thumb");
@ -173,7 +174,10 @@ public class OssServiceImpl implements OssService {
* @param user 用户信息
* @return
*/
public Map upload(MultipartFile file, UserDto user, String dir, String uploadPath, String uploadName, String fileName) {
public Map upload(MultipartFile file, UserDto user, String dir, String uploadPath, String uploadName) {
// 如果是图片先压缩图片再上传到 cos 对象存储
file = UploadUtil.compressImageIfNeeded(file, 2048);
// 创建临时文件
creTempFile(file, dir, uploadName);
// String localUrl = IP + "/admin/shop/static/image/" + dir + uploadName; // 文件本地路径
@ -294,7 +298,7 @@ public class OssServiceImpl implements OssService {
throw new ApiException(String.format(I18nUtil._("允许上传格式为:【%s】"), StringUtils.join(allowExtList, ",")));
}
return upload(file, user, dir, uploadPath, uploadName, fileName);
return upload(file, user, dir, uploadPath, uploadName);
}
/**

View File

@ -2078,11 +2078,11 @@ public class ShopProductBaseServiceImpl extends BaseServiceImpl<ShopProductBaseM
Integer product_sale_num = Convert.toInt(product_index_row.get("product_sale_num"));
if (product_base_row != null) {
// 获取店铺营业状态1-开业营业中且在营业时间内12-开业打烊中但在营业时间外 2-停业中3-开业或活动筹备中
Pair<Integer, String> store_biz_state = shopStoreBaseService.getStoreBizState(Convert.toInt(product_base_row.get("store_id")));
product_base_row.put("store_biz_state", store_biz_state.getFirst());
}
// if (product_base_row != null) {
// // 获取店铺营业状态1-开业营业中且在营业时间内12-开业打烊中但在营业时间外 2-停业中3-开业或活动筹备中
// Pair<Integer, String> store_biz_state = shopStoreBaseService.getStoreBizState(Convert.toInt(product_base_row.get("store_id")));
// product_base_row.put("store_biz_state", store_biz_state.getFirst());
// }
//虚拟销量
// todo 是否为商家后台访问

View File

@ -338,7 +338,12 @@ public class ShopMchEntryServiceImpl extends BaseServiceImpl<ShopMchEntryMapper,
if (mobileAndLicenseNumber != null) {
Map<String, Object> 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<ShopMchEntryMapper,
String mchName = StrUtil.isBlank(record.getStore_name()) ? "重新修正资料" : record.getStore_name();
Map<String, Object> 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();

View File

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

View File

@ -592,6 +592,7 @@
<result property="kind_id" column="kind_id"/>
<result property="delivery_type_id" column="delivery_type_id"/>
<result property="distance" column="distance"/>
<!--预约订单相关字段-->
<result property="booking_state" column="booking_state"/>
<result property="booking_begin_time" column="booking_begin_time"/>
@ -656,10 +657,320 @@
<result property="order_item_return_num" column="order_item_return_num"/>
<result property="order_item_return_agree_amount" column="order_item_return_agree_amount"/>
</collection>
<!-- 退款订单集合映射 -->
<collection property="return_order_detail" ofType="com.suisung.mall.common.modules.order.dto.MchReturnOrderDetailDTO">
<id property="return_id" column="return_id"/>
<result property="order_id" column="return_order_id"/>
<result property="buyer_user_id" column="return_buyer_user_id"/>
<result property="buyer_store_id" column="return_buyer_store_id"/>
<result property="return_reason_id" column="return_reason_id"/>
<result property="return_reason_name" column="return_reason_name"/>
<result property="return_buyer_message" column="return_buyer_message"/>
<result property="store_id" column="return_store_id"/>
<result property="return_refund_amount" column="return_refund_amount"/>
<result property="return_commision_fee" column="return_commision_fee"/>
<result property="return_state_id" column="return_state_id"/>
<result property="return_state_name" column="return_state_name"/>
<result property="return_tel" column="return_tel"/>
<result property="return_year" column="return_year"/>
<result property="return_month" column="return_month"/>
<result property="return_day" column="return_day"/>
<result property="return_addr" column="return_addr"/>
<result property="return_mobile" column="return_mobile"/>
<result property="return_telephone" column="return_telephone"/>
<result property="return_contact_name" column="return_contact_name"/>
<result property="subsite_id" column="return_subsite_id"/>
<result property="return_add_time" column="return_add_time"/>
<result property="return_finish_time" column="return_finish_time"/>
<result property="return_is_paid" column="return_is_paid"/>
<result property="return_channel_code" column="return_channel_code"/>
<result property="return_channel_trans_id" column="return_channel_trans_id"/>
<result property="return_channel_time" column="return_channel_time"/>
<result property="return_channel_flag" column="return_channel_flag"/>
<result property="deposit_trade_no" column="return_deposit_trade_no"/>
<result property="payment_channel_id" column="return_payment_channel_id"/>
<result property="return_store_message" column="return_store_message"/>
<result property="plantform_return_state_id" column="plantform_return_state_id"/>
<result property="plantform_return_state_name" column="plantform_return_state_name"/>
<result property="return_flag" column="return_flag"/>
<result property="return_type" column="return_type"/>
<result property="submit_return_refund_amount" column="submit_return_refund_amount"/>
<result property="buyer_user_name" column="return_buyer_user_name"/>
<result property="contract_type_ids" column="contract_type_ids"/>
<result property="try_return_count" column="try_return_count"/>
<!-- 收货地址相关信息 -->
<result property="da_province" column="return_da_province"/>
<result property="da_city" column="return_da_city"/>
<result property="da_county" column="return_da_county"/>
<result property="da_address" column="return_da_address"/>
<result property="da_name" column="return_da_name"/>
<result property="da_mobile" column="return_da_mobile"/>
<result property="da_telephone" column="return_da_telephone"/>
<result property="da_province_id" column="return_da_province_id"/>
<result property="da_city_id" column="return_da_city_id"/>
<result property="da_county_id" column="return_da_county_id"/>
<result property="da_latitude" column="return_da_latitude"/>
<result property="da_longitude" column="return_da_longitude"/>
<!-- 退款商品列表映射 -->
<collection property="items" ofType="com.suisung.mall.common.modules.order.dto.MchReturnOrderItemDetailDTO">
<id property="order_return_item_id" column="order_return_item_id"/>
<result property="return_id" column="item_return_id"/>
<result property="order_item_id" column="return_order_item_id"/>
<result property="order_id" column="item_order_id"/>
<result property="return_item_num" column="return_item_num"/>
<result property="return_item_subtotal" column="return_item_subtotal"/>
<result property="return_reason_id" column="item_return_reason_id"/>
<result property="return_item_note" column="return_item_note"/>
<result property="return_item_image" column="return_item_image"/>
<result property="return_state_id" column="item_return_state_id"/>
<result property="return_item_commision_fee" column="return_item_commision_fee"/>
<result property="return_item_store_remark" column="return_item_store_remark"/>
<!-- 关联的订单商品信息 -->
<result property="item_name" column="return_item_name"/>
<result property="product_name" column="return_product_name"/>
<result property="item_id" column="return_item_id"/>
<result property="order_item_image" column="return_order_item_image"/>
<result property="unit_id" column="return_unit_id"/>
<result property="unit_name" column="return_unit_name"/>
<result property="order_item_unit_price" column="return_order_item_unit_price"/>
<result property="order_item_quantity" column="return_order_item_quantity"/>
<result property="order_item_amount" column="return_order_item_amount"/>
<result property="order_item_weight_total" column="return_order_item_weight_total"/>
<result property="order_item_note" column="return_order_item_note"/>
<result property="item_number" column="return_item_number"/>
<result property="product_number" column="return_product_number"/>
</collection>
</collection>
</resultMap>
<!--refundstatus 退款状态:0-是无退款;1-是部分退款;2-是全部退款
<select id="selectMchOrderPageList" resultMap="MchOrderResult">
<include refid="mchOrderColumns"/>
<include refid="mchOrderFromAndJoins"/>
<include refid="mchOrderWhereCondition"/>
ORDER BY ob.order_time DESC
</select>
<select id="countMchOrderByCondition" resultType="java.lang.Long">
SELECT COUNT(DISTINCT ob.order_id)
<include refid="mchOrderFromAndJoins"/>
<include refid="mchOrderWhereCondition"/>
</select>
<select id="getMchOrderDetail" resultMap="MchOrderResult">
<include refid="mchOrderColumns"/>
<include refid="mchOrderFromAndJoins"/>
<where>
ob.order_id=#{orderId}
</where>
ORDER BY ob.order_id DESC
</select>
<!--应付金额/应支付金额:order_goods_amount - order_discount_amount + order_shipping_fee - order_voucher_price - order_points_fee - order_adjust_fee-->
<sql id="mchOrderColumns">
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 <!-- 10天 = 10*24*60*60*1000 = 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,
<!--总计优惠金额 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)
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
</sql>
<!-- 公共的FROM和JOIN子句 -->
<sql id="mchOrderFromAndJoins">
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
</sql>
<!--refundstatus 退款状态:0-是无退款;1-是部分退款;2-是全部退款
orderstatus 订单状态2010-待付款;2011-待订单审核;2012-待发货;2013-待财务审核;2014-待配货/待出库审核;2020-待发货;
2030-待发货/待收货确认;2040-已发货/待收货确认;2050-已签收;2060-已完成/已签收;2070-已取消/已作废;2080-自提-->
<!-- status: 同城配送订单状态delivery=1时才生效无值 or 0-全部订单1-进行中订单2-异常超时订单3-退款订单9-已完成订单-->
@ -693,17 +1004,17 @@
<!-- 1-进行中订单 -->
<when test="status != null and status == 1 and expireSeconds != null and expireSeconds > 0">
AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030,2040)
AND (oi.order_time + #{expireSeconds}*1000) <![CDATA[>=]]> UNIX_TIMESTAMP() * 1000
AND (oi.order_time + #{expireSeconds}*1000) <![CDATA[>]]> UNIX_TIMESTAMP() * 1000
</when>
<!-- 2-异常(超时)订单 -->
<when test="status != null and status == 2 and expireSeconds != null and expireSeconds > 0">
AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030,2040)
AND (oi.order_time + #{expireSeconds}*1000) <![CDATA[<]]> UNIX_TIMESTAMP() * 1000
AND ob.order_state_id IN (2011,2012,2013,2014,2020,2030)
AND (oi.order_time + #{expireSeconds}*1000) <![CDATA[<=]]> UNIX_TIMESTAMP() * 1000
</when>
<!-- 3-退款订单 -->
<when test="status != null and status == 3">
AND od.order_refund_status IN (1, 2)
AND ob.order_state_id = 2070
<!-- AND ob.order_state_id = 2070-->
</when>
<!-- 9-已完成订单 -->
<when test="status != null and status == 9">
@ -765,145 +1076,6 @@
</where>
</sql>
<!--应付金额/应支付金额:order_goods_amount - order_discount_amount + order_shipping_fee - order_voucher_price - order_points_fee - order_adjust_fee-->
<sql id="mchOrderColumns">
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 <!-- 10天 = 10*24*60*60*1000 = 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,
<!--总计优惠金额 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)
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
</sql>
<select id="selectMchOrderPageList" resultMap="MchOrderResult">
<include refid="mchOrderColumns"/>
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.product_id=spi.product_id
left join shop_base_store_grade sbsg on sb.store_grade_id=sbsg.store_grade_id
<include refid="mchOrderWhereCondition"/>
ORDER BY ob.order_time DESC
</select>
<select id="countMchOrderByCondition" resultType="java.lang.Long">
SELECT COUNT(DISTINCT ob.order_id)
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
<include refid="mchOrderWhereCondition"/>
</select>
<select id="getMchOrderDetail" resultMap="MchOrderResult">
<include refid="mchOrderColumns"/>
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.product_id=spi.product_id
left join shop_base_store_grade sbsg on sb.store_grade_id=sbsg.store_grade_id
<where>
ob.order_id=#{orderId}
</where>
ORDER BY ob.order_id DESC
</select>
<select id="getWxOrderBaseInfo" resultMap="WxOrderBaseInfoResult">
SELECT a.store_id,

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long