Merge branch 'main' into dev
This commit is contained in:
commit
4efd79c180
@ -260,7 +260,7 @@ public class LoginController extends BaseControllerImpl {
|
||||
return accountUserBaseService.doMerchSmsRegisterAndLogin(userMobile, randKey, verifyCode, cid, osType);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "微信用户一键登录和注册")
|
||||
@ApiOperation(value = "微信用户一键登录与注册")
|
||||
@RequestMapping(value = "/doWxUserRegisterAndLogin", method = RequestMethod.POST)
|
||||
public CommonResult doWxUserRegisterAndLogin(@RequestBody WxUserInfoReq wxUserInfoReq) {
|
||||
return accountUserBaseService.doWxUserRegisterAndLogin(wxUserInfoReq);
|
||||
|
||||
@ -2429,7 +2429,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
||||
* 根据账号和账号类型获取一条记录
|
||||
*
|
||||
* @param userAccount 用户账号
|
||||
* @param userIsAdmin 用户类型: null-普通用户; 1-管理员; 2-入驻商家;
|
||||
* @param userIsAdmin 用户类型: null or 0-普通用户; 1-管理员; 2-入驻商家;
|
||||
* @return AccountUserBase 用户基础信息,如果未找到则返回null
|
||||
*/
|
||||
public AccountUserBase getByAccountAndType(String userAccount, Integer userIsAdmin) {
|
||||
@ -3019,7 +3019,6 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
||||
|
||||
// 检查输入字符是不是包含 sql 注入特征,如果包含不给以通过
|
||||
if (!CommonService.isValidInput(wxUserInfoReq.getPhoneNumber(), wxUserInfoReq.getOpenId())) {
|
||||
// new ApiException(ResultCode.VALIDATE_INPUTS);
|
||||
return CommonResult.failed(ResultCode.VALIDATE_INPUTS);
|
||||
}
|
||||
|
||||
@ -3061,7 +3060,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
||||
accountUserBase.setRights_group_id("0");// 店铺管理员,店铺 权限
|
||||
|
||||
if (!saveOrUpdate(accountUserBase)) {
|
||||
return CommonResult.failed(_("用户注册失败!"));
|
||||
return CommonResult.failed(_("微信注册账号失败!"));
|
||||
}
|
||||
|
||||
Integer userId = accountUserBase.getUser_id();
|
||||
@ -3153,7 +3152,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
||||
AccountUserBindConnect accountUserBindConnect = accountUserBindConnectService.getBindByBindId(mobile, BindCode.MOBILE, CommonConstant.USER_TYPE_NORMAL);
|
||||
if (accountUserBindConnect == null || !Objects.equals(accountUserBase.getUser_id(), accountUserBindConnect.getUser_id())) {
|
||||
// 先绑定手机号
|
||||
if (accountUserBindConnectService.bindMobileAndOpenId(wxUserInfoReq, accountUserBase.getUser_id(), accountUserBase.getUser_is_admin()) == null) {
|
||||
if (accountUserBindConnectService.bindMobileAndOpenId(wxUserInfoReq, accountUserBase.getUser_id(), CommonConstant.USER_TYPE_NORMAL) == null) {
|
||||
return CommonResult.failed(_("账号绑定失败!"));
|
||||
}
|
||||
}
|
||||
@ -3163,7 +3162,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
||||
params.put("client_id", AuthConstant.MOBILE_CLIENT_ID);
|
||||
params.put("client_secret", AuthConstant.AUTHORITY_MOBILE_SECRET);
|
||||
params.put("grant_type", "password");
|
||||
params.put("verify_pwd", "1001"); // 是否验证密码 1001:不验证;1002:验证(内部登录没有用户明文密码,只能不验证)
|
||||
params.put("verify_pwd", "1001"); // 是否验证密码 1001-不验证;1002-验证(内部登录没有用户明文密码,只能不验证)
|
||||
params.put("username", mobile);
|
||||
params.put("password", "");
|
||||
params.put("user_mobile", mobile);
|
||||
|
||||
@ -399,7 +399,7 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
||||
.eq("bind_active", CommonConstant.Enable)
|
||||
.orderByAsc("bind_time");
|
||||
|
||||
AccountUserBindConnect accountUserBindConnect = findOne(queryWrapper);
|
||||
AccountUserBindConnect accountUserBindConnect = getOne(queryWrapper);
|
||||
if (accountUserBindConnect != null) {
|
||||
return accountUserBindConnect;
|
||||
}
|
||||
@ -413,7 +413,7 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
||||
record.setBind_openid(wxUserInfoReq.getOpenId());
|
||||
record.setBind_unionid(wxUserInfoReq.getUnionId());
|
||||
record.setBind_gender(wxUserInfoReq.getGender());
|
||||
record.setBind_nickname(wxUserInfoReq.getNickName());
|
||||
record.setBind_nickname(wxUserInfoReq.getNickName() + mobile);
|
||||
record.setBind_icon(wxUserInfoReq.getAvatarUrl());
|
||||
record.setBind_country(wxUserInfoReq.getCountry());
|
||||
record.setBind_province(wxUserInfoReq.getProvince());
|
||||
|
||||
@ -26,6 +26,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@ -66,8 +67,8 @@ public class AnalyticsUserServiceImpl implements AnalyticsUserService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (yestodayRegUser.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (todayRegUser.getNum().subtract(yestodayRegUser.getNum())).divide(yestodayRegUser.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (todayRegUser.getNum().subtract(yestodayRegUser.getNum())).divide(yestodayRegUser.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -126,8 +127,8 @@ public class AnalyticsUserServiceImpl implements AnalyticsUserService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -174,78 +174,92 @@ public class OssServiceImpl implements OssService {
|
||||
String url = null;
|
||||
Integer uploadType = configService.getConfig("upload", 1);
|
||||
|
||||
if (uploadType.equals(1)) {
|
||||
url = ConfigConstant.URL_BASE + "/admin/oss/upload/" + dir + "/" + uploadName; // 文件本地路径
|
||||
} else if (uploadType.equals(2)) {
|
||||
// oss 服务
|
||||
try {
|
||||
url = uploadObject2OSS(new File(uploadPath), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(uploadName));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("文件上传失败!", e);
|
||||
return null;
|
||||
}
|
||||
} else if (uploadType.equals(4)) {
|
||||
url = uploadObject4OSS(new File(uploadPath), TENGXUN_DEFAULT_DIR.concat("/").concat(dir).concat("/").concat(uploadName));
|
||||
}
|
||||
|
||||
String media_duration = "";
|
||||
try {
|
||||
media_duration = VideoUtil.getFormatDuration(uploadPath);
|
||||
} catch (IOException e) {
|
||||
throw new ApiException(String.format(I18nUtil._("解析音视频时长异常!url【%s】"), uploadPath));
|
||||
}
|
||||
|
||||
//截图
|
||||
//todo 放入upload中
|
||||
String thumb_file_url = "";
|
||||
if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) {
|
||||
String cover_path = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||
String cover_upname = uploadName.replace("." + VideoUtil.getVideoFormat(uploadName), ".jpg");
|
||||
|
||||
if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) {
|
||||
if (uploadType.equals(1)) {
|
||||
url = ConfigConstant.URL_BASE + "/admin/oss/upload/" + dir + "/" + uploadName; // 文件本地路径
|
||||
} else if (uploadType.equals(2)) {
|
||||
// oss 服务
|
||||
try {
|
||||
thumb_file_url = uploadObject2OSS(new File(cover_path), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat("/").concat(cover_upname));
|
||||
url = uploadObject2OSS(new File(uploadPath), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(uploadName));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("文件上传失败!", e);
|
||||
return null;
|
||||
}
|
||||
} else if (uploadType.equals(4)) {
|
||||
url = uploadObject4OSS(new File(uploadPath), TENGXUN_DEFAULT_DIR.concat("/").concat(dir).concat("/").concat(uploadName));
|
||||
}
|
||||
|
||||
String media_duration = "";
|
||||
try {
|
||||
media_duration = VideoUtil.getFormatDuration(uploadPath);
|
||||
} catch (IOException e) {
|
||||
throw new ApiException(String.format(I18nUtil._("解析音视频时长异常!url【%s】"), uploadPath));
|
||||
}
|
||||
|
||||
//截图
|
||||
//todo 放入upload中
|
||||
String thumb_file_url = "";
|
||||
if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) {
|
||||
String cover_path = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||
String cover_upname = uploadName.replace("." + VideoUtil.getVideoFormat(uploadName), ".jpg");
|
||||
|
||||
if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) {
|
||||
try {
|
||||
thumb_file_url = uploadObject2OSS(new File(cover_path), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat("/").concat(cover_upname));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
thumb_file_url = "";
|
||||
}
|
||||
} else {
|
||||
thumb_file_url = "";
|
||||
}
|
||||
} else {
|
||||
thumb_file_url = "";
|
||||
}
|
||||
|
||||
// 保存文件信息
|
||||
MediaDTO mediaDTO = new MediaDTO();
|
||||
String name = file.getOriginalFilename();
|
||||
long size = file.getSize();
|
||||
|
||||
mediaDTO.setMedia_name(name);
|
||||
mediaDTO.setMedia_alt(name);
|
||||
mediaDTO.setMedia_size(size);
|
||||
mediaDTO.setMedia_url(url);
|
||||
mediaDTO.setMedia_order(1);
|
||||
mediaDTO.setMedia_path("/".concat(dir).concat("/").concat(uploadName));
|
||||
mediaDTO.setMedia_domain(getOssUrlPrefix().concat(ALIYUN_OSS_DIR_PREFIX));
|
||||
mediaDTO.setMedia_time(new Date());
|
||||
mediaDTO.setMedia_desc("");
|
||||
mediaDTO.setMedia_duration(media_duration);
|
||||
|
||||
shopStoreMediaService.saveMedia(mediaDTO, user);
|
||||
Map result = new HashMap<>();
|
||||
result.put("media_duration", media_duration);
|
||||
result.put("media_url", url);
|
||||
result.put("thumb", thumb_file_url);
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
// 删除临时文件
|
||||
try {
|
||||
File tempFile = new File(uploadPath);
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
logger.info("临时文件已删除: {}", uploadPath);
|
||||
}
|
||||
|
||||
// 同时删除可能创建的封面文件
|
||||
String coverPath = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||
File coverFile = new File(coverPath);
|
||||
if (coverFile.exists()) {
|
||||
coverFile.delete();
|
||||
logger.info("临时封面文件已删除: {}", coverPath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("删除临时文件失败: {}", uploadPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文件信息
|
||||
MediaDTO mediaDTO = new MediaDTO();
|
||||
String name = file.getOriginalFilename();
|
||||
long size = file.getSize();
|
||||
|
||||
mediaDTO.setMedia_name(name);
|
||||
mediaDTO.setMedia_alt(name);
|
||||
mediaDTO.setMedia_size(size);
|
||||
mediaDTO.setMedia_url(url);
|
||||
mediaDTO.setMedia_order(1);
|
||||
mediaDTO.setMedia_path("/".concat(dir).concat("/").concat(fileName));
|
||||
|
||||
if (uploadType.equals(1)) {
|
||||
mediaDTO.setMedia_domain(ConfigConstant.URL_BASE);
|
||||
} else if (uploadType.equals(2)) {
|
||||
mediaDTO.setMedia_domain(getOssUrlPrefix().concat(ALIYUN_OSS_DIR_PREFIX));
|
||||
} else if (uploadType.equals(4)) {
|
||||
mediaDTO.setMedia_domain(TENGXUN_ENDPOINT.concat("/").concat(TENGXUN_DEFAULT_DIR));
|
||||
}
|
||||
mediaDTO.setMedia_time(new Date());
|
||||
mediaDTO.setMedia_desc("");
|
||||
mediaDTO.setMedia_duration(media_duration);
|
||||
|
||||
shopStoreMediaService.saveMedia(mediaDTO, user);
|
||||
Map result = new HashMap<>();
|
||||
result.put("media_duration", media_duration);
|
||||
result.put("media_url", url);
|
||||
result.put("thumb", thumb_file_url);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map upload(MultipartFile file, UserDto user) {
|
||||
|
||||
@ -88,12 +88,25 @@ public class CommonConstant {
|
||||
public static final String PUSH_MSG_CATE_MCH_ORDER_DETAIL = "mchOrderDetail";
|
||||
public static final String PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST = "mchOnLineOrderList";
|
||||
public static final String PUSH_MSG_CATE_MCH_ABNORMAL_ORDER_LIST = "mchAbnormalOrderList";
|
||||
public static final String PUSH_MSG_CATE_MCH_RETURN_ORDER_LIST = "mchRetrunOrderList";
|
||||
public static final String PUSH_MSG_CATE_MCH_RETURN_ORDER_LIST = "mchReturnOrderList";
|
||||
|
||||
public static final String CONF_KEY_SAME_CITY_ORDER_EXPIRE_SECONDS = "sameCityOrderExpireSeconds";
|
||||
|
||||
public static final String SPLIT_ = "diffCityOrderExpireSeconds";
|
||||
|
||||
// 订单分拆后 运费和商品子订单前缀
|
||||
public static final String Sep_DeliveryFee_Prefix = "DF_";
|
||||
public static final String Sep_GoodsFee_Prefix = "ORD_";
|
||||
|
||||
// 分账状态:1-已分账;2-未分账;3-分账已失败;
|
||||
public static final Integer Sta_Separate_Success = 1;
|
||||
public static final Integer Sta_Separate_Undone = 2;
|
||||
public static final Integer Sta_Separate_Fail = 3;
|
||||
|
||||
// 最低平台内部配送费 配置key
|
||||
public static final String Inner_Min_DeliveryFee_Key = "inner_min_delivery_fee";
|
||||
|
||||
|
||||
//秒杀活动订阅消息模板id
|
||||
public static final String BIND_SUB_TMPL_SKILL = "kiDj_hSF_ASwD-Dlgxnypi6IJBQZ12a-hEpd3zZ-Uxc";
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@ import org.springframework.stereotype.Component;
|
||||
@ToString
|
||||
public class ConfigConstant {
|
||||
|
||||
public static final String VERSION = "1.0.1"; //网站版本
|
||||
public static final String VERSION = "1.0.2"; //网站版本
|
||||
|
||||
public static final Integer MAX_LIST_NUM = -1; //最大页码
|
||||
|
||||
|
||||
@ -31,4 +31,11 @@ public class RedisConstant {
|
||||
public static final String Product_Cate_Key = ConstantRedis.Cache_NameSpace + "product_cate_Key";
|
||||
|
||||
public static final String Store_Brand_Key = ConstantRedis.Cache_NameSpace + "store_brand_key";
|
||||
|
||||
public static final String SF_Order_Proc_Expire_Key = ConstantRedis.Cache_NameSpace + "sf_order_proc_expire_key__";
|
||||
|
||||
public static final String SF_Order_Proc_WillExpire_Key = ConstantRedis.Cache_NameSpace + "sf_order_proc_will_expire_key__";
|
||||
|
||||
public static final String Order_Pay_Retry_Count_Key = ConstantRedis.Cache_NameSpace + "order_pay_retry_count:";
|
||||
|
||||
}
|
||||
|
||||
@ -6,11 +6,18 @@ import com.suisung.mall.common.api.ResultCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.validation.BindException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.ConstraintViolation;
|
||||
import javax.validation.ConstraintViolationException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 全局异常处理器
|
||||
@ -25,22 +32,60 @@ import java.sql.SQLException;
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
private static final String UNKNOWN_LOCATION = "未知位置";
|
||||
private static final String DB_ERROR_MSG = "数据库操作失败,请稍后重试";
|
||||
private static final String LOG_FORMAT = "业务异常 || URI={} || Method={} || Error={} || Location={}";
|
||||
|
||||
/**
|
||||
* 获取异常发生位置信息
|
||||
*
|
||||
* @param stackTrace 异常堆栈
|
||||
* @return 格式化的位置信息(类名.方法名 : 行号)
|
||||
* 处理参数校验异常
|
||||
*/
|
||||
private String getExceptionLocation(StackTraceElement[] stackTrace) {
|
||||
if (stackTrace == null || stackTrace.length == 0) {
|
||||
return UNKNOWN_LOCATION;
|
||||
@ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
|
||||
public CommonResult handleValidationException(Exception e, HttpServletRequest req) {
|
||||
String errorMessage;
|
||||
|
||||
if (e instanceof MethodArgumentNotValidException) {
|
||||
errorMessage = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
} else {
|
||||
errorMessage = ((BindException) e).getBindingResult().getFieldErrors().stream()
|
||||
.map(FieldError::getDefaultMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
}
|
||||
StackTraceElement first = stackTrace[0];
|
||||
return String.format("%s.%s:%d", first.getClassName(), first.getMethodName(), first.getLineNumber());
|
||||
|
||||
logError(req, "参数校验失败", e);
|
||||
return CommonResult.validateFailed("数据是否有误,请检查!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理约束违反异常
|
||||
*/
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
public CommonResult handleConstraintViolation(ConstraintViolationException e, HttpServletRequest req) {
|
||||
String errorMessage = e.getConstraintViolations().stream()
|
||||
.map(ConstraintViolation::getMessage)
|
||||
.collect(Collectors.joining("; "));
|
||||
|
||||
logError(req, "约束违反异常", e);
|
||||
return CommonResult.validateFailed("系统数据异常,请联系管理员!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理参数类型不匹配异常
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public CommonResult handleMethodArgumentTypeMismatch(MethodArgumentTypeMismatchException e, HttpServletRequest req) {
|
||||
String errorMessage = String.format("参数%s类型不匹配,需要%s类型,但接收到%s",
|
||||
e.getName(), e.getRequiredType().getSimpleName(), e.getValue());
|
||||
|
||||
logError(req, "参数类型不匹配", e);
|
||||
return CommonResult.validateFailed("参数类型不匹配");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理数字格式异常和其他参数异常
|
||||
*/
|
||||
@ExceptionHandler({NumberFormatException.class, IllegalArgumentException.class})
|
||||
public CommonResult handleParameterException(Exception e, HttpServletRequest req) {
|
||||
logError(req, "参数异常", e);
|
||||
return CommonResult.validateFailed("数据格式输入有误,请检查!");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,13 +93,12 @@ public class GlobalExceptionHandler {
|
||||
*/
|
||||
@ExceptionHandler({SQLException.class, DataAccessException.class, Exception.class})
|
||||
public CommonResult handleSystemException(HttpServletRequest req, Exception e) {
|
||||
String location = getExceptionLocation(e.getStackTrace());
|
||||
logger.error(LOG_FORMAT, req.getRequestURI(), req.getMethod(), e.getMessage(), location, e);
|
||||
logError(req, e.getMessage(), e);
|
||||
|
||||
if (e instanceof SQLException || e instanceof DataAccessException) {
|
||||
return CommonResult.failed(DB_ERROR_MSG);
|
||||
return CommonResult.failed("系统数据异常,请联系管理员!");
|
||||
}
|
||||
return CommonResult.failed(e.getMessage());
|
||||
return CommonResult.failed("系统内部异常,请联系管理员!");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -62,12 +106,22 @@ public class GlobalExceptionHandler {
|
||||
*/
|
||||
@ExceptionHandler(ApiException.class)
|
||||
public CommonResult handleApiException(HttpServletRequest req, ApiException e) {
|
||||
String location = getExceptionLocation(e.getStackTrace());
|
||||
logger.error(LOG_FORMAT,
|
||||
req.getRequestURI(), req.getMethod(), e.getErrorCode(), location, e);
|
||||
|
||||
logError(req, e.getErrorCode() != null ? e.getErrorCode().getMessage() : e.getMessage(), e);
|
||||
return StrUtil.isNotBlank(e.getMessage())
|
||||
? CommonResult.failed(e.getMessage())
|
||||
: CommonResult.failed(ResultCode.FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录错误日志
|
||||
*/
|
||||
private void logError(HttpServletRequest req, String message, Exception e) {
|
||||
StackTraceElement[] stackTrace = e.getStackTrace();
|
||||
String location = (stackTrace == null || stackTrace.length == 0)
|
||||
? "未知位置"
|
||||
: String.format("%s.%s:%d", stackTrace[0].getClassName(), stackTrace[0].getMethodName(), stackTrace[0].getLineNumber());
|
||||
|
||||
logger.error("业务异常 || URI={} || Method={} || Error={} || Location={}",
|
||||
req.getRequestURI(), req.getMethod(), message, location, e);
|
||||
}
|
||||
}
|
||||
@ -185,9 +185,9 @@ public interface PayService {
|
||||
/**
|
||||
* 更改交易订单的订单状态和付款状态
|
||||
*
|
||||
* @param orderId
|
||||
* @param orderStateId 空值或0将不更新
|
||||
* @param tradeIsPaid 空值或0将不更新
|
||||
* @param orderId 订单Id
|
||||
* @param orderStateId 订单状态,空值或0将不更新
|
||||
* @param tradeIsPaid 支付付款状态(ENUM):3010-未付款;3011-付款待审核;3012-部分付款;3013-已付款;空值或0将不更新
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/admin/pay/pay-consume-trade/change/state")
|
||||
|
||||
@ -311,6 +311,12 @@ public interface ShopService {
|
||||
boolean lklPayNotifyUpdateShopOrderLkl(@RequestBody JSONObject lklPayNotifyDataJson);
|
||||
|
||||
|
||||
/**
|
||||
* 终端cos上传文件
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
@PostMapping(value = "/mobile/shop/oss/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
||||
CommonResult uploadFile(@RequestPart(name = "upfile") MultipartFile file);
|
||||
|
||||
|
||||
@ -49,9 +49,6 @@ public class LklOrderSeparate {
|
||||
@ApiModelProperty(value = "平台订单Id")
|
||||
private String order_id;
|
||||
|
||||
@ApiModelProperty(value = "分账总金额,单位:分")
|
||||
private String total_amt;
|
||||
|
||||
@ApiModelProperty(value = "分账计算类型:0- 按照指定金额,1- 按照指定比例。默认 0")
|
||||
private String cal_type;
|
||||
|
||||
@ -79,8 +76,14 @@ public class LklOrderSeparate {
|
||||
@ApiModelProperty(value = "最终分账明细,JSON 格式")
|
||||
private String detail_datas;
|
||||
|
||||
@ApiModelProperty(value = "总计分账金额")
|
||||
private Integer total_separate_value;
|
||||
@ApiModelProperty(value = "分账发生总金额")
|
||||
private String total_amt;
|
||||
|
||||
@ApiModelProperty(value = "实际分账金额")
|
||||
private String actual_separate_amt;
|
||||
|
||||
@ApiModelProperty(value = "分账费用")
|
||||
private String total_fee_amt;
|
||||
|
||||
@ApiModelProperty(value = "拉卡拉机构编号")
|
||||
private String lkl_org_no;
|
||||
@ -91,6 +94,9 @@ public class LklOrderSeparate {
|
||||
@ApiModelProperty(value = "处理状态:ACCEPTED-已受理, PROCESSING-处理中, FAIL-失败, SUCCESS-成功")
|
||||
private String final_status;
|
||||
|
||||
@ApiModelProperty(value = "异步通知数据")
|
||||
private String notify_resp;
|
||||
|
||||
@ApiModelProperty(value = "分账(失败后)的标记")
|
||||
private String remark;
|
||||
|
||||
|
||||
@ -38,10 +38,18 @@ public class ShopOrderLkl implements Serializable {
|
||||
|
||||
private String order_id;
|
||||
|
||||
private String out_separate_no;
|
||||
|
||||
private Integer total_amt;
|
||||
|
||||
private Integer split_amt;
|
||||
|
||||
private Integer split_amt_ref;
|
||||
|
||||
private Integer shopping_fee;
|
||||
|
||||
private Integer shopping_fee_inner;
|
||||
|
||||
private BigDecimal split_ratio;
|
||||
|
||||
private String payment_time;
|
||||
@ -52,24 +60,44 @@ public class ShopOrderLkl implements Serializable {
|
||||
|
||||
private String trade_status;
|
||||
|
||||
private String lkl_trade_no;
|
||||
|
||||
private String lkl_log_no;
|
||||
|
||||
private String lkl_log_date;
|
||||
|
||||
private String lkl_trade_no;
|
||||
private String lkl_sub_trade_no;
|
||||
|
||||
private String lkl_sub_log_no;
|
||||
|
||||
private String lkl_receive_trade_no;
|
||||
|
||||
private String lkl_receive_log_no;
|
||||
|
||||
private String lkl_merchant_no;
|
||||
|
||||
private String lkl_term_no;
|
||||
|
||||
private String wx_transaction_id; // 微信用户交易单号, 确认收货时使用
|
||||
|
||||
private String notify_url;
|
||||
|
||||
private String receive_notify_url;
|
||||
|
||||
private String lkl_req;
|
||||
|
||||
private String lkl_resp;
|
||||
|
||||
private String lkl_notify_resp;
|
||||
|
||||
private String lkl_receive_notify_resp;
|
||||
|
||||
private Integer receive_status;
|
||||
|
||||
private Integer separate_status;
|
||||
|
||||
private String separate_remark;
|
||||
|
||||
private Integer status;
|
||||
|
||||
private Date created_at;
|
||||
|
||||
@ -78,13 +78,19 @@ public class ShopStoreBase implements Serializable {
|
||||
@ApiModelProperty(value = "店铺状态(ENUM):0-关闭; 1-运营中, 商品是否可用,需要判断该商品店铺状态")
|
||||
private Integer store_is_open;
|
||||
|
||||
@ApiModelProperty(value = "店铃声开关:1-开启;2-关闭;")
|
||||
private Integer ringtone_is_enable;
|
||||
|
||||
@ApiModelProperty(value = "店铺营业状态:1-营业中;2-已打烊;")
|
||||
private Integer store_biz_state;
|
||||
|
||||
@ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑")
|
||||
private Integer shop_parent_id;
|
||||
|
||||
@ApiModelProperty(value = "店铺分类编号")
|
||||
@ApiModelProperty(value = "店铺二级分类编号")
|
||||
private Integer store_2nd_category_id;
|
||||
|
||||
@ApiModelProperty(value = "店铺一级分类编号")
|
||||
private Integer store_category_id;
|
||||
|
||||
@ApiModelProperty(value = "店铺资料信息状态(ENUM):3210-待完善资料; 3220-等待审核; 3230-资料审核没有通过; 3240-资料审核通过,待付款")
|
||||
|
||||
@ -0,0 +1,756 @@
|
||||
package com.suisung.mall.common.pojo.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 拉卡拉订单分账信息处理类
|
||||
* 实现基于总金额和基于可分账金额的分账算法,支持平台、代理商和商家的多级分账
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
public class LklSeparateDTO {
|
||||
|
||||
// 基础金额属性
|
||||
private Integer totalSeparateAmount; // 分账总金额(分)
|
||||
private Integer canSeparateAmount; // 可分账金额(分)
|
||||
private Integer refCanSeparateAmount; // 拉卡拉参考可分账金额(分)
|
||||
private Integer shippingFee; // 配送费(分)
|
||||
|
||||
// 分账比例属性
|
||||
private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.25%)
|
||||
private BigDecimal mchRatio; // 商户分账比例(如 0.96=96%)
|
||||
private BigDecimal platRatio; // 平台分账比例(如 0.01=1%)
|
||||
private BigDecimal agent1stRatio; // 一级代理商分账比例(如 0.01=1%)
|
||||
private BigDecimal agent2ndRatio; // 二级代理商分账比例(如 0.03=3%)
|
||||
|
||||
// 分账金额结果属性
|
||||
private Integer lklAmount; // 拉卡拉分账金额(分)
|
||||
private Integer mchAmount; // 商户分账金额(分)
|
||||
private Integer platAmount; // 平台分账金额(分)
|
||||
private Integer agent1stAmount; // 一级代理商分账金额(分)
|
||||
private Integer agent2ndAmount; // 二级代理商分账金额(分)
|
||||
|
||||
/**
|
||||
* 测试方法
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// 创建分账对象
|
||||
LklSeparateDTO dto = new LklSeparateDTO();
|
||||
|
||||
// 设置测试参数 - 正常情况
|
||||
dto.setTotalSeparateAmount(1000); // 分账总额 1000分
|
||||
dto.setShippingFee(100); // 配送费 100分
|
||||
dto.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025
|
||||
dto.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.94
|
||||
dto.setPlatRatio(new BigDecimal("0.06")); // 平台分账比例 0.01
|
||||
// dto.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.01
|
||||
// dto.setAgent2ndRatio(new BigDecimal("0.01")); // 二级代理商分账比例 0.01
|
||||
|
||||
// 执行基于总金额的分账算法
|
||||
System.out.println("=== 基于总金额的分账算法测试(正常情况) ===");
|
||||
SharingResult result1 = dto.sharingOnTotalAmount();
|
||||
System.out.println(result1);
|
||||
if (result1.isSuccess()) {
|
||||
printSharingDetails(dto, "基于总金额");
|
||||
}
|
||||
|
||||
// 测试基于可分账金额的分账算法(正常情况)
|
||||
System.out.println("\n=== 基于可分账金额的分账算法测试(正常情况) ===");
|
||||
LklSeparateDTO dto2 = new LklSeparateDTO();
|
||||
dto2.setTotalSeparateAmount(1); // 分账总额 1000分
|
||||
dto2.setShippingFee(0); // 配送费 100分
|
||||
// dto2.setRefCanSeparateAmount(null);
|
||||
dto2.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例 0.0025
|
||||
dto2.setMchRatio(new BigDecimal("0.94")); // 商家分账比例 0.857 (会产生小数)
|
||||
dto2.setPlatRatio(new BigDecimal("0.01")); // 平台分账比例 0.01
|
||||
// 不设置一级和二级代理商分账比例,测试不参与分账的情况
|
||||
// dto2.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例 0.023 (会产生小数)
|
||||
// dto2.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例 0.031 (会产生小数)
|
||||
|
||||
SharingResult result2 = dto2.sharingOnCanSeparateAmount();
|
||||
System.out.println(result2);
|
||||
if (result2.isSuccess()) {
|
||||
printSharingDetails(dto2, "基于可分账金额");
|
||||
}
|
||||
|
||||
// 测试toJSON和toString方法
|
||||
System.out.println("\n=== toJSON和toString方法测试 ===");
|
||||
System.out.println("基于可分账金额算法的JSON输出:");
|
||||
System.out.println(dto2.toJSON());
|
||||
|
||||
System.out.println("\n基于可分账金额算法的toString输出:");
|
||||
System.out.println(dto2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算百分比
|
||||
*
|
||||
* @param amount 金额
|
||||
* @param total 总金额
|
||||
* @return 百分比字符串,保留两位小数
|
||||
*/
|
||||
public static String calculatePercentage(Integer amount, Integer total) {
|
||||
if (amount == null || total == null || total == 0) {
|
||||
return "0.00";
|
||||
}
|
||||
BigDecimal percentage = new BigDecimal(amount).multiply(new BigDecimal("100"))
|
||||
.divide(new BigDecimal(total), 2, RoundingMode.HALF_UP);
|
||||
return percentage.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印分账详情
|
||||
*
|
||||
* @param dto 分账对象
|
||||
* @param type 分账类型
|
||||
*/
|
||||
private static void printSharingDetails(LklSeparateDTO dto, String type) {
|
||||
System.out.println(type + "分账详情:");
|
||||
if (dto.getTotalSeparateAmount() != null) {
|
||||
System.out.println(" 分账总金额: " + dto.getTotalSeparateAmount() + "分");
|
||||
}
|
||||
// 打印拉卡拉分账金额(即使为0也要显示)
|
||||
if (dto.getLklAmount() != null) {
|
||||
System.out.println(" 拉卡拉分账金额: " + dto.getLklAmount() + "分" +
|
||||
" (占比: " + calculatePercentage(dto.getLklAmount(), dto.getTotalSeparateAmount()) + "%)");
|
||||
}
|
||||
// 打印配送费(即使为0也要显示)
|
||||
if (dto.getShippingFee() != null) {
|
||||
System.out.println(" 配送费: " + dto.getShippingFee() + "分" +
|
||||
" (占比: " + calculatePercentage(dto.getShippingFee(), dto.getTotalSeparateAmount()) + "%)");
|
||||
}
|
||||
if (dto.getCanSeparateAmount() != null) {
|
||||
System.out.println(" 可分账金额: " + dto.getCanSeparateAmount() + "分");
|
||||
}
|
||||
if (dto.getRefCanSeparateAmount() != null) {
|
||||
System.out.println(" 参考可分账金额: " + dto.getRefCanSeparateAmount() + "分");
|
||||
}
|
||||
if (dto.getCanSeparateAmount() != null && dto.getRefCanSeparateAmount() != null) {
|
||||
System.out.println(" 参考可分账差值: " + (dto.getRefCanSeparateAmount() - dto.getCanSeparateAmount()) + "分");
|
||||
}
|
||||
if (dto.getPlatAmount() != null) {
|
||||
System.out.println(" 平台分账金额: " + dto.getPlatAmount() + "分" +
|
||||
(dto.getTotalSeparateAmount() != null ?
|
||||
" (总金额占比: " + calculatePercentage(dto.getPlatAmount(), dto.getTotalSeparateAmount()) + "%)" : "") +
|
||||
(dto.getCanSeparateAmount() != null && dto.getCanSeparateAmount() > 0 ?
|
||||
" (可分账占比: " + calculatePercentage(dto.getPlatAmount(), dto.getCanSeparateAmount()) + "%)" : ""));
|
||||
}
|
||||
if (dto.getAgent1stAmount() != null) {
|
||||
System.out.println(" 一级代理商分账金额: " + dto.getAgent1stAmount() + "分" +
|
||||
(dto.getTotalSeparateAmount() != null ?
|
||||
" (总金额占比: " + calculatePercentage(dto.getAgent1stAmount(), dto.getTotalSeparateAmount()) + "%)" : "") +
|
||||
(dto.getCanSeparateAmount() != null && dto.getCanSeparateAmount() > 0 ?
|
||||
" (可分账占比: " + calculatePercentage(dto.getAgent1stAmount(), dto.getCanSeparateAmount()) + "%)" : ""));
|
||||
}
|
||||
if (dto.getAgent2ndAmount() != null) {
|
||||
System.out.println(" 二级代理商分账金额: " + dto.getAgent2ndAmount() + "分" +
|
||||
(dto.getTotalSeparateAmount() != null ?
|
||||
" (总金额占比: " + calculatePercentage(dto.getAgent2ndAmount(), dto.getTotalSeparateAmount()) + "%)" : "") +
|
||||
(dto.getCanSeparateAmount() != null && dto.getCanSeparateAmount() > 0 ?
|
||||
" (可分账占比: " + calculatePercentage(dto.getAgent2ndAmount(), dto.getCanSeparateAmount()) + "%)" : ""));
|
||||
}
|
||||
if (dto.getMchAmount() != null) {
|
||||
System.out.println(" 商家分账金额: " + dto.getMchAmount() + "分" +
|
||||
(dto.getTotalSeparateAmount() != null ?
|
||||
" (总金额占比: " + calculatePercentage(dto.getMchAmount(), dto.getTotalSeparateAmount()) + "%)" : "") +
|
||||
(dto.getCanSeparateAmount() != null && dto.getCanSeparateAmount() > 0 ?
|
||||
" (可分账占比: " + calculatePercentage(dto.getMchAmount(), dto.getCanSeparateAmount()) + "%)" : ""));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账算法 - 基于总金额进行分账
|
||||
*
|
||||
* @return SharingResult 分账结果,包含是否成功和失败原因
|
||||
*/
|
||||
public SharingResult sharingOnTotalAmount() {
|
||||
// 执行基于总金额的分账计算
|
||||
SharingCalculationResult result = calculateBasedOnTotalAmount();
|
||||
|
||||
if (result.isSuccess()) {
|
||||
// 更新实例变量
|
||||
this.lklAmount = result.getLklAmount();
|
||||
this.canSeparateAmount = result.getCanSeparateAmount();
|
||||
this.refCanSeparateAmount = result.getRefCanSeparateAmount();
|
||||
this.shippingFee = result.getShippingFee();
|
||||
this.platAmount = result.getPlatAmount();
|
||||
this.agent1stAmount = result.getAgent1stAmount();
|
||||
this.agent2ndAmount = result.getAgent2ndAmount();
|
||||
this.mchAmount = result.getMchAmount();
|
||||
return new SharingResult(true, null);
|
||||
} else {
|
||||
return new SharingResult(false, result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于总金额的分账计算(独立算法)
|
||||
*
|
||||
* @return SharingCalculationResult 计算结果
|
||||
*/
|
||||
private SharingCalculationResult calculateBasedOnTotalAmount() {
|
||||
// 1. 参数校验
|
||||
if (totalSeparateAmount == null || totalSeparateAmount <= 0) {
|
||||
return new SharingCalculationResult(false, "分账总金额不能为空或小于等于0");
|
||||
}
|
||||
|
||||
// 2. 计算拉卡拉分账金额
|
||||
int lklAmount = calculateAmountWithRatio(lklRatio, totalSeparateAmount, RoundingMode.HALF_UP);
|
||||
|
||||
// 3. 处理配送费(可以为空或0,表示免配送费)
|
||||
int effectiveShippingFee = (shippingFee != null && shippingFee > 0) ? shippingFee : 0;
|
||||
|
||||
// 4. 检查配送费是否合法(不能大于等于可分账金额)
|
||||
if (effectiveShippingFee >= totalSeparateAmount - lklAmount) {
|
||||
return new SharingCalculationResult(false, "配送费不能大于等于可分账金额");
|
||||
}
|
||||
|
||||
// 5. 计算可分账金额(总金额 - 拉卡拉分账金额 - 配送费)
|
||||
int availableAmount = totalSeparateAmount - lklAmount - effectiveShippingFee;
|
||||
if (availableAmount < 0) {
|
||||
return new SharingCalculationResult(false, "可分账金额不能为负数");
|
||||
}
|
||||
|
||||
int canSeparateAmount = availableAmount;
|
||||
// 保持canSeparateAmount和refCanSeparateAmount的独立性,不进行相互赋值
|
||||
|
||||
// 记录refCanSeparateAmount相关信息
|
||||
System.out.println("基于总金额算法 - 可分账金额计算完成: canSeparateAmount=" + canSeparateAmount +
|
||||
", refCanSeparateAmount=" + this.refCanSeparateAmount);
|
||||
|
||||
// 5.1 检查refCanSeparateAmount是否为有效值,如果是有效值且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账金额
|
||||
int separateAmountForCalculation = canSeparateAmount; // 默认使用计算出的可分账金额
|
||||
if (this.refCanSeparateAmount != null && this.refCanSeparateAmount > 0 && this.refCanSeparateAmount < totalSeparateAmount) {
|
||||
// refCanSeparateAmount是非空非零且比totalSeparateAmount小的有效值
|
||||
if (!this.refCanSeparateAmount.equals(canSeparateAmount)) {
|
||||
// 有效值如果和计算出来的canSeparateAmount的值不一致
|
||||
separateAmountForCalculation = this.refCanSeparateAmount; // 使用refCanSeparateAmount作为分账算法中的可分金额
|
||||
System.out.println("基于总金额算法 - 使用refCanSeparateAmount作为分账金额: refCanSeparateAmount=" +
|
||||
this.refCanSeparateAmount + ", canSeparateAmount=" + canSeparateAmount +
|
||||
", 差值=" + (this.refCanSeparateAmount - canSeparateAmount));
|
||||
} else {
|
||||
System.out.println("基于总金额算法 - refCanSeparateAmount与canSeparateAmount相等,无需调整");
|
||||
}
|
||||
} else {
|
||||
System.out.println("基于总金额算法 - refCanSeparateAmount无效,使用计算出的canSeparateAmount");
|
||||
}
|
||||
|
||||
// 6. 初始化各分账金额
|
||||
int platAmount = 0;
|
||||
int agent1stAmount = 0;
|
||||
int agent2ndAmount = 0;
|
||||
int mchAmount = 0;
|
||||
|
||||
// 7. 根据参与方动态调整分账比例(基于总金额的独立逻辑)
|
||||
BigDecimal effectivePlatRatio = platRatio != null ? platRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveAgent1stRatio = agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveAgent2ndRatio = agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveMchRatio = mchRatio != null ? mchRatio : BigDecimal.ZERO;
|
||||
|
||||
// 判断代理商是否参与分账(在调整比例前进行判断)
|
||||
boolean hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
boolean hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
|
||||
// 根据商家分账比例动态调整其他方比例(独立于基于可分账金额的算法)
|
||||
if (effectiveMchRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal remainingRatio = BigDecimal.ONE.subtract(effectiveMchRatio);
|
||||
BigDecimal onePercent = new BigDecimal("0.01");
|
||||
|
||||
if (hasAgent1st && hasAgent2nd) {
|
||||
// 平台、一级代理商、二级代理商都参与
|
||||
// 平台固定1%,一级代理商固定1%,二级代理商为剩余比例
|
||||
effectivePlatRatio = onePercent;
|
||||
effectiveAgent1stRatio = onePercent;
|
||||
effectiveAgent2ndRatio = remainingRatio.subtract(onePercent).subtract(onePercent);
|
||||
} else if (hasAgent1st || hasAgent2nd) {
|
||||
// 只有平台和一个代理商参与
|
||||
effectivePlatRatio = onePercent;
|
||||
// 代理商为剩余比例
|
||||
if (hasAgent1st) {
|
||||
effectiveAgent1stRatio = remainingRatio.subtract(onePercent);
|
||||
} else {
|
||||
effectiveAgent2ndRatio = remainingRatio.subtract(onePercent);
|
||||
}
|
||||
} else {
|
||||
// 只有平台参与
|
||||
effectivePlatRatio = remainingRatio;
|
||||
}
|
||||
|
||||
// 重新判断代理商是否参与分账(基于调整后的比例)
|
||||
hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
// 如果商家比例不存在或为0,使用各自设置的固定比例,代理商参与状态已在前面确定
|
||||
|
||||
// 8. 按优先级顺序计算各分账方金额
|
||||
// 平台分账(优先级1)- 基于总金额计算
|
||||
if (effectivePlatRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
platAmount = calculateAmountWithRatio(effectivePlatRatio, totalSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保平台分账金额不超过可分账金额
|
||||
if (platAmount > separateAmountForCalculation) {
|
||||
platAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= platAmount;
|
||||
}
|
||||
|
||||
// 一级代理商分账(优先级2)- 基于总金额计算
|
||||
if (hasAgent1st && effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
agent1stAmount = calculateAmountWithRatio(effectiveAgent1stRatio, totalSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保不超过剩余金额
|
||||
if (agent1stAmount > separateAmountForCalculation) {
|
||||
agent1stAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= agent1stAmount;
|
||||
}
|
||||
|
||||
// 二级代理商分账(优先级3)- 基于总金额计算
|
||||
if (hasAgent2nd && effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
agent2ndAmount = calculateAmountWithRatio(effectiveAgent2ndRatio, totalSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保不超过剩余金额
|
||||
if (agent2ndAmount > separateAmountForCalculation) {
|
||||
agent2ndAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= agent2ndAmount;
|
||||
}
|
||||
|
||||
// 9. 商家分账(优先级4,获得剩余所有金额)
|
||||
mchAmount = separateAmountForCalculation;
|
||||
|
||||
// 10. 检查商家分账金额是否合法
|
||||
if (mchAmount < 0) {
|
||||
return new SharingCalculationResult(false, "商家分账金额不能为负数");
|
||||
}
|
||||
|
||||
// 创建并返回计算结果
|
||||
SharingCalculationResult calculationResult = new SharingCalculationResult(true, null);
|
||||
calculationResult.setLklAmount(lklAmount);
|
||||
calculationResult.setCanSeparateAmount(canSeparateAmount);
|
||||
calculationResult.setRefCanSeparateAmount(this.refCanSeparateAmount);
|
||||
calculationResult.setShippingFee(effectiveShippingFee);
|
||||
calculationResult.setPlatAmount(platAmount);
|
||||
calculationResult.setAgent1stAmount(agent1stAmount);
|
||||
calculationResult.setAgent2ndAmount(agent2ndAmount);
|
||||
calculationResult.setMchAmount(mchAmount);
|
||||
|
||||
return calculationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账算法 - 基于可分账金额进行分账
|
||||
*
|
||||
* @return SharingResult 分账结果,包含是否成功和失败原因
|
||||
*/
|
||||
public SharingResult sharingOnCanSeparateAmount() {
|
||||
// 执行基于可分账金额的分账计算
|
||||
SharingCalculationResult result = calculateBasedOnCanSeparateAmount();
|
||||
|
||||
if (result.isSuccess()) {
|
||||
// 更新实例变量
|
||||
this.lklAmount = result.getLklAmount();
|
||||
this.canSeparateAmount = result.getCanSeparateAmount();
|
||||
this.refCanSeparateAmount = result.getRefCanSeparateAmount();
|
||||
this.shippingFee = result.getShippingFee();
|
||||
this.platAmount = result.getPlatAmount();
|
||||
this.agent1stAmount = result.getAgent1stAmount();
|
||||
this.agent2ndAmount = result.getAgent2ndAmount();
|
||||
this.mchAmount = result.getMchAmount();
|
||||
return new SharingResult(true, null);
|
||||
} else {
|
||||
return new SharingResult(false, result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于可分账金额的分账计算(独立算法)
|
||||
*
|
||||
* @return SharingCalculationResult 计算结果
|
||||
*/
|
||||
private SharingCalculationResult calculateBasedOnCanSeparateAmount() {
|
||||
// 1. 参数校验
|
||||
if (totalSeparateAmount == null || totalSeparateAmount <= 0) {
|
||||
return new SharingCalculationResult(false, "分账总金额不能为空或小于等于0");
|
||||
}
|
||||
|
||||
// 2. 先扣除配送费(可以为空或0,表示免配送费)
|
||||
int effectiveShippingFee = (shippingFee != null && shippingFee > 0) ? shippingFee : 0;
|
||||
if (effectiveShippingFee >= totalSeparateAmount) {
|
||||
return new SharingCalculationResult(false, "配送费不能大于等于分账总金额");
|
||||
}
|
||||
|
||||
// 3. 计算扣除配送费后的金额
|
||||
int amountAfterShipping = totalSeparateAmount - effectiveShippingFee;
|
||||
|
||||
// 4. 计算拉卡拉分账金额(手续费)= 分账总金额 * 拉卡拉分账比例
|
||||
int lklAmount = calculateAmountWithRatio(lklRatio, totalSeparateAmount, RoundingMode.HALF_UP);
|
||||
|
||||
// 5. 检查拉卡拉分账金额是否合法
|
||||
if (lklAmount >= amountAfterShipping) {
|
||||
return new SharingCalculationResult(false, "拉卡拉分账金额不能大于等于扣除配送费后的金额");
|
||||
}
|
||||
|
||||
// 6. 计算可分账金额 = 分账总金额 - 配送费 - 拉卡拉分账金额
|
||||
int availableAmount = amountAfterShipping - lklAmount;
|
||||
if (availableAmount < 0) {
|
||||
return new SharingCalculationResult(false, "可分账金额不能为负数");
|
||||
}
|
||||
|
||||
int canSeparateAmount = availableAmount;
|
||||
// 记录refCanSeparateAmount相关信息
|
||||
System.out.println("基于可分账金额算法 - 可分账金额计算完成: canSeparateAmount=" + canSeparateAmount +
|
||||
", refCanSeparateAmount=" + this.refCanSeparateAmount);
|
||||
|
||||
// 6.1 检查refCanSeparateAmount是否为有效值,如果是有效值且与计算出的canSeparateAmount不一致,则使用refCanSeparateAmount作为分账金额
|
||||
int separateAmountForCalculation = canSeparateAmount; // 默认使用计算出的可分账金额
|
||||
if (this.refCanSeparateAmount != null && this.refCanSeparateAmount > 0 && this.refCanSeparateAmount < totalSeparateAmount) {
|
||||
// refCanSeparateAmount是非空非零且比totalSeparateAmount小的有效值
|
||||
if (!this.refCanSeparateAmount.equals(canSeparateAmount)) {
|
||||
// 有效值如果和计算出来的canSeparateAmount的值不一致
|
||||
separateAmountForCalculation = this.refCanSeparateAmount; // 使用refCanSeparateAmount作为分账算法中的可分金额
|
||||
System.out.println("基于可分账金额算法 - 使用refCanSeparateAmount作为分账金额: refCanSeparateAmount=" +
|
||||
this.refCanSeparateAmount + ", canSeparateAmount=" + canSeparateAmount +
|
||||
", 差值=" + (this.refCanSeparateAmount - canSeparateAmount));
|
||||
} else {
|
||||
System.out.println("基于可分账金额算法 - refCanSeparateAmount与canSeparateAmount相等,无需调整");
|
||||
}
|
||||
} else {
|
||||
System.out.println("基于可分账金额算法 - refCanSeparateAmount无效,使用计算出的canSeparateAmount");
|
||||
}
|
||||
|
||||
// 7. 初始化各分账金额
|
||||
int platAmount = 0;
|
||||
int agent1stAmount = 0;
|
||||
int agent2ndAmount = 0;
|
||||
int mchAmount = 0;
|
||||
|
||||
// 8. 根据参与方动态调整分账比例(基于可分账金额的独立逻辑)
|
||||
BigDecimal effectivePlatRatio = platRatio != null ? platRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveAgent1stRatio = agent1stRatio != null ? agent1stRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveAgent2ndRatio = agent2ndRatio != null ? agent2ndRatio : BigDecimal.ZERO;
|
||||
BigDecimal effectiveMchRatio = mchRatio != null ? mchRatio : BigDecimal.ZERO;
|
||||
|
||||
// 判断代理商是否参与分账(在调整比例前进行判断)
|
||||
boolean hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
boolean hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
|
||||
// 根据商家分账比例动态调整其他方比例(独立于基于总金额的算法)
|
||||
if (effectiveMchRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal remainingRatio = BigDecimal.ONE.subtract(effectiveMchRatio);
|
||||
BigDecimal onePercent = new BigDecimal("0.01");
|
||||
|
||||
if (hasAgent1st && hasAgent2nd) {
|
||||
// 平台、一级代理商、二级代理商都参与
|
||||
// 平台固定1%,一级代理商固定1%,二级代理商为剩余比例
|
||||
effectivePlatRatio = onePercent;
|
||||
effectiveAgent1stRatio = onePercent;
|
||||
effectiveAgent2ndRatio = remainingRatio.subtract(onePercent).subtract(onePercent);
|
||||
} else if (hasAgent1st || hasAgent2nd) {
|
||||
// 只有平台和一个代理商参与
|
||||
effectivePlatRatio = onePercent;
|
||||
// 代理商为剩余比例
|
||||
if (hasAgent1st) {
|
||||
effectiveAgent1stRatio = remainingRatio.subtract(onePercent);
|
||||
} else {
|
||||
effectiveAgent2ndRatio = remainingRatio.subtract(onePercent);
|
||||
}
|
||||
} else {
|
||||
// 只有平台参与
|
||||
effectivePlatRatio = remainingRatio;
|
||||
}
|
||||
|
||||
// 重新判断代理商是否参与分账(基于调整后的比例)
|
||||
hasAgent1st = effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
hasAgent2nd = effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0;
|
||||
}
|
||||
// 如果商家比例不存在或为0,使用各自设置的固定比例,代理商参与状态已在前面确定
|
||||
|
||||
// 9. 按新的优先级顺序计算各分账方金额(基于可分账金额计算)
|
||||
// 商家分账(优先级1)- 基于可分账金额计算,采用向上进位方式
|
||||
if (effectiveMchRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
mchAmount = calculateAmountWithRatio(effectiveMchRatio, canSeparateAmount, RoundingMode.UP);
|
||||
// 确保商家分账金额不超过剩余金额
|
||||
if (mchAmount > separateAmountForCalculation) {
|
||||
mchAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= mchAmount;
|
||||
}
|
||||
|
||||
// 平台分账(优先级2)- 基于可分账金额计算,采用四舍五入方式
|
||||
if (effectivePlatRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
platAmount = calculateAmountWithRatio(effectivePlatRatio, canSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保平台分账金额不超过剩余金额
|
||||
if (platAmount > separateAmountForCalculation) {
|
||||
platAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= platAmount;
|
||||
}
|
||||
|
||||
// 一级代理商分账(优先级3)- 基于可分账金额计算,采用四舍五入方式
|
||||
if (hasAgent1st && effectiveAgent1stRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
agent1stAmount = calculateAmountWithRatio(effectiveAgent1stRatio, canSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保不超过剩余金额
|
||||
if (agent1stAmount > separateAmountForCalculation) {
|
||||
agent1stAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= agent1stAmount;
|
||||
}
|
||||
|
||||
// 二级代理商分账(优先级4)- 基于可分账金额计算,采用四舍五入方式
|
||||
if (hasAgent2nd && effectiveAgent2ndRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
agent2ndAmount = calculateAmountWithRatio(effectiveAgent2ndRatio, canSeparateAmount, RoundingMode.HALF_UP);
|
||||
// 确保不超过剩余金额
|
||||
if (agent2ndAmount > separateAmountForCalculation) {
|
||||
agent2ndAmount = separateAmountForCalculation;
|
||||
}
|
||||
separateAmountForCalculation -= agent2ndAmount;
|
||||
}
|
||||
|
||||
// 10. 如果还有剩余金额,将其分配给商家
|
||||
if (separateAmountForCalculation > 0) {
|
||||
mchAmount += separateAmountForCalculation;
|
||||
}
|
||||
|
||||
// 11. 检查商家分账金额是否合法
|
||||
if (mchAmount < 0) {
|
||||
return new SharingCalculationResult(false, "商家分账金额不能为负数");
|
||||
}
|
||||
|
||||
// 创建并返回计算结果
|
||||
SharingCalculationResult calculationResult = new SharingCalculationResult(true, null);
|
||||
calculationResult.setLklAmount(lklAmount);
|
||||
calculationResult.setCanSeparateAmount(canSeparateAmount);
|
||||
calculationResult.setRefCanSeparateAmount(this.refCanSeparateAmount);
|
||||
calculationResult.setShippingFee(effectiveShippingFee);
|
||||
calculationResult.setPlatAmount(platAmount);
|
||||
calculationResult.setAgent1stAmount(agent1stAmount);
|
||||
calculationResult.setAgent2ndAmount(agent2ndAmount);
|
||||
calculationResult.setMchAmount(mchAmount);
|
||||
|
||||
return calculationResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将分账信息转换为格式化的JSON字符串
|
||||
*
|
||||
* @return JSON格式的字符串
|
||||
*/
|
||||
public String toJSON() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("{");
|
||||
|
||||
// 基础金额信息
|
||||
sb.append("基础金额信息:{");
|
||||
sb.append("分账总金额:").append(totalSeparateAmount != null ? totalSeparateAmount : 0).append("分,");
|
||||
sb.append("配送费:").append(shippingFee != null ? shippingFee : 0);
|
||||
sb.append("},");
|
||||
|
||||
// 分账比例信息
|
||||
sb.append("分账比例信息:{");
|
||||
sb.append("拉卡拉分账比例:").append(lklRatio != null ? lklRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("商家分账比例:").append(mchRatio != null ? mchRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("平台分账比例:").append(platRatio != null ? platRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("一级代理商分账比例:").append(agent1stRatio != null ? agent1stRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("二级代理商分账比例:").append(agent2ndRatio != null ? agent2ndRatio.stripTrailingZeros().toPlainString() : "0");
|
||||
sb.append("},");
|
||||
|
||||
// 分账金额结果
|
||||
sb.append("分账金额结果:{");
|
||||
sb.append("拉卡拉分账金额:").append(lklAmount != null ? lklAmount : 0).append("分,");
|
||||
sb.append("可分账金额:").append(canSeparateAmount != null ? canSeparateAmount : 0).append("分,");
|
||||
sb.append("参考可分账金额:").append(refCanSeparateAmount != null ? refCanSeparateAmount : 0).append("分,");
|
||||
if (canSeparateAmount != null && refCanSeparateAmount != null) {
|
||||
sb.append("参考可分账差值:").append(refCanSeparateAmount - canSeparateAmount).append("分,");
|
||||
}
|
||||
sb.append("配送费:").append(shippingFee != null ? shippingFee : 0).append("分,");
|
||||
sb.append("平台分账金额:").append(platAmount != null ? platAmount : 0).append("分,");
|
||||
sb.append("一级代理商分账金额:").append(agent1stAmount != null ? agent1stAmount : 0).append("分,");
|
||||
sb.append("二级代理商分账金额:").append(agent2ndAmount != null ? agent2ndAmount : 0).append("分,");
|
||||
sb.append("商家分账金额:").append(mchAmount != null ? mchAmount : 0).append("分");
|
||||
sb.append("}");
|
||||
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据比例计算金额
|
||||
*
|
||||
* @param ratio 比例
|
||||
* @param baseAmount 基础金额
|
||||
* @param roundingMode 舍入模式
|
||||
* @return 计算后的金额
|
||||
*/
|
||||
private int calculateAmountWithRatio(BigDecimal ratio, int baseAmount, RoundingMode roundingMode) {
|
||||
if (ratio == null || ratio.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return ratio.multiply(new BigDecimal(baseAmount))
|
||||
.setScale(0, roundingMode).intValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("LklSeparateDTO{");
|
||||
|
||||
// 基础金额信息
|
||||
sb.append("基础金额信息:{");
|
||||
sb.append("分账总金额:").append(totalSeparateAmount != null ? totalSeparateAmount : 0).append("分,");
|
||||
sb.append("配送费:").append(shippingFee != null ? shippingFee : 0);
|
||||
sb.append("},");
|
||||
|
||||
// 分账比例信息
|
||||
sb.append("分账比例信息:{");
|
||||
sb.append("拉卡拉分账比例:").append(lklRatio != null ? lklRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("商家分账比例:").append(mchRatio != null ? mchRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("平台分账比例:").append(platRatio != null ? platRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("一级代理商分账比例:").append(agent1stRatio != null ? agent1stRatio.stripTrailingZeros().toPlainString() : "0").append(",");
|
||||
sb.append("二级代理商分账比例:").append(agent2ndRatio != null ? agent2ndRatio.stripTrailingZeros().toPlainString() : "0");
|
||||
sb.append("},");
|
||||
|
||||
// 分账金额结果
|
||||
sb.append("分账金额结果:{");
|
||||
sb.append("拉卡拉分账金额:").append(lklAmount != null ? lklAmount : 0).append("分,");
|
||||
sb.append("可分账金额:").append(canSeparateAmount != null ? canSeparateAmount : 0).append("分,");
|
||||
sb.append("参考可分账金额:").append(refCanSeparateAmount != null ? refCanSeparateAmount : 0).append("分,");
|
||||
if (canSeparateAmount != null && refCanSeparateAmount != null) {
|
||||
sb.append("参考可分账差值:").append(refCanSeparateAmount - canSeparateAmount).append("分,");
|
||||
}
|
||||
sb.append("配送费:").append(shippingFee != null ? shippingFee : 0).append("分,");
|
||||
sb.append("平台分账金额:").append(platAmount != null ? platAmount : 0).append("分,");
|
||||
sb.append("一级代理商分账金额:").append(agent1stAmount != null ? agent1stAmount : 0).append("分,");
|
||||
sb.append("二级代理商分账金额:").append(agent2ndAmount != null ? agent2ndAmount : 0).append("分,");
|
||||
sb.append("商家分账金额:").append(mchAmount != null ? mchAmount : 0).append("分");
|
||||
sb.append("}");
|
||||
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账结果类,包含分账是否成功以及失败原因
|
||||
*/
|
||||
public static class SharingResult {
|
||||
private boolean success;
|
||||
private String errorMessage;
|
||||
|
||||
public SharingResult() {
|
||||
}
|
||||
|
||||
public SharingResult(boolean success, String errorMessage) {
|
||||
this.success = success;
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean success) {
|
||||
this.success = success;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return success ? "分账成功" : "分账失败: " + errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 分账计算结果类,包含分账计算的详细结果
|
||||
*/
|
||||
public static class SharingCalculationResult extends SharingResult {
|
||||
// 分账金额结果属性
|
||||
private Integer lklAmount; // 拉卡拉分账金额(分)
|
||||
private Integer canSeparateAmount; // 可分账金额(分)
|
||||
private Integer refCanSeparateAmount; // 拉卡拉参考可分账金额(分)
|
||||
private Integer shippingFee; // 配送费(分)
|
||||
private Integer platAmount; // 平台分账金额(分)
|
||||
private Integer agent1stAmount; // 一级代理商分账金额(分)
|
||||
private Integer agent2ndAmount; // 二级代理商分账金额(分)
|
||||
private Integer mchAmount; // 商户分账金额(分)
|
||||
|
||||
public SharingCalculationResult() {
|
||||
}
|
||||
|
||||
public SharingCalculationResult(boolean success, String errorMessage) {
|
||||
super(success, errorMessage);
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public Integer getLklAmount() {
|
||||
return lklAmount;
|
||||
}
|
||||
|
||||
public void setLklAmount(Integer lklAmount) {
|
||||
this.lklAmount = lklAmount;
|
||||
}
|
||||
|
||||
public Integer getCanSeparateAmount() {
|
||||
return canSeparateAmount;
|
||||
}
|
||||
|
||||
public void setCanSeparateAmount(Integer canSeparateAmount) {
|
||||
this.canSeparateAmount = canSeparateAmount;
|
||||
}
|
||||
|
||||
public Integer getRefCanSeparateAmount() {
|
||||
return refCanSeparateAmount;
|
||||
}
|
||||
|
||||
public void setRefCanSeparateAmount(Integer refCanSeparateAmount) {
|
||||
this.refCanSeparateAmount = refCanSeparateAmount;
|
||||
}
|
||||
|
||||
public Integer getShippingFee() {
|
||||
return shippingFee;
|
||||
}
|
||||
|
||||
public void setShippingFee(Integer shippingFee) {
|
||||
this.shippingFee = shippingFee;
|
||||
}
|
||||
|
||||
public Integer getPlatAmount() {
|
||||
return platAmount;
|
||||
}
|
||||
|
||||
public void setPlatAmount(Integer platAmount) {
|
||||
this.platAmount = platAmount;
|
||||
}
|
||||
|
||||
public Integer getAgent1stAmount() {
|
||||
return agent1stAmount;
|
||||
}
|
||||
|
||||
public void setAgent1stAmount(Integer agent1stAmount) {
|
||||
this.agent1stAmount = agent1stAmount;
|
||||
}
|
||||
|
||||
public Integer getAgent2ndAmount() {
|
||||
return agent2ndAmount;
|
||||
}
|
||||
|
||||
public void setAgent2ndAmount(Integer agent2ndAmount) {
|
||||
this.agent2ndAmount = agent2ndAmount;
|
||||
}
|
||||
|
||||
public Integer getMchAmount() {
|
||||
return mchAmount;
|
||||
}
|
||||
|
||||
public void setMchAmount(Integer mchAmount) {
|
||||
this.mchAmount = mchAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,12 @@
|
||||
|
||||
package com.suisung.mall.common.service.impl;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.suisung.mall.common.api.StateCode;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@ -140,6 +145,117 @@ public class CommonService {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(isValidInput("", "+8618924071446"));
|
||||
System.out.println(getLklCombineSplitRespInfo("8226330541100GU", "[{\"sub_trade_no\":\"20250911110110000066202050017882\",\"sub_log_no\":\"66202050017882\",\"origin_sub_trade_no\":\"20250911110113130266250044162421\",\"origin_sub_log_no\":\"66250044162421\",\"merchant_no\":\"822584059990FYP\",\"term_no\":\"N5811590\",\"amount\":\"1\"},{\"sub_trade_no\":\"20250911110110000066202050152097\",\"sub_log_no\":\"66202050152097\",\"origin_sub_trade_no\":\"20250911110113130266250044162422\",\"origin_sub_log_no\":\"66250044162422\",\"merchant_no\":\"8226330541100GU\",\"term_no\":\"N5817779\",\"amount\":\"1\"}]", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从拉卡拉分账响应信息中提取合单运费或商品订单信息
|
||||
* <p>该方法用于处理合单支付场景,从拉卡拉返回的分账信息中筛选出运费子单或商品子单信息</p>
|
||||
*
|
||||
* @param merchantNo 商家商户号
|
||||
* @param outSplitRspInfos 拉卡拉分账响应信息,格式为JSON数组字符串:
|
||||
* [
|
||||
* {
|
||||
* "sub_trade_no":"20250830110113130266250034401288", // 子交易流水号
|
||||
* "merchant_no":"822584059990FYP", // 商户号
|
||||
* "amount":"1", // 分账金额
|
||||
* "settle_type":"0", // 结算类型
|
||||
* "sub_log_no":"66250034401288", // 子流水号
|
||||
* "out_sub_trade_no":"DF-DD-20250830-21", // 外部子交易订单号(DF开头为运费订单)
|
||||
* "term_no":"N5811590" // 终端设备号
|
||||
* },
|
||||
* {
|
||||
* "sub_trade_no":"20250830110113130266250034401289", // 子交易流水号
|
||||
* "merchant_no":"8226330541100GU", // 商户号
|
||||
* "amount":"1", // 分账金额
|
||||
* "settle_type":"0", // 结算类型
|
||||
* "sub_log_no":"66250034401289", // 子流水号
|
||||
* "out_sub_trade_no":"ORD-DD-20250830-21", // 外部子交易订单号(ORD开头为商品订单)
|
||||
* "term_no":"N5817779" // 终端设备号
|
||||
* }
|
||||
* ]
|
||||
* @param isDeliveryFee true: 提取运费子单信息, false: 提取商品子单信息
|
||||
* @return JSONObject 返回匹配的子单信息,格式如下:
|
||||
* {
|
||||
* "sub_trade_no":"20250830110113130266250034401288",
|
||||
* "merchant_no":"822584059990FYP",
|
||||
* "amount":"1",
|
||||
* "settle_type":"0",
|
||||
* "sub_log_no":"66250034401288",
|
||||
* "out_sub_trade_no":"DF-DD-20250830-21",
|
||||
* "term_no":"N5811590"
|
||||
* }
|
||||
*/
|
||||
public static JSONObject getLklCombineSplitRespInfo(String merchantNo, String outSplitRspInfos, boolean isDeliveryFee) {
|
||||
log.debug("[拉卡拉合单数据拆分] 开始获取合单数据: merchantNo={} isDeliveryFee={} outSplitRspInfos长度={}",
|
||||
merchantNo, isDeliveryFee, outSplitRspInfos != null ? outSplitRspInfos.length() : 0);
|
||||
|
||||
// 输入参数校验:检查分账信息是否为空
|
||||
if (StrUtil.isBlank(outSplitRspInfos)) {
|
||||
log.warn("[拉卡拉合单数据拆分] 合单数据为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 解析JSON数组:将字符串转换为JSONArray对象
|
||||
JSONArray outSplitRspInfoArray;
|
||||
try {
|
||||
outSplitRspInfoArray = JSONUtil.parseArray(outSplitRspInfos);
|
||||
log.debug("[拉卡拉合单数据拆分] JSON解析完成,数组大小={}", outSplitRspInfoArray.size());
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉合单数据拆分] JSON解析失败:{}", outSplitRspInfos, e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查解析后的数组是否为空
|
||||
if (outSplitRspInfoArray.isEmpty()) {
|
||||
log.warn("[拉卡拉合单数据拆分] 拆分合单数据失败:分账信息数组为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 遍历JSON数组查找匹配的子单 (合单订单通常只有两个子单:一个运费单,一个商品单)
|
||||
final String deliveryPrefix = CommonConstant.Sep_DeliveryFee_Prefix;
|
||||
final String goodsPrefix = CommonConstant.Sep_GoodsFee_Prefix;
|
||||
log.debug("[拉卡拉合单数据拆分] 开始遍历数组查找匹配子单: deliveryPrefix={} goodsPrefix={}", deliveryPrefix, goodsPrefix);
|
||||
|
||||
for (int i = 0; i < outSplitRspInfoArray.size(); i++) {
|
||||
JSONObject item = outSplitRspInfoArray.getJSONObject(i);
|
||||
|
||||
if (item == null) {
|
||||
log.debug("[拉卡拉合单数据拆分] 数组第{}项为空,跳过", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 获取外部子交易订单号字段和商户号
|
||||
String subMerchantNo = item.getStr("merchant_no");
|
||||
String outSubTradeNo = item.getStr("out_sub_trade_no");
|
||||
log.debug("[拉卡拉合单数据拆分] 检查第{}项: outSubTradeNo={} subMerchantNo={}", i, outSubTradeNo, subMerchantNo);
|
||||
|
||||
// 根据isDeliveryFee参数和商户号筛选对应的子单信息
|
||||
if (isDeliveryFee && StrUtil.isNotBlank(merchantNo) && !merchantNo.equals(subMerchantNo)) {
|
||||
log.debug("[拉卡拉合单数据拆分] 找到运费子单(通过商户号匹配): outSubTradeNo={} subMerchantNo={}", outSubTradeNo, subMerchantNo);
|
||||
return item;
|
||||
} else if (!isDeliveryFee && StrUtil.isNotBlank(merchantNo) && merchantNo.equals(subMerchantNo)) {
|
||||
log.debug("[拉卡拉合单数据拆分] 找到商品子单(通过商户号匹配): outSubTradeNo={} subMerchantNo={}", outSubTradeNo, subMerchantNo);
|
||||
return item;
|
||||
} else {
|
||||
// 如果商户号匹配失败,则通过订单号前缀匹配
|
||||
if (isDeliveryFee && StrUtil.isNotBlank(outSubTradeNo) && outSubTradeNo.startsWith(deliveryPrefix)) {
|
||||
log.debug("[拉卡拉合单数据拆分] 找到运费子单(通过前缀匹配): outSubTradeNo={}", outSubTradeNo);
|
||||
return item;
|
||||
} else if (!isDeliveryFee && StrUtil.isNotBlank(outSubTradeNo) && outSubTradeNo.startsWith(goodsPrefix)) {
|
||||
log.debug("[拉卡拉合单数据拆分] 找到商品子单(通过前缀匹配): outSubTradeNo={}", outSubTradeNo);
|
||||
return item;
|
||||
} else {
|
||||
log.debug("[拉卡拉合单数据拆分] 第{}项不匹配条件: isDeliveryFee={} merchantNo={} subMerchantNo={} outSubTradeNo={}",
|
||||
i, isDeliveryFee, merchantNo, subMerchantNo, outSubTradeNo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 未找到匹配的子单信息
|
||||
log.warn("[拉卡拉合单数据拆分] 未找到匹配的子单信息: merchantNo={} isDeliveryFee={}", merchantNo, isDeliveryFee);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ public class UniCloudPushServiceImpl implements UniCloudPushService {
|
||||
|
||||
try {
|
||||
JSONObject json = JSONUtil.parseObj(body);
|
||||
log.info("[推送服务] 原始响应: {}", json.toStringPretty());
|
||||
// log.debug("[推送服务] 原始响应: {}", json.toStringPretty());
|
||||
|
||||
// 解析标准响应字段
|
||||
Integer errCode = json.getInt("errCode", -1);
|
||||
|
||||
@ -356,13 +356,104 @@ public class CommonUtil {
|
||||
|
||||
/**
|
||||
* 对象去重工具
|
||||
*
|
||||
* @param keyExtractor
|
||||
* @return
|
||||
* @param <T>
|
||||
* @return
|
||||
*/
|
||||
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
|
||||
Set<Object> seen = new HashSet<>();
|
||||
return t -> seen.add(keyExtractor.apply(t));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按照指定比例计算分账金额
|
||||
* 分配优先级: 商家 > 平台 > 代理商
|
||||
*
|
||||
* @param totalAmount 分账总金额(单位:分)
|
||||
* @param merchantRatio 商家分账比例
|
||||
* @param platformRatioInRemaining 平台在剩余比例中的分账比例
|
||||
* @param agentRatioInRemaining 代理商在剩余比例中的分账比例
|
||||
* @return 包含商家、平台、代理商分得金额的Map
|
||||
*/
|
||||
public static Map<String, Integer> calculateProfitSharing(int totalAmount,
|
||||
BigDecimal merchantRatio,
|
||||
BigDecimal platformRatioInRemaining,
|
||||
BigDecimal agentRatioInRemaining) {
|
||||
Map<String, Integer> result = new HashMap<>();
|
||||
|
||||
// 初始化各方法定金额
|
||||
int merchantAmount = 0;
|
||||
int platformAmount = 0;
|
||||
int agentAmount = 0;
|
||||
|
||||
// 1. 计算商家按比例应得的金额(优先级最高)
|
||||
if (merchantRatio != null && merchantRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal merchantAmountDecimal = merchantRatio.multiply(new BigDecimal(totalAmount));
|
||||
merchantAmount = merchantAmountDecimal.setScale(0, RoundingMode.DOWN).intValue();
|
||||
|
||||
// 确保商家至少获得1分钱(如果商家参与分账但分配为0,且总金额大于0)
|
||||
if (merchantAmount == 0 && totalAmount > 0) {
|
||||
merchantAmount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 计算剩余金额
|
||||
int remainingAmount = totalAmount - merchantAmount;
|
||||
|
||||
// 3. 只有当有剩余金额时,才计算平台和代理商的分配
|
||||
if (remainingAmount > 0) {
|
||||
// 计算剩余比例(1 - 商家比例)
|
||||
BigDecimal remainingRatio = new BigDecimal("1");
|
||||
if (merchantRatio != null && merchantRatio.compareTo(BigDecimal.ZERO) > 0) {
|
||||
remainingRatio = new BigDecimal("1").subtract(merchantRatio);
|
||||
}
|
||||
|
||||
// 如果代理商不参与分账
|
||||
if (agentRatioInRemaining == null || agentRatioInRemaining.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
// 所有剩余金额都给平台
|
||||
platformAmount = remainingAmount;
|
||||
} else {
|
||||
// 计算平台应得金额(基于剩余金额)
|
||||
if (platformRatioInRemaining != null && platformRatioInRemaining.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 平台在剩余部分中的分配 = 剩余金额 * 平台在剩余中的比例
|
||||
BigDecimal platformShareOfRemaining = platformRatioInRemaining.multiply(new BigDecimal(remainingAmount));
|
||||
platformAmount = platformShareOfRemaining.setScale(0, RoundingMode.DOWN).intValue();
|
||||
}
|
||||
|
||||
// 计算代理商应得金额(基于剩余金额)
|
||||
if (agentRatioInRemaining != null && agentRatioInRemaining.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 代理商在剩余部分中的分配 = 剩余金额 * 代理商在剩余中的比例
|
||||
BigDecimal agentShareOfRemaining = agentRatioInRemaining.multiply(new BigDecimal(remainingAmount));
|
||||
agentAmount = agentShareOfRemaining.setScale(0, RoundingMode.DOWN).intValue();
|
||||
}
|
||||
|
||||
// 重新计算剩余金额用于最终分配
|
||||
int finalRemainingAmount = remainingAmount - platformAmount - agentAmount;
|
||||
|
||||
// 确保平台至少获得1分钱(如果平台参与分账但分配为0,且还有剩余金额)
|
||||
if (platformRatioInRemaining != null && platformRatioInRemaining.compareTo(BigDecimal.ZERO) > 0
|
||||
&& platformAmount == 0 && finalRemainingAmount > 0) {
|
||||
platformAmount = 1;
|
||||
finalRemainingAmount--;
|
||||
}
|
||||
|
||||
// 按优先级分配剩余金额: 商家 > 平台 > 代理商
|
||||
// 剩余金额给商家(因为商家优先级最高)
|
||||
merchantAmount += finalRemainingAmount;
|
||||
}
|
||||
}
|
||||
|
||||
result.put("merchantAmount", merchantAmount);
|
||||
result.put("platformAmount", platformAmount);
|
||||
result.put("agentAmount", agentAmount);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("测试1分钱分配:");
|
||||
System.out.println(calculateProfitSharing(9800, new BigDecimal("0.94"), new BigDecimal("0.2"), new BigDecimal("0")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,12 +6,14 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class VideoUtil {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(VideoUtil.class);
|
||||
public static List<String> videoAllowFiles = new ArrayList<String>() {{
|
||||
add("flv");
|
||||
add("swf");
|
||||
@ -31,7 +33,6 @@ public class VideoUtil {
|
||||
add("wav");
|
||||
add("mid");
|
||||
}};
|
||||
private static Logger logger = LoggerFactory.getLogger(VideoUtil.class);
|
||||
|
||||
/**
|
||||
* 得到语音或视频文件时长,单位秒 并格式化
|
||||
@ -51,11 +52,43 @@ public class VideoUtil {
|
||||
* @return 单位为毫秒
|
||||
*/
|
||||
public static long getMp4Duration(String videoPath) throws IOException {
|
||||
IsoFile isoFile = new IsoFile(videoPath);
|
||||
long lengthInSeconds =
|
||||
isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
|
||||
isoFile.getMovieBox().getMovieHeaderBox().getTimescale();
|
||||
return lengthInSeconds;
|
||||
try {
|
||||
IsoFile isoFile = new IsoFile(videoPath);
|
||||
long lengthInSeconds =
|
||||
isoFile.getMovieBox().getMovieHeaderBox().getDuration() /
|
||||
isoFile.getMovieBox().getMovieHeaderBox().getTimescale();
|
||||
return lengthInSeconds;
|
||||
} catch (Exception e) {
|
||||
// 处理 MOV 文件可能存在的解析问题,尝试使用 FFmpeg 获取时长
|
||||
logger.warn("无法通过 IsoFile 解析视频文件时长: {}, 尝试使用 FFmpeg", videoPath);
|
||||
try {
|
||||
return getDurationWithFFmpeg(videoPath);
|
||||
} catch (Exception ffmpegException) {
|
||||
logger.error("FFmpeg 也无法解析视频文件时长: {}", videoPath);
|
||||
throw new IOException("无法解析视频文件: " + videoPath, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 FFmpeg 获取视频时长(备用方法)
|
||||
*
|
||||
* @param videoPath
|
||||
* @return 单位为毫秒
|
||||
* @throws IOException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private static long getDurationWithFFmpeg(String videoPath) throws IOException, InterruptedException {
|
||||
String command = "ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 " + videoPath;
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String result = reader.readLine();
|
||||
process.waitFor();
|
||||
if (result != null) {
|
||||
// ffprobe 返回的是秒数,需要转换为毫秒
|
||||
return (long) (Double.parseDouble(result) * 1000);
|
||||
}
|
||||
throw new IOException("无法获取视频时长");
|
||||
}
|
||||
|
||||
|
||||
@ -194,8 +227,7 @@ public class VideoUtil {
|
||||
int mm = (temp % 3600) / 60;
|
||||
int ss = (temp % 3600) % 60;
|
||||
|
||||
return hh != 0 ? ((hh < 10 ? ("0" + hh) : hh) + ":") : "" +
|
||||
(mm < 10 ? ("0" + mm) : mm) + ":" +
|
||||
return hh != 0 ? ((hh < 10 ? ("0" + hh) : hh) + ":") : (mm < 10 ? ("0" + mm) : mm) + ":" +
|
||||
(ss < 10 ? ("0" + ss) : ss);
|
||||
}
|
||||
}
|
||||
@ -206,7 +238,7 @@ class InputStreamRunnable extends Thread {
|
||||
|
||||
public InputStreamRunnable(InputStream is, String _type) {
|
||||
try {
|
||||
bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), "UTF-8"));
|
||||
bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), StandardCharsets.UTF_8));
|
||||
type = _type;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
|
||||
@ -90,6 +90,7 @@ secure:
|
||||
- "/esProduct/**"
|
||||
- "/admin/oss/upload/**"
|
||||
- "/admin/shop/wxqrcode/common/wxurlscheme"
|
||||
- "/mobile/shop/lakala/sign/ec/**"
|
||||
- "/mobile/**/**/test/case"
|
||||
- "/**/**/testcase"
|
||||
universal:
|
||||
|
||||
@ -24,44 +24,10 @@ import java.io.IOException;
|
||||
@RestController
|
||||
@RequestMapping("/mobile/pay/lakala")
|
||||
public class LakalaController extends BaseControllerImpl {
|
||||
//
|
||||
// @Resource
|
||||
// private LakalaPayService lakalaPayService;
|
||||
|
||||
@ApiOperation(value = "本地文件转base64", notes = "本地文件转base64")
|
||||
@RequestMapping(value = "/file2base64", method = RequestMethod.POST)
|
||||
public String file2Base64(@RequestParam("file") MultipartFile file) throws IOException {
|
||||
String str = Base64Utils.encodeToString(file.getBytes());
|
||||
return str;
|
||||
}
|
||||
|
||||
// @ApiOperation(value = "商户分账业务开通申请", notes = "商户分账业务开通申请")
|
||||
// @RequestMapping(value = "/ledger/applyLedgerMer", method = RequestMethod.POST)
|
||||
// public CommonResult ledgerApplyLedgerMer(@RequestBody JSONObject paramsJSON) {
|
||||
// return lakalaPayService.applyLedgerMer(paramsJSON);
|
||||
// }
|
||||
//
|
||||
// @ApiOperation(value = "商户分账业务开通申请异步回调回调", notes = "商户分账业务开通申请异步回调回调")
|
||||
// @RequestMapping(value = "/ledger/applyLedgerMerNotify", method = RequestMethod.POST)
|
||||
// public JSONObject ledgerApplyLedgerMerNotify(HttpServletRequest request) {
|
||||
// return lakalaPayService.applyLedgerMerNotify(request);
|
||||
// }
|
||||
//
|
||||
// @ApiOperation(value = "分账接收方创建申请", notes = "分账接收方创建申请")
|
||||
// @RequestMapping(value = "/ledger/applyLedgerReceiver", method = RequestMethod.POST)
|
||||
// public CommonResult applyLedgerReceiver(@RequestBody JSONObject paramsJSON) {
|
||||
// return lakalaPayService.applyLedgerReceiver(paramsJSON);
|
||||
// }
|
||||
//
|
||||
// @ApiOperation(value = "分账关系绑定申请", notes = "分账关系绑定申请")
|
||||
// @RequestMapping(value = "/ledger/applyBind", method = RequestMethod.POST)
|
||||
// public CommonResult applyBind(@RequestBody JSONObject paramsJSON) {
|
||||
// return lakalaPayService.applyLedgerMerReceiverBind(paramsJSON);
|
||||
// }
|
||||
//
|
||||
// @ApiOperation(value = "分账关系绑定申请异步回调通知", notes = "分账关系绑定申请异步回调通知")
|
||||
// @RequestMapping(value = "/ledger/applyBindNotify", method = RequestMethod.POST)
|
||||
// public JSONObject applyBindNotify(HttpServletRequest request) {
|
||||
// return lakalaPayService.applyLedgerMerReceiverBindNotify(request);
|
||||
// }
|
||||
}
|
||||
|
||||
@ -39,4 +39,19 @@ public interface AccountBaseConfigService extends IBaseService<AccountBaseConfig
|
||||
|
||||
boolean getTradeModePlantform();
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
*
|
||||
* @param configKey
|
||||
* @return
|
||||
*/
|
||||
String getSystemConfig(String configKey);
|
||||
|
||||
/**
|
||||
* 获取平台内部最低配送费,单位(分)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Integer getInnerMinDeliveryFee();
|
||||
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.constant.RedisConstant;
|
||||
import com.suisung.mall.common.feignService.AccountService;
|
||||
import com.suisung.mall.common.modules.account.AccountBaseConfig;
|
||||
@ -226,6 +227,43 @@ public class AccountBaseConfigServiceImpl extends BaseServiceImpl<AccountBaseCon
|
||||
return getTradeMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置值
|
||||
* <p>
|
||||
* 根据配置键获取对应的配置值,如果配置值为空则返回空字符串。
|
||||
* </p>
|
||||
*
|
||||
* @param configKey 配置键
|
||||
* @return 配置值,如果找不到或为空则返回空字符串
|
||||
*/
|
||||
@Override
|
||||
public String getSystemConfig(String configKey) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(configKey)) {
|
||||
log.warn("[系统配置] 参数校验失败:配置键不能为空");
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
String configValue = accountService.getAccountBaseConfigValue(configKey);
|
||||
return StrUtil.blankToDefault(configValue, "0");
|
||||
} catch (Exception e) {
|
||||
log.error("[系统配置] 获取配置值异常,configKey={}", configKey, e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取平台内部最低配送费,单位(分)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Integer getInnerMinDeliveryFee() {
|
||||
String v = getSystemConfig(CommonConstant.Inner_Min_DeliveryFee_Key);
|
||||
return NumberUtil.isNumber(v) ? Convert.toInt(v) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易模式 2直接交易:商家直接收款; 1担保交易:平台收款,平台和商家结算。
|
||||
*/
|
||||
|
||||
@ -83,7 +83,7 @@ public class AnalytiscTradeServiceImpl implements AnalytiscTradeService {
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (yestodayTradeAmount.getAmount().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (todayTradeAmount.getAmount().subtract(yestodayTradeAmount.getAmount())).divide(yestodayTradeAmount.getAmount(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -20,19 +20,16 @@ import com.lkl.laop.sdk.LKLSDK;
|
||||
import com.lkl.laop.sdk.exception.SDKException;
|
||||
import com.lkl.laop.sdk.request.V2MmsOpenApiUploadFileRequest;
|
||||
import com.lkl.laop.sdk.request.V3LabsRelationRefundRequest;
|
||||
import com.lkl.laop.sdk.request.V3LabsTransPreorderRequest;
|
||||
import com.lkl.laop.sdk.request.model.V3LabsTradeLocationInfo;
|
||||
import com.lkl.laop.sdk.request.model.V3LabsTradePreorderWechatBus;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.exception.ApiException;
|
||||
import com.suisung.mall.common.feignService.ShopService;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
||||
import com.suisung.mall.common.utils.DateTimeUtils;
|
||||
import com.suisung.mall.common.utils.I18nUtil;
|
||||
import com.suisung.mall.common.utils.RestTemplateHttpUtil;
|
||||
import com.suisung.mall.pay.service.AccountBaseConfigService;
|
||||
import com.suisung.mall.pay.service.LakalaPayService;
|
||||
import com.suisung.mall.pay.service.LklLedgerMemberService;
|
||||
import com.suisung.mall.pay.service.LklLedgerMerReceiverBindService;
|
||||
import com.suisung.mall.pay.service.LklLedgerReceiverService;
|
||||
import com.suisung.mall.pay.utils.LakalaUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -82,14 +79,9 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
@Autowired
|
||||
private ShopService shopService;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private LklLedgerMemberService lklLedgerMemberService;
|
||||
|
||||
@Autowired
|
||||
private LklLedgerReceiverService lklLedgerReceiverService;
|
||||
|
||||
@Autowired
|
||||
private LklLedgerMerReceiverBindService lklLedgerMerReceiverBindService;
|
||||
private AccountBaseConfigService accountBaseConfigService;
|
||||
|
||||
/**
|
||||
* 初始化 拉卡拉SDK
|
||||
@ -120,76 +112,170 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
* @param storeId 店铺号
|
||||
* @param orderId 订单号
|
||||
* @param subject 订单标题
|
||||
* @param totalAmount 订单金额
|
||||
* @param totalAmount 订单金额(单位:分)
|
||||
* @param notifyURL 回调地址
|
||||
* @param requestIP 请求ip
|
||||
* @param requestIP 请求IP
|
||||
* @param remark 备注
|
||||
* @return
|
||||
* @return 拉卡拉预下单响应结果
|
||||
*/
|
||||
@Override
|
||||
public JSONObject lklTransPreOrder(String merchantNo, String termNo, String xcxAppId, String openId, String storeId, String orderId, String subject, String totalAmount, String notifyURL, String requestIP, String remark) {
|
||||
// 1. 配置初始化
|
||||
initLKLSDK();
|
||||
|
||||
if (StrUtil.isBlank(merchantNo) || StrUtil.isBlank(termNo)) {
|
||||
throw new ApiException(I18nUtil._("缺少商户号或终端号!"));
|
||||
// 1. 参数校验
|
||||
if (StrUtil.isBlank(merchantNo)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:商户号不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("商户号不能为空!"));
|
||||
}
|
||||
|
||||
//2. 装配数据
|
||||
/*** 微信主扫场景示例 */
|
||||
V3LabsTransPreorderRequest v3LabsTransPreorderWechatReq = new V3LabsTransPreorderRequest();
|
||||
v3LabsTransPreorderWechatReq.setMerchantNo(merchantNo);
|
||||
v3LabsTransPreorderWechatReq.setTermNo(termNo);
|
||||
v3LabsTransPreorderWechatReq.setOutTradeNo(orderId);
|
||||
v3LabsTransPreorderWechatReq.setSubject(subject);
|
||||
//微信:WECHAT 支付宝:ALIPAY 银联:UQRCODEPAY 翼支付: BESTPAY 苏宁易付宝: SUNING 拉卡拉支付账户:LKLACC 网联小钱包:NUCSPAY 京东钱包:JD
|
||||
v3LabsTransPreorderWechatReq.setAccountType("WECHAT");
|
||||
// 41:NATIVE((ALIPAY,云闪付支持,京东白条分期)51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付,翼支付JS支付、拉卡拉钱包支付)71:微信小程序支付 61:APP支付(微信APP支付)
|
||||
v3LabsTransPreorderWechatReq.setTransType("51");
|
||||
v3LabsTransPreorderWechatReq.setTotalAmount(totalAmount); // 应支付金额,单位:分
|
||||
v3LabsTransPreorderWechatReq.setSettleType("1"); //“0”或者空,常规结算方式,如需接拉卡拉分账通需传“1”,商户未开通分账之前切记不用上送此参数。;
|
||||
v3LabsTransPreorderWechatReq.setNotifyUrl(notifyURL);
|
||||
v3LabsTransPreorderWechatReq.setRemark(remark);
|
||||
if (StrUtil.isBlank(termNo)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:终端号不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("终端号不能为空!"));
|
||||
}
|
||||
|
||||
//地址位置信息
|
||||
V3LabsTradeLocationInfo v3LabsTradePreorderLocationInfo = new V3LabsTradeLocationInfo(requestIP);
|
||||
v3LabsTransPreorderWechatReq.setLocationInfo(v3LabsTradePreorderLocationInfo);
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:订单号不能为空, merchantNo={}", merchantNo);
|
||||
throw new ApiException(I18nUtil._("订单号不能为空!"));
|
||||
}
|
||||
|
||||
//微信主扫场景下 acc_busi_fields 域内容
|
||||
V3LabsTradePreorderWechatBus wechatBus = new V3LabsTradePreorderWechatBus();
|
||||
wechatBus.setSubAppid(xcxAppId); // 小程序appId
|
||||
wechatBus.setUserId(openId); // 微信 openId
|
||||
wechatBus.setDeviceInfo("WEB"); // 终端设备号(门店号或收银设备ID),注意:PC网页或JSAPI支付请传”WEB”
|
||||
// wechatBus.setAttach(storeId); // 附加数据,商户自定义数据,在查询交易结果时原样返回。
|
||||
v3LabsTransPreorderWechatReq.setAccBusiFields(wechatBus);
|
||||
if (StrUtil.isBlank(subject)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:订单标题不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("订单标题不能为空!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(totalAmount)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:订单金额不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("订单金额不能为空!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(notifyURL)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:回调地址不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("回调地址不能为空!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(requestIP)) {
|
||||
log.warn("[拉卡拉预下单] 参数校验失败:请求IP不能为空, orderId={}", orderId);
|
||||
throw new ApiException(I18nUtil._("请求IP不能为空!"));
|
||||
}
|
||||
|
||||
log.info("[拉卡拉预下单] 开始处理请求, merchantNo={}, termNo={}, orderId={}", merchantNo, termNo, orderId);
|
||||
|
||||
try {
|
||||
log.info("拉卡拉预下单请求参数:{}", JSONUtil.toJsonStr(v3LabsTransPreorderWechatReq));
|
||||
// 2. 装配请求数据
|
||||
JSONObject reqData = new JSONObject();
|
||||
reqData.put("merchant_no", merchantNo);
|
||||
reqData.put("term_no", termNo);
|
||||
reqData.put("out_trade_no", orderId);
|
||||
reqData.put("subject", subject);
|
||||
// 微信:WECHAT 支付宝:ALIPAY 银联:UQRCODEPAY 翼支付: BESTPAY 苏宁易付宝: SUNING 拉卡拉支付账户:LKLACC 网联小钱包:NUCSPAY 京东钱包:JD
|
||||
reqData.put("account_type", "WECHAT");
|
||||
// 41:NATIVE((ALIPAY,云闪付支持,京东白条分期)51:JSAPI(微信公众号支付,支付宝服务窗支付,银联JS支付,翼支付JS支付、拉卡拉钱包支付)71:微信小程序支付 61:APP支付(微信APP支付)
|
||||
reqData.put("trans_type", "51");
|
||||
reqData.put("total_amount", totalAmount); // 应支付金额,单位:分
|
||||
reqData.put("settle_type", "1"); // "0"或者空,常规结算方式,如需接拉卡拉分账通需传"1",商户未开通分账之前切记不用上送此参数。
|
||||
reqData.put("notify_url", notifyURL);
|
||||
reqData.put("remark", remark);
|
||||
reqData.put("complete_notify_url", "https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify");
|
||||
|
||||
//3. 发送请求
|
||||
String responseStr = LKLSDK.httpPost(v3LabsTransPreorderWechatReq);
|
||||
log.info("拉卡拉预下单响应数据:{}", responseStr);
|
||||
if (StrUtil.isBlank(responseStr)) {
|
||||
// 地址位置信息
|
||||
JSONObject locationInfo = new JSONObject();
|
||||
locationInfo.put("request_ip", requestIP);
|
||||
reqData.put("location_info", locationInfo);
|
||||
|
||||
// 微信业务参数
|
||||
if (StrUtil.isNotBlank(xcxAppId) && StrUtil.isNotBlank(openId)) {
|
||||
JSONObject accBusiFields = new JSONObject();
|
||||
accBusiFields.put("sub_appid", xcxAppId); // 小程序appId
|
||||
accBusiFields.put("user_id", openId); // 微信 openId
|
||||
accBusiFields.put("device_info", "WEB"); // 终端设备号(门店号或收银设备ID),注意:PC网页或JSAPI支付请传"WEB"
|
||||
reqData.put("acc_busi_fields", accBusiFields);
|
||||
log.debug("[拉卡拉预下单] 已添加微信业务参数, xcxAppId={}, openId={}", xcxAppId, openId);
|
||||
} else {
|
||||
log.warn("[拉卡拉预下单] 微信业务参数不完整或不需要, xcxAppId={}, openId={}", xcxAppId, openId);
|
||||
}
|
||||
|
||||
// 3. 构造请求体
|
||||
JSONObject reqBody = new JSONObject();
|
||||
reqBody.put("req_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss"));
|
||||
reqBody.put("version", "3.0");
|
||||
reqBody.put("req_data", reqData);
|
||||
|
||||
log.info("[拉卡拉预下单] 请求参数组装完成, orderId={}", orderId);
|
||||
log.debug("[拉卡拉预下单] 完整请求参数: {}", JSONUtil.toJsonStr(reqBody));
|
||||
|
||||
// 4. 发送请求
|
||||
String reqUrl = serverUrl + "/api/v3/labs/trans/preorder";
|
||||
log.info("[拉卡拉预下单] 准备发送请求, orderId={}, url={}", orderId, reqUrl);
|
||||
|
||||
String authorization = LakalaUtil.genAuthorizationByPath(priKeyPath, appId, serialNo, reqBody.toString());
|
||||
if (StrUtil.isBlank(authorization)) {
|
||||
log.error("[拉卡拉预下单] 生成签名失败, orderId={}", orderId);
|
||||
throw new ApiException("生成请求签名失败!");
|
||||
}
|
||||
|
||||
JSONObject header = new JSONObject();
|
||||
header.put("Authorization", authorization);
|
||||
header.put("Content-Type", "application/json");
|
||||
|
||||
log.debug("[拉卡拉预下单] 请求头信息生成完成, orderId={}", orderId);
|
||||
|
||||
// 使用RestTemplate发送POST请求
|
||||
ResponseEntity<JSONObject> lakalaRespEntity = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class);
|
||||
log.info("[拉卡拉预下单] 收到响应, orderId={}, responseStatus={}", orderId, lakalaRespEntity != null ? lakalaRespEntity.getStatusCode() : "NULL");
|
||||
|
||||
// 5. 处理响应结果
|
||||
if (lakalaRespEntity == null) {
|
||||
log.warn("[拉卡拉预下单] 响应为空, orderId={}", orderId);
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr);
|
||||
JSONObject respBody = lakalaRespEntity.getBody();
|
||||
log.debug("[拉卡拉预下单] 响应体内容, orderId={}, responseBody={}", orderId, respBody);
|
||||
|
||||
if (lakalaRespJSON != null && lakalaRespJSON.getStr("code").equals("BBS00000")) {
|
||||
// 新增一个拉卡拉订单记录 shop_order_lkl 表
|
||||
JSONObject lklPayReqAndRespJson = new JSONObject();
|
||||
lklPayReqAndRespJson.put("req", JSONUtil.parseObj(v3LabsTransPreorderWechatReq));
|
||||
lklPayReqAndRespJson.put("resp", lakalaRespJSON);
|
||||
shopService.lklPayAddShopOrderLkl(lklPayReqAndRespJson);
|
||||
if (respBody == null) {
|
||||
log.warn("[拉卡拉预下单] 响应体为空, orderId={}", orderId);
|
||||
return null;
|
||||
}
|
||||
//4. 响应
|
||||
return lakalaRespJSON;
|
||||
} catch (SDKException e) {
|
||||
log.error("拉卡拉支付出错:", e);
|
||||
throw new ApiException(I18nUtil._("支付失败!"), e);
|
||||
|
||||
String responseCode = respBody.getStr("code");
|
||||
if (StrUtil.isBlank(responseCode)) {
|
||||
log.warn("[拉卡拉预下单] 响应码为空, orderId={}", orderId);
|
||||
return respBody;
|
||||
}
|
||||
|
||||
// 使用安全的字符串比较方式,避免空指针异常
|
||||
if (!lklPaySuccessCode.equals(responseCode)) {
|
||||
log.warn("[拉卡拉预下单] 响应码异常, orderId={}, code={}, msg={}", orderId, responseCode, respBody.getStr("msg"));
|
||||
return respBody;
|
||||
}
|
||||
|
||||
// 平台最低配送费,单位(分)
|
||||
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
|
||||
reqData.set("shopping_fee_inner", innerMinDeliverFee); // 平台内部最低配送费,单位(分)
|
||||
|
||||
log.info("[拉卡拉预下单] 支付成功,准备保存订单记录, orderId={}", orderId);
|
||||
// 新增一个拉卡拉订单记录 shop_order_lkl 表
|
||||
JSONObject lklPayReqAndRespJson = new JSONObject();
|
||||
lklPayReqAndRespJson.put("req", reqData);
|
||||
lklPayReqAndRespJson.put("resp", respBody);
|
||||
|
||||
try {
|
||||
shopService.lklPayAddShopOrderLkl(lklPayReqAndRespJson);
|
||||
log.debug("[拉卡拉预下单] 订单记录保存成功, orderId={}", orderId);
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉预下单] 保存订单记录失败, orderId={}", orderId, e);
|
||||
// 不中断主流程,仅记录错误
|
||||
}
|
||||
|
||||
// 6. 返回响应结果
|
||||
log.info("[拉卡拉预下单] 处理完成, orderId={}, responseCode={}", orderId, responseCode);
|
||||
return respBody;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉预下单] 系统异常, merchantNo={}, termNo={}, orderId={}", merchantNo, termNo, orderId, e);
|
||||
throw new ApiException("拉卡拉预下单出错:" + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 拉卡拉合单预下单(主要有运费的订单使用合单)
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=208
|
||||
@ -203,41 +289,64 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
* @param storeId 店铺Id
|
||||
* @param orderId 订单号
|
||||
* @param subject 订单标题
|
||||
* @param totalAmount 订单总金额
|
||||
* @param agentAmount 代理商收取金额
|
||||
* @param totalAmount 订单总金额(单位:分)
|
||||
* @param agentAmount 代理商收取金额(单位:分)
|
||||
* @param notifyURL 回调地址
|
||||
* @param requestIP 请求ip
|
||||
* @param requestIP 请求IP
|
||||
* @param remark 备注
|
||||
* @return 拉卡拉合单预下单响应结果
|
||||
**/
|
||||
@Override
|
||||
public JSONObject lklTransMergePreOrder(String merchantNo, String termNo, String agentMerchantNo, String agentTermNo, String xcxAppId, String openId, String storeId, String orderId, String subject, String totalAmount, String agentAmount, String notifyURL, String requestIP, String remark) {
|
||||
// 2. 参数校验
|
||||
log.info("[拉卡拉合单预下单] 开始处理请求, merchantNo={}, termNo={}, agentMerchantNo={}, agentTermNo={}, orderId={}",
|
||||
merchantNo, termNo, agentMerchantNo, agentTermNo, orderId);
|
||||
|
||||
// 1. 参数校验
|
||||
if (StrUtil.isBlank(merchantNo)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:商家商户号不能为空, orderId={}", orderId);
|
||||
throw new ApiException("商家商户号不能为空!");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(termNo)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:终端号不能为空, orderId={}", orderId);
|
||||
throw new ApiException("终端号不能为空!");
|
||||
}
|
||||
|
||||
// 3. 校验其他必要参数
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:订单号不能为空, merchantNo={}, termNo={}", merchantNo, termNo);
|
||||
throw new ApiException("订单号不能为空!");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(subject)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:订单标题不能为空, orderId={}", orderId);
|
||||
throw new ApiException("订单标题不能为空!");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(totalAmount)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:订单总金额不能为空, orderId={}", orderId);
|
||||
throw new ApiException("订单总金额不能为空!");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(notifyURL)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:回调地址不能为空, orderId={}", orderId);
|
||||
throw new ApiException("回调地址不能为空!");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(requestIP)) {
|
||||
log.warn("[拉卡拉合单预下单] 参数校验失败:请求IP不能为空, orderId={}", orderId);
|
||||
throw new ApiException("请求IP不能为空!");
|
||||
}
|
||||
|
||||
// 如果不符合合单交易条件,则走聚合主扫单笔交易
|
||||
if (StrUtil.isBlank(agentMerchantNo) || StrUtil.isBlank(agentTermNo) || StrUtil.isBlank(agentAmount) || "0".equals(agentAmount)) {
|
||||
log.info("不符合合单交易条件,转为单笔交易处理。商家商户号:{},代理商商户号:{},代理商终端号:{},代理商金额:{}",
|
||||
log.info("[拉卡拉合单预下单] 不符合合单交易条件,转为单笔交易处理。商家商户号:{},代理商商户号:{},代理商终端号:{},代理商金额:{}",
|
||||
merchantNo, agentMerchantNo, agentTermNo, agentAmount);
|
||||
return lklTransPreOrder(merchantNo, termNo, xcxAppId, openId, storeId, orderId, subject, totalAmount, notifyURL, requestIP, remark);
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. 装配请求数据
|
||||
log.debug("[拉卡拉合单预下单] 开始装配请求数据, orderId={}", orderId);
|
||||
// 2. 装配请求数据
|
||||
JSONObject reqData = new JSONObject();
|
||||
|
||||
// 基本交易信息
|
||||
@ -250,6 +359,7 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
reqData.put("subject", subject); // 订单标题
|
||||
reqData.put("notify_url", notifyURL); // 异步通知地址
|
||||
reqData.put("remark", remark); // 备注
|
||||
reqData.put("complete_notify_url", "https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify"); // 发货类小程序确认收获后通知商户的地址
|
||||
|
||||
// 位置信息
|
||||
JSONObject locationInfo = new JSONObject();
|
||||
@ -257,24 +367,42 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
reqData.put("location_info", locationInfo);
|
||||
|
||||
// 微信业务参数
|
||||
JSONObject accBusiFields = new JSONObject();
|
||||
accBusiFields.put("sub_appid", xcxAppId); // 小程序appid
|
||||
accBusiFields.put("user_id", openId); // 用户openid
|
||||
reqData.put("acc_busi_fields", accBusiFields);
|
||||
if (StrUtil.isNotBlank(xcxAppId) && StrUtil.isNotBlank(openId)) {
|
||||
JSONObject accBusiFields = new JSONObject();
|
||||
accBusiFields.put("sub_appid", xcxAppId); // 小程序appId
|
||||
accBusiFields.put("user_id", openId); // 用户openid
|
||||
reqData.put("acc_busi_fields", accBusiFields);
|
||||
log.debug("[拉卡拉合单预下单] 已添加微信业务参数, xcxAppId={}, openId={}", xcxAppId, openId);
|
||||
} else {
|
||||
log.warn("[拉卡拉合单预下单] 微信业务参数不完整或不需要, xcxAppId={}, openId={}", xcxAppId, openId);
|
||||
}
|
||||
|
||||
// 重要约定,订单号规则:商品订单:ORD-订单号,运费订单:DF-订单号
|
||||
// 分单信息
|
||||
// 重要约定,订单号规则:商品订单:ORD_订单号,运费订单:DF_订单号
|
||||
// 商品子单信息
|
||||
JSONObject goodsSplitInfo = new JSONObject();
|
||||
goodsSplitInfo.put("out_sub_trade_no", "ORD-" + orderId); // 子订单号
|
||||
goodsSplitInfo.put("out_sub_trade_no", CommonConstant.Sep_GoodsFee_Prefix + orderId); // 商品子订单号
|
||||
goodsSplitInfo.put("merchant_no", merchantNo); // 分账商户号
|
||||
goodsSplitInfo.put("term_no", termNo); // 分账终端号
|
||||
int totalAmountInt = Convert.toInt(totalAmount) - Convert.toInt(agentAmount);
|
||||
goodsSplitInfo.put("amount", Convert.toStr(totalAmountInt)); // 分账金额
|
||||
goodsSplitInfo.put("settle_type", "0"); // "0"或者空,常规结算方式
|
||||
|
||||
// 安全转换金额,避免类型转换异常
|
||||
int totalAmountInt = 0;
|
||||
int agentAmountInt = 0;
|
||||
try {
|
||||
totalAmountInt = Convert.toInt(totalAmount);
|
||||
agentAmountInt = Convert.toInt(agentAmount);
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("[拉卡拉合单预下单] 金额转换异常, totalAmount={}, agentAmount={}, orderId={}", totalAmount, agentAmount, orderId, e);
|
||||
throw new ApiException("金额格式错误!");
|
||||
}
|
||||
|
||||
int goodsAmountInt = totalAmountInt - agentAmountInt;
|
||||
goodsSplitInfo.put("amount", Convert.toStr(goodsAmountInt)); // 分账金额
|
||||
goodsSplitInfo.put("settle_type", "1"); // "0"或者空,常规结算方式
|
||||
goodsSplitInfo.put("sub_remark", "商品订单金额"); // 子单备注信息
|
||||
|
||||
// 运费子单信息
|
||||
JSONObject deliverySplitInfo = new JSONObject();
|
||||
deliverySplitInfo.put("out_sub_trade_no", "DF-" + orderId); // 子订单号
|
||||
deliverySplitInfo.put("out_sub_trade_no", CommonConstant.Sep_DeliveryFee_Prefix + orderId); // 运费子订单号
|
||||
deliverySplitInfo.put("merchant_no", agentMerchantNo); // 分账商户号
|
||||
deliverySplitInfo.put("term_no", agentTermNo); // 分账终端号
|
||||
deliverySplitInfo.put("amount", agentAmount); // 分账金额
|
||||
@ -286,48 +414,89 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
outSplitInfo.add(goodsSplitInfo);
|
||||
reqData.put("out_split_info", outSplitInfo);
|
||||
|
||||
// 5. 构造请求体
|
||||
log.info("[拉卡拉合单预下单] 分单信息组装完成, orderId={}, goodsAmount={}分, deliveryAmount={}分",
|
||||
orderId, goodsAmountInt, agentAmountInt);
|
||||
|
||||
// 3. 构造请求体
|
||||
JSONObject reqBody = new JSONObject();
|
||||
reqBody.put("req_time", DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMddHHmmss"));
|
||||
reqBody.put("version", "3.0");
|
||||
reqBody.put("req_data", reqData);
|
||||
|
||||
// 6. 发送请求
|
||||
log.info("[拉卡拉合单预下单] 请求参数组装完成, orderId={}", orderId);
|
||||
log.debug("[拉卡拉合单预下单] 完整请求参数: {}", JSONUtil.toJsonStr(reqBody));
|
||||
|
||||
// 4. 发送请求
|
||||
String reqUrl = serverUrl + "/api/v3/labs/trans/merge/preorder";
|
||||
log.info("拉卡拉合单预下单请求参数:{}", reqBody);
|
||||
log.info("[拉卡拉合单预下单] 准备发送请求, orderId={}, url={}", orderId, reqUrl);
|
||||
|
||||
String authorization = LakalaUtil.genAuthorizationByPath(priKeyPath, appId, serialNo, reqBody.toString());
|
||||
if (StrUtil.isBlank(authorization)) {
|
||||
log.error("[拉卡拉合单预下单] 生成签名失败, orderId={}", orderId);
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "生成请求签名失败").set("resp_data", null);
|
||||
}
|
||||
|
||||
JSONObject header = new JSONObject();
|
||||
header.put("Authorization", authorization);
|
||||
header.put("Content-Type", "application/json");
|
||||
|
||||
// 这里的请求方法,对返回的字段进行了处理,需要注意
|
||||
ResponseEntity<JSONObject> lakalaRespJSON = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class);
|
||||
log.info("拉卡拉合单交易响应参数:{}", lakalaRespJSON);
|
||||
log.debug("[拉卡拉合单预下单] 请求头信息生成完成, orderId={}", orderId);
|
||||
|
||||
// 7. 处理响应结果
|
||||
if (lakalaRespJSON == null) {
|
||||
// 发送请求
|
||||
ResponseEntity<JSONObject> lakalaRespEntity = RestTemplateHttpUtil.sendPostBodyBackEntity(reqUrl, header, reqBody, JSONObject.class);
|
||||
log.info("[拉卡拉合单预下单] 收到响应, orderId={}, responseStatus={}", orderId, lakalaRespEntity != null ? lakalaRespEntity.getStatusCode() : "NULL");
|
||||
|
||||
// 5. 处理响应结果
|
||||
if (lakalaRespEntity == null) {
|
||||
log.warn("[拉卡拉合单预下单] 响应为空, orderId={}", orderId);
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "拉卡拉合单交易无响应值").set("resp_data", null);
|
||||
}
|
||||
|
||||
JSONObject respBody = lakalaRespJSON.getBody();
|
||||
JSONObject respBody = lakalaRespEntity.getBody();
|
||||
log.debug("[拉卡拉合单预下单] 响应体内容, orderId={}, responseBody={}", orderId, respBody);
|
||||
|
||||
// 7. 处理响应结果
|
||||
if (!lklPaySuccessCode.equals(respBody.getStr("code")) || respBody == null) {
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "拉卡拉合单交易无响应值").set("resp_data", null);
|
||||
// 6. 处理响应结果
|
||||
if (respBody == null) {
|
||||
log.warn("[拉卡拉合单预下单] 响应体为空, orderId={}", orderId);
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "拉卡拉合单交易响应体为空").set("resp_data", null);
|
||||
}
|
||||
|
||||
String responseCode = respBody.getStr("code");
|
||||
if (StrUtil.isBlank(responseCode)) {
|
||||
log.warn("[拉卡拉合单预下单] 响应码为空, orderId={}", orderId);
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "拉卡拉合单交易响应码为空").set("resp_data", null);
|
||||
}
|
||||
|
||||
// 使用安全的字符串比较方式,避免空指针异常
|
||||
if (!lklPaySuccessCode.equals(responseCode)) {
|
||||
log.warn("[拉卡拉合单预下单] 响应码异常, orderId={}, code={}, msg={}", orderId, responseCode, respBody.getStr("msg"));
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "拉卡拉合单交易失败:" + respBody.getStr("msg")).set("resp_data", null);
|
||||
}
|
||||
|
||||
log.info("[拉卡拉合单预下单] 支付成功,准备保存订单记录, orderId={} shopping_fee_inner={}", orderId, agentAmountInt);
|
||||
// 新增一个拉卡拉订单记录 shop_order_lkl 表
|
||||
JSONObject lklPayReqAndRespJson = new JSONObject();
|
||||
reqData.set("shopping_fee_inner", agentAmountInt); // 平台内部最低配送费,单位(分)
|
||||
|
||||
lklPayReqAndRespJson.put("req", reqData);
|
||||
lklPayReqAndRespJson.put("resp", respBody); // 返回原始响应数据
|
||||
shopService.lklPayAddShopOrderLkl(lklPayReqAndRespJson);
|
||||
|
||||
// 8. 返回响应结果
|
||||
try {
|
||||
// 新增 shopOrderLkl 记录
|
||||
shopService.lklPayAddShopOrderLkl(lklPayReqAndRespJson);
|
||||
log.debug("[拉卡拉合单预下单] 订单记录保存成功, orderId={}", orderId);
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉合单预下单] 保存订单记录失败, orderId={}", orderId, e);
|
||||
// 不中断主流程,仅记录错误
|
||||
}
|
||||
|
||||
// 7. 返回响应结果
|
||||
log.info("[拉卡拉合单预下单] 处理完成, orderId={}, responseCode={}", orderId, responseCode);
|
||||
return respBody;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("拉卡拉合单交易出错,订单号:{},错误信息:", orderId, e);
|
||||
throw new ApiException("拉卡拉合单交易出错:" + e.getMessage(), e);
|
||||
log.error("[拉卡拉合单预下单] 系统异常, merchantNo={}, termNo={}, orderId={}", merchantNo, termNo, orderId, e);
|
||||
return new JSONObject().set("code", "BBS00001").set("msg", "系统异常:" + e.getMessage()).set("resp_data", null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +506,7 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
* 参考地址:https://o.lakala.com/#/home/document/detail?id=113
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param outTradeNo 退货订单号,如: FX-20241214-1
|
||||
* @param outTradeNo 外部交易订单号
|
||||
* @param originTradeNo 原拉卡拉交易流水号
|
||||
* @param refundAmount 退款金额(单位:分)
|
||||
* @param refundReason 退款原因
|
||||
@ -348,33 +517,58 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
@Override
|
||||
public Pair<Boolean, String> innerLklRefund(Integer storeId, String outTradeNo, String originTradeNo, String refundAmount, String refundReason, String lklMerchantNo, String lklTermNo) {
|
||||
try {
|
||||
log.info("开始执行拉卡拉内部退款,参数: storeId={}, outTradeNo={}, originTradeNo={}, refundAmount={}, refundReason={}",
|
||||
log.info("[拉卡拉退款] 开始执行拉卡拉内部退款,参数: storeId={}, outTradeNo={}, originTradeNo={}, refundAmount={}, refundReason={}",
|
||||
storeId, outTradeNo, originTradeNo, refundAmount, refundReason);
|
||||
|
||||
// 1. 获取请求IP
|
||||
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
||||
if (attributes == null) {
|
||||
log.error("无法获取HttpServletRequest,退款失败");
|
||||
log.error("[拉卡拉退款] 无法获取HttpServletRequest,退款失败");
|
||||
return Pair.of(false, I18nUtil._("系统异常,无法获取请求信息!"));
|
||||
}
|
||||
HttpServletRequest request = attributes.getRequest();
|
||||
String requestIp = IpKit.getRealIp(request);
|
||||
|
||||
// 2. 校验参数
|
||||
if (ObjectUtil.isEmpty(storeId) || org.apache.commons.lang3.StringUtils.isAnyBlank(outTradeNo, refundAmount, originTradeNo)) {
|
||||
log.warn("退款请求参数不完整: storeId={}, outTradeNo={}, originTradeNo={}, refundAmount={}, requestIp={}", storeId, outTradeNo, originTradeNo, refundAmount, requestIp);
|
||||
return Pair.of(false, I18nUtil._("缺少必要参数,退款失败!"));
|
||||
if (ObjectUtil.isEmpty(storeId)) {
|
||||
log.warn("[拉卡拉退款] 店铺ID不能为空: storeId={}", storeId);
|
||||
return Pair.of(false, I18nUtil._("店铺ID不能为空,退款失败!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(outTradeNo)) {
|
||||
log.warn("[拉卡拉退款] 外部交易订单号不能为空: outTradeNo={}", outTradeNo);
|
||||
return Pair.of(false, I18nUtil._("外部交易订单号不能为空,退款失败!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(originTradeNo)) {
|
||||
log.warn("[拉卡拉退款] 原拉卡拉交易流水号不能为空: originTradeNo={}", originTradeNo);
|
||||
return Pair.of(false, I18nUtil._("原拉卡拉交易流水号不能为空,退款失败!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(refundAmount)) {
|
||||
log.warn("[拉卡拉退款] 退款金额不能为空: refundAmount={}", refundAmount);
|
||||
return Pair.of(false, I18nUtil._("退款金额不能为空,退款失败!"));
|
||||
}
|
||||
|
||||
// 校验退款金额格式
|
||||
if (!refundAmount.matches("\\d+") || Integer.parseInt(refundAmount) <= 0) {
|
||||
log.warn("退款金额不合法: refundAmount={}", refundAmount);
|
||||
log.warn("[拉卡拉退款] 退款金额不合法: refundAmount={}", refundAmount);
|
||||
return Pair.of(false, I18nUtil._("退款金额不合法!"));
|
||||
}
|
||||
|
||||
// 3. 初始化拉卡拉SDK
|
||||
initLKLSDK();
|
||||
|
||||
// 4. 获取店铺的拉卡拉商户号和终端号
|
||||
ShopStoreBase shopStoreBase = shopService.getLklMerchantNoAndTermNo(storeId);
|
||||
if (shopStoreBase == null || org.apache.commons.lang3.StringUtils.isAnyBlank(shopStoreBase.getLkl_merchant_no(), shopStoreBase.getLkl_term_no())) {
|
||||
log.error("无法获取店铺的拉卡拉商户号或终端号: storeId={}", storeId);
|
||||
if (shopStoreBase == null) {
|
||||
log.error("[拉卡拉退款] 无法获取店铺信息: storeId={}", storeId);
|
||||
return Pair.of(false, I18nUtil._("无法获取店铺信息,退款失败!"));
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(shopStoreBase.getLkl_merchant_no()) || StrUtil.isBlank(shopStoreBase.getLkl_term_no())) {
|
||||
log.error("[拉卡拉退款] 无法获取店铺的拉卡拉商户号或终端号: storeId={}, merchantNo={}, termNo={}",
|
||||
storeId, shopStoreBase.getLkl_merchant_no(), shopStoreBase.getLkl_term_no());
|
||||
return Pair.of(false, I18nUtil._("缺少商户号参数,退款失败!"));
|
||||
}
|
||||
|
||||
@ -393,37 +587,44 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
|
||||
refundRequest.setLocationInfo(new V3LabsTradeLocationInfo(requestIp, null, ""));
|
||||
|
||||
log.info("拉卡拉退款请求参数: {}", JSONUtil.toJsonStr(refundRequest));
|
||||
log.info("[拉卡拉退款] 请求参数: {}", JSONUtil.toJsonStr(refundRequest));
|
||||
|
||||
String responseString = LKLSDK.httpPost(refundRequest);
|
||||
// 6. 处理响应
|
||||
if (StrUtil.isBlank(responseString)) {
|
||||
log.error("拉卡拉退款接口无响应");
|
||||
log.error("[拉卡拉退款] 拉卡拉退款接口无响应");
|
||||
return Pair.of(false, I18nUtil._("服务端无返回值,退款失败!"));
|
||||
}
|
||||
|
||||
log.info("拉卡拉退款接口响应: {}", responseString);
|
||||
log.info("[拉卡拉退款] 拉卡拉退款接口响应: {}", responseString);
|
||||
|
||||
JSONObject lakalaResponseJson = JSONUtil.parseObj(responseString);
|
||||
if (lakalaResponseJson == null) {
|
||||
log.error("拉卡拉退款接口返回值解析失败: responseString={}", responseString);
|
||||
log.error("[拉卡拉退款] 拉卡拉退款接口返回值解析失败: responseString={}", responseString);
|
||||
return Pair.of(false, I18nUtil._("返回值解析失败,退款失败!"));
|
||||
}
|
||||
if (!"BBS00000".equals(lakalaResponseJson.getStr("code"))) {
|
||||
|
||||
String responseCode = lakalaResponseJson.getStr("code");
|
||||
if (StrUtil.isBlank(responseCode)) {
|
||||
log.error("[拉卡拉退款] 拉卡拉退款响应码为空: response={}", responseString);
|
||||
return Pair.of(false, I18nUtil._("返回值格式错误,退款失败!"));
|
||||
}
|
||||
|
||||
if (!"BBS00000".equals(responseCode)) {
|
||||
String errorMessage = lakalaResponseJson.getStr("msg", "未知错误");
|
||||
log.error("拉卡拉退款失败, 错误信息: {}", errorMessage);
|
||||
log.error("[拉卡拉退款] 拉卡拉退款失败, 错误信息: {}, 响应码: {}", errorMessage, responseCode);
|
||||
return Pair.of(false, I18nUtil._(errorMessage));
|
||||
}
|
||||
|
||||
JSONObject responseData = lakalaResponseJson.getJSONObject("resp_data");
|
||||
log.info("拉卡拉退款成功: outTradeNo={}", outTradeNo);
|
||||
log.info("[拉卡拉退款] 拉卡拉退款成功: outTradeNo={}", outTradeNo);
|
||||
return Pair.of(true, responseData == null ? "" : responseData.toString());
|
||||
|
||||
} catch (SDKException e) {
|
||||
log.error("拉卡拉退款SDK异常: ", e);
|
||||
log.error("[拉卡拉退款] 拉卡拉退款SDK异常: ", e);
|
||||
return Pair.of(false, I18nUtil._("拉卡拉退款SDK异常,退款失败!") + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("拉卡拉退款发生未知异常: ", e);
|
||||
log.error("[拉卡拉退款] 拉卡拉退款发生未知异常: ", e);
|
||||
return Pair.of(false, I18nUtil._("拉卡拉退款发生未知异常,退款失败!") + e.getMessage());
|
||||
}
|
||||
}
|
||||
@ -468,331 +669,4 @@ public class LakalaPayServiceImpl implements LakalaPayService {
|
||||
throw new ApiException(I18nUtil._("文件上传失败!"), e);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public CommonResult applyLedgerMer(JSONObject paramsJSON) {
|
||||
// // 1. 配置初始化
|
||||
// initLKLSDK();
|
||||
//
|
||||
// //2. 装配数据
|
||||
// V2MmsOpenApiLedgerApplyLedgerMerRequest req = new V2MmsOpenApiLedgerApplyLedgerMerRequest();
|
||||
// req.setVersion("2.0");
|
||||
// String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数
|
||||
// req.setOrderNo(orderNo);
|
||||
// req.setOrgCode(orgCode);
|
||||
// req.setMerInnerNo(paramsJSON.getStr("merInnerNo"));
|
||||
// req.setMerCupNo(paramsJSON.getStr("merCupNo"));
|
||||
// req.setContactMobile(paramsJSON.getStr("contactMobile"));
|
||||
// req.setSplitLowestRatio(new BigDecimal(paramsJSON.getStr("splitLowestRatio")));
|
||||
// String fileName = paramsJSON.getStr("splitEntrustFileName");
|
||||
// req.setSplitEntrustFileName(fileName);
|
||||
//
|
||||
// // 分账结算委托书文件上传到拉卡拉服务器
|
||||
// JSONObject fileUploadResp = uploadFile(orderNo, "SPLIT_ENTRUST_FILE", StringUtils.getFileExt(fileName), paramsJSON.getStr("splitEntrustFile"));
|
||||
// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) {
|
||||
// throw new ApiException(I18nUtil._("分账结算委托书上传失败!"));
|
||||
// }
|
||||
//
|
||||
// String splitEntrustFilePath = fileUploadResp.getStr("attFileId");
|
||||
// req.setSplitEntrustFilePath(splitEntrustFilePath); //比如:G1/M00/06/64/CrFdEmBQc-aAGc_XAAAiIbS3WIE960.pdf;
|
||||
//
|
||||
// if (isProdProject()) {
|
||||
// projectDomain = projectDomain + "/api";
|
||||
// }
|
||||
// // 给拉卡拉通知的回调地址
|
||||
// String retUrl = projectDomain + "/mobile/pay/lakala/ledger/applyLedgerMerNotify";
|
||||
// req.setRetUrl(retUrl);
|
||||
//
|
||||
// paramsJSON.set("orderNo", orderNo);
|
||||
// paramsJSON.set("version", "2.0");
|
||||
// paramsJSON.set("ret_url", retUrl);
|
||||
// paramsJSON.set("org_code", orgCode);
|
||||
// paramsJSON.set("split_entrust_file_path", splitEntrustFilePath);
|
||||
//
|
||||
// try {
|
||||
// //3. 发送请求
|
||||
// String responseStr = LKLSDK.httpPost(req);
|
||||
//
|
||||
// // {'retCode':'000000','retMsg':'申请已受理,请等待审核结果','respData':{'version':'1.0','orderNo':'KFPT20230223181025407788734','orgCode':'1','applyId':681201215598657536}}
|
||||
// JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr);
|
||||
// if (lakalaRespJSON == null || !lakalaRespJSON.getStr("retCode").equals("000000")) {
|
||||
// throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg")));
|
||||
// }
|
||||
//
|
||||
// paramsJSON.set("apply_id", lakalaRespJSON.getByPath("respData.applyId"));
|
||||
// paramsJSON.set("remark", lakalaRespJSON.getStr("retMsg"));
|
||||
// paramsJSON.set("audit_status_text", paramsJSON.get("remark"));
|
||||
//
|
||||
// // 新增数据
|
||||
// // 将 JSON 对象的键名转换为下划线命名
|
||||
// LklLedgerMember lklLedgerMember = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerMember.class);
|
||||
// lklLedgerMemberService.saveOrUpdateByMerCupNo(lklLedgerMember);
|
||||
//
|
||||
// return CommonResult.success(null, "提交成功,待审核中!");
|
||||
// } catch (SDKException e) {
|
||||
// log.error("分账申请失败:", e);
|
||||
// throw new ApiException(I18nUtil._("分账申请失败!"), e);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 商户分账业务开通申请回调
|
||||
// * 参考:https://o.lakala.com/#/home/document/detail?id=379
|
||||
// *
|
||||
// * @param request
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// public JSONObject applyLedgerMerNotify(HttpServletRequest request) {
|
||||
// // 验签
|
||||
// String authorization = request.getHeader("Authorization");
|
||||
// String requestBody = LakalaUtil.getBody(request);
|
||||
//
|
||||
// boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath);
|
||||
// if (!checkSuccess) {
|
||||
// return JSONUtil.createObj().set("retCode", "OP90002").set("retMsg", "验签失败!");
|
||||
// }
|
||||
//
|
||||
// JSONObject paramsJSON = JSONUtil.parseObj(requestBody);
|
||||
//
|
||||
// JSONObject respData = new JSONObject();
|
||||
// respData.put("retCode", "OP90003");
|
||||
// respData.put("retMsg", "响应处理失败!");
|
||||
//
|
||||
// if (paramsJSON != null && paramsJSON.get("respData") != null) {
|
||||
// JSONObject reqData = (JSONObject) paramsJSON.get("respData");
|
||||
//
|
||||
// Boolean success = lklLedgerMemberService.updateAuditResult(reqData.getStr("applyId"),
|
||||
// reqData.getStr("merInnerNo"),
|
||||
// reqData.getStr("merCupNo"),
|
||||
// reqData.getStr("entrustFileName"),
|
||||
// reqData.getStr("entrustFilePath"),
|
||||
// reqData.getStr("auditStatus"),
|
||||
// reqData.getStr("auditStatusText"),
|
||||
// reqData.getStr("remark"));
|
||||
//
|
||||
// if (success) {
|
||||
// respData.put("retCode", "000000");
|
||||
// respData.put("retMsg", "操作成功!");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return respData;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 分账接收方创建申请
|
||||
// * 参考:https://o.lakala.com/#/home/document/detail?id=380
|
||||
// *
|
||||
// * @param paramsJSON
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// public CommonResult applyLedgerReceiver(JSONObject paramsJSON) {
|
||||
// // 1. 配置初始化
|
||||
// initLKLSDK();
|
||||
//
|
||||
// //2. 装配数据
|
||||
// V2MmsOpenApiLedgerApplyLedgerReceiverRequest req = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest();
|
||||
//
|
||||
// String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数
|
||||
// req.setOrderNo(orderNo);
|
||||
// req.setOrgCode(orgCode);
|
||||
// req.setVersion("2.0");
|
||||
//
|
||||
// req.setReceiverName(paramsJSON.getStr("receiverName"));
|
||||
// req.setContactMobile(paramsJSON.getStr("contactMobile"));
|
||||
//
|
||||
// req.setLicenseNo(paramsJSON.getStr("licenseNo"));
|
||||
// req.setLicenseName(paramsJSON.getStr("licenseName"));
|
||||
// req.setLegalPersonName(paramsJSON.getStr("legalPersonName"));
|
||||
// req.setLegalPersonCertificateType(paramsJSON.getStr("legalPersonCertificateType"));
|
||||
// req.setLegalPersonCertificateNo(paramsJSON.getStr("legalPersonCertificateNo"));
|
||||
//
|
||||
// req.setAcctNo(paramsJSON.getStr("acctNo"));
|
||||
// req.setAcctName(paramsJSON.getStr("acctName"));
|
||||
// req.setAcctTypeCode(paramsJSON.getStr("acctTypeCode"));
|
||||
// req.setAcctCertificateType(paramsJSON.getStr("acctCertificateType"));
|
||||
//
|
||||
// req.setAcctCertificateNo(paramsJSON.getStr("acctCertificateNo"));
|
||||
// req.setAcctOpenBankCode(paramsJSON.getStr("acctOpenBankCode"));
|
||||
// req.setAcctOpenBankName(paramsJSON.getStr("acctOpenBankName"));
|
||||
// req.setAcctClearBankCode(paramsJSON.getStr("acctClearBankCode"));
|
||||
//
|
||||
// if (paramsJSON.getJSONArray("attachList") != null && paramsJSON.getJSONArray("attachList").size() > 0) {
|
||||
// List<V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo> attachList = new ArrayList<>();
|
||||
// V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo attachInfo = new V2MmsOpenApiLedgerApplyLedgerReceiverRequest.AttachInfo();
|
||||
// for (JSONObject attachJSON : paramsJSON.getJSONArray("attachList").jsonIter()) {
|
||||
// String fileName = attachJSON.getStr("attachName");
|
||||
// String attachType = attachJSON.getStr("attachType");
|
||||
// String fileBase64 = attachJSON.getStr("attachStoreFile");
|
||||
// attachInfo.setAttachName(fileName);
|
||||
// attachInfo.setAttachType(attachType);
|
||||
//
|
||||
// JSONObject fileUploadResp = uploadFile(StringUtils.genLklOrderNo(8), attachType,
|
||||
// StringUtils.getFileExt(fileName), fileBase64);
|
||||
// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) {
|
||||
// throw new ApiException(I18nUtil._("附件上传失败!"));
|
||||
// }
|
||||
//
|
||||
// attachInfo.setAttachStorePath(fileUploadResp.getStr("attFileId"));
|
||||
// attachList.add(attachInfo);
|
||||
// }
|
||||
//
|
||||
// req.setAttachList(attachList);
|
||||
// paramsJSON.set("attach_list", JSONUtil.toJsonStr(attachList));
|
||||
// }
|
||||
//
|
||||
// paramsJSON.set("orderNo", orderNo);
|
||||
// paramsJSON.set("version", "2.0");
|
||||
// paramsJSON.set("org_code", orgCode);
|
||||
//
|
||||
// try {
|
||||
// //3. 发送请求,申请创建分账接收方
|
||||
// String responseStr = LKLSDK.httpPost(req);
|
||||
//
|
||||
// JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr);
|
||||
// if (lakalaRespJSON == null || !lakalaRespJSON.getStr("retCode").equals("000000")) {
|
||||
// throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg")));
|
||||
// }
|
||||
//
|
||||
// paramsJSON.set("receiver_no", lakalaRespJSON.getByPath("respData.receiverNo"));
|
||||
// paramsJSON.set("org_id", lakalaRespJSON.getByPath("respData.orgId"));
|
||||
// paramsJSON.set("org_name", lakalaRespJSON.getByPath("respData.orgName"));
|
||||
//
|
||||
// // 新增数据
|
||||
// // 将 JSON 对象的键名转换为下划线命名
|
||||
// LklLedgerReceiver lklLedgerReceiver = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerReceiver.class);
|
||||
//
|
||||
// // 新增或修改本地数据
|
||||
// lklLedgerReceiverService.saveOrUpdateByReceiverNo(lklLedgerReceiver);
|
||||
//
|
||||
// return CommonResult.success(null, "接收方创建成功!");
|
||||
// } catch (SDKException e) {
|
||||
// log.error("接收方创建失败:", e);
|
||||
// throw new ApiException(I18nUtil._("接收方创建失败!"), e);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 分账关系绑定申请
|
||||
// * 参考:https://o.lakala.com/#/home/document/detail?id=386
|
||||
// *
|
||||
// * @param paramsJSON
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// public CommonResult applyLedgerMerReceiverBind(JSONObject paramsJSON) {
|
||||
// // 1. 配置初始化
|
||||
// initLKLSDK();
|
||||
//
|
||||
// //2. 装配数据
|
||||
// V2MmsOpenApiLedgerApplyBindRequest req = new V2MmsOpenApiLedgerApplyBindRequest();
|
||||
//
|
||||
// String orderNo = StringUtils.genLklOrderNo(8); // 8位随机数
|
||||
// req.setOrderNo(orderNo);
|
||||
// req.setOrgCode(orgCode);
|
||||
// req.setVersion("2.0");
|
||||
//
|
||||
// req.setMerInnerNo(paramsJSON.getStr("merInnerNo"));
|
||||
// req.setMerCupNo(paramsJSON.getStr("merCupNo"));
|
||||
// req.setReceiverNo(paramsJSON.getStr("receiverNo"));
|
||||
//
|
||||
// String fileName = paramsJSON.getStr("entrustFileName");
|
||||
// String splitEntrustFileBase64 = paramsJSON.getStr("entrustFile");
|
||||
// req.setEntrustFileName(fileName);
|
||||
//
|
||||
// String retUrl = projectDomain + "/mobile/pay/lakala/ledger/applyLedgerMerReceiverBindNotify";
|
||||
// req.setRetUrl(retUrl);
|
||||
//
|
||||
// // 文件上传到拉卡拉服务器
|
||||
// JSONObject fileUploadResp = uploadFile(orderNo,
|
||||
// "SPLIT_COOPERATION_FILE",
|
||||
// StringUtils.getFileExt(fileName),
|
||||
// splitEntrustFileBase64);
|
||||
// if (fileUploadResp == null || StrUtil.isBlank(fileUploadResp.getStr("attFileId"))) {
|
||||
// throw new ApiException(I18nUtil._("合作协议上传失败!"));
|
||||
// }
|
||||
//
|
||||
// String entrustFilePath = fileUploadResp.getStr("attFileId");
|
||||
// req.setEntrustFilePath(entrustFilePath);
|
||||
//
|
||||
// paramsJSON.set("orderNo", orderNo);
|
||||
// paramsJSON.set("version", "2.0");
|
||||
// paramsJSON.set("ret_url", retUrl);
|
||||
// paramsJSON.set("org_code", orgCode);
|
||||
// paramsJSON.set("entrust_file_name", fileName);
|
||||
// paramsJSON.set("entrust_file_path", entrustFilePath);
|
||||
//
|
||||
// try {
|
||||
// //3. 发送请求
|
||||
// String responseStr = LKLSDK.httpPost(req);
|
||||
//
|
||||
// JSONObject lakalaRespJSON = JSONUtil.parseObj(responseStr);
|
||||
// if (lakalaRespJSON == null || !lakalaRespJSON.getStr("retCode").equals("000000")) {
|
||||
// throw new ApiException(I18nUtil._(lakalaRespJSON.getStr("retMsg")));
|
||||
// }
|
||||
//
|
||||
// paramsJSON.set("apply_id", lakalaRespJSON.getByPath("respData.applyId"));
|
||||
// paramsJSON.set("remark", lakalaRespJSON.getStr("retMsg"));
|
||||
//
|
||||
// // 新增数据
|
||||
// // 将 JSON 对象的键名转换为下划线命名
|
||||
// LklLedgerMerReceiverBind lklLedgerMerReceiverBind = JSONUtil.toBean(StringUtils.convertCamelToSnake(paramsJSON.toString()), LklLedgerMerReceiverBind.class);
|
||||
// lklLedgerMerReceiverBindService.saveOrUpdateByMerCupNoReceiverNo(lklLedgerMerReceiverBind);
|
||||
//
|
||||
// return CommonResult.success(null, "提交成功,待审核中!");
|
||||
// } catch (SDKException e) {
|
||||
// log.error("分账绑定关系申请失败:", e);
|
||||
// throw new ApiException(I18nUtil._("分账绑定关系申请失败!"), e);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 分账关系绑定申请回调
|
||||
// * 参考:https://o.lakala.com/#/home/document/detail?id=379
|
||||
// *
|
||||
// * @param request
|
||||
// * @return
|
||||
// */
|
||||
// @Override
|
||||
// public JSONObject applyLedgerMerReceiverBindNotify(HttpServletRequest request) {
|
||||
// // 验签
|
||||
// String authorization = request.getHeader("Authorization");
|
||||
// String requestBody = LakalaUtil.getBody(request);
|
||||
//
|
||||
// boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath);
|
||||
// if (!checkSuccess) {
|
||||
// return JSONUtil.createObj().set("retCode", "OP90002").set("retMsg", "验签失败!");
|
||||
// }
|
||||
//
|
||||
//// String requestBody = LakalaUtil.getBody(request);
|
||||
//
|
||||
// JSONObject paramsJSON = JSONUtil.parseObj(requestBody);
|
||||
// JSONObject respData = new JSONObject();
|
||||
// respData.put("retCode", "OP90003");
|
||||
// respData.put("retMsg", "响应处理失败!");
|
||||
//
|
||||
// if (paramsJSON != null && paramsJSON.get("respData") != null) {
|
||||
// JSONObject reqData = (JSONObject) paramsJSON.get("respData");
|
||||
//
|
||||
// Boolean success = lklLedgerMerReceiverBindService.updateAuditResult(reqData.getStr("applyId"),
|
||||
// reqData.getStr("merInnerNo"),
|
||||
// reqData.getStr("merCupNo"),
|
||||
// reqData.getStr("receiverNo"),
|
||||
// reqData.getStr("entrustFileName"),
|
||||
// reqData.getStr("entrustFilePath"),
|
||||
// reqData.getStr("auditStatus"),
|
||||
// reqData.getStr("auditStatusText"),
|
||||
// reqData.getStr("remark"));
|
||||
// if (success) {
|
||||
// respData.put("retCode", "000000");
|
||||
// respData.put("retMsg", "操作成功!");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return respData;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -52,6 +52,7 @@ import com.suisung.mall.common.modules.pay.*;
|
||||
import com.suisung.mall.common.modules.pay.dto.ItemActivityInfoDTO;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
||||
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
||||
import com.suisung.mall.common.service.impl.CommonService;
|
||||
import com.suisung.mall.common.utils.CheckUtil;
|
||||
import com.suisung.mall.common.utils.I18nUtil;
|
||||
import com.suisung.mall.common.utils.LogUtil;
|
||||
@ -675,9 +676,17 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
cn.hutool.json.JSONObject lakalaRespJSON = new cn.hutool.json.JSONObject();
|
||||
|
||||
BigDecimal shippingFee = shopService.getOrderShippingFee(out_trade_no);
|
||||
logger.debug("预支付时,查到的订单{},运费:{}", out_trade_no, shippingFee);
|
||||
if (shippingFee == null || shippingFee.intValue() <= 0) {
|
||||
shippingFee = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
if (shippingFee == null || shippingFee.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
// 平台最低配送费,单位(分)
|
||||
Integer innerMinDeliverFee = accountBaseConfigService.getInnerMinDeliveryFee();
|
||||
|
||||
logger.debug("预支付时,查到的订单{},商家配送费:{}元,平台最低配送费要求:{}分", out_trade_no, shippingFee, innerMinDeliverFee);
|
||||
|
||||
// 平台最低配送费,单位(分)
|
||||
if (innerMinDeliverFee == null || innerMinDeliverFee.intValue() <= 0) {
|
||||
// 没有运费
|
||||
// 拉卡拉预支付返回参数
|
||||
lakalaRespJSON = lakalaPayService.lklTransPreOrder(shopStoreBase.getLkl_merchant_no(), shopStoreBase.getLkl_term_no(),
|
||||
@ -686,11 +695,12 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
requestIP, trade_remark);
|
||||
} else { // 有运费的情况
|
||||
// 拉卡拉合单预支付返回参数
|
||||
// TODO RMK 这里只有固定一个运费代理商代收运费,以后代理商模块做好了,要动态读取店铺的运费代理商。
|
||||
// RMK 这里只有固定一个运费代理商代收运费,以后代理商模块做好了,要动态读取店铺的运费代理商。
|
||||
lakalaRespJSON = lakalaPayService.lklTransMergePreOrder(shopStoreBase.getLkl_merchant_no(), shopStoreBase.getLkl_term_no(),
|
||||
delivery_merchant_no, delivery_term_no, // 以后根据代理商动态分配
|
||||
appId, openId, storeIdStr, out_trade_no, subject, total_amt,
|
||||
Convert.toStr(shippingFee.multiply(BigDecimal.valueOf(100)).intValue()),
|
||||
// Convert.toStr(shippingFee.multiply(BigDecimal.valueOf(100)).intValue()),
|
||||
Convert.toStr(innerMinDeliverFee),
|
||||
notifyUrl,
|
||||
requestIP, trade_remark);
|
||||
}
|
||||
@ -1344,51 +1354,69 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理拉卡拉支付异步通知
|
||||
* 该方法用于接收并处理来自拉卡拉支付平台的异步通知,包括验签、解析参数、更新订单状态等操作
|
||||
*
|
||||
* @param request HTTP请求对象,包含拉卡拉支付平台发送的异步通知数据
|
||||
* @return String 返回处理结果,用于响应拉卡拉支付平台
|
||||
*/
|
||||
@Override
|
||||
public String lklNotifyUrl(HttpServletRequest request) {
|
||||
Map<String, String> params;
|
||||
try {
|
||||
logger.info("[拉卡拉支付通知] 开始处理拉卡拉支付异步通知");
|
||||
|
||||
// 初始化拉卡拉SDK
|
||||
lakalaPayService.initLKLSDK();
|
||||
log.debug("[拉卡拉支付通知] 拉卡拉SDK初始化完成");
|
||||
|
||||
// 读取请求体并验签
|
||||
String body = LKLSDK.notificationHandle(request);
|
||||
String authorization = request.getHeader("Authorization");
|
||||
logger.debug("[拉卡拉支付通知] 接收到通知数据,body长度={},authorization长度={}",
|
||||
body != null ? body.length() : 0,
|
||||
authorization != null ? authorization.length() : 0);
|
||||
|
||||
if (StrUtil.isBlank(body)) {
|
||||
log.warn("[拉卡拉支付通知] 验签失败,请求体为空");
|
||||
return lklNotifyMsg(false, "验签失败!");
|
||||
}
|
||||
|
||||
// 敏感头信息脱敏打印
|
||||
logger.info("拉卡拉支付异步通知回调 body:{} \n authorization: {}", body, authorization);
|
||||
// 异步通知返回的body json数据:{"out_trade_no":"202203151637334864280014","trade_no":"2022031566210203291925","log_no":"66210203291925",
|
||||
// "acc_trade_no":"2022031522001483661454130929 ","trade_status":"SUCCESS","trade_state":"SUCCESS","total_amount":"1",
|
||||
// "payer_amount":"1","acc_settle_amount":"1","trade_time":"20220315163808","user_id1":"app***@163.com",
|
||||
// "user_id2":"2088432881453660","notify_url":"https://www.baidu.com","account_type":"ALIPAY","card_type":"99"}
|
||||
|
||||
// 合单返回的数据:{"out_trade_no":"DD-20250830-10","trade_no":"20250830110113130266250034160499","log_no":"66250034160499","acc_trade_no":"4200002826202508306761393882","trade_status":"SUCCESS","trade_state":"SUCCESS","total_amount":"2","payer_amount":"2","acc_settle_amount":"2","acc_mdiscount_amount":"0","acc_discount_amount":"0","trade_time":"20250830180435","user_id1":"oDVKR7T0qxg6O8tqIL9SgY6LXqqQ","user_id2":"oVxsc1QRAqDRv_gAmXuLZwSVSL18","notify_url":"https://mall.gpxscs.cn/mobile/pay/index/lkl_wxPay_notify_url","account_type":"WECHAT","bank_type":"OTHERS","card_type":"02","merchant_no":"8226330541100GU","remark":"","sub_mch_id":"803819329","out_split_rsp_infos":[{"sub_trade_no":"20250830110113130266250034112794","sub_log_no":"66250034112794","out_sub_trade_no":"ORD_DD-20250830-10","merchant_no":"8226330541100GU","term_no":"N5817779","amount":"1","settle_type":"0"},{"sub_trade_no":"20250830110113130266250034160498","sub_log_no":"66250034160498","out_sub_trade_no":"DF_DD-20250830-10","merchant_no":"822584059990FYP","term_no":"N5811590","amount":"1","settle_type":"0"}],"trade_req_date":"20250830","gb_amount":"","qb_amount":""}
|
||||
logger.info("[拉卡拉支付通知] 拉卡拉支付异步通知回调 body:{} \n authorization: {}",
|
||||
body, authorization != null ? "***" : "null");
|
||||
|
||||
// 解析JSON格式响应
|
||||
cn.hutool.json.JSONObject lklNotifyRespJSON = JSONUtil.parseObj(body);
|
||||
logger.debug("[拉卡拉支付通知] 解析JSON完成: keys={}", lklNotifyRespJSON.keySet());
|
||||
|
||||
params = Convert.toMap(String.class, String.class, lklNotifyRespJSON);
|
||||
String orderId = params.getOrDefault("out_trade_no", "");
|
||||
String accTradeNo = params.getOrDefault("acc_trade_no", ""); // 需要跟拉卡拉确认这个字段是原支付交易对应的微信订单号吗?
|
||||
String outSplitRspInfos = params.getOrDefault("out_split_rsp_infos", ""); // 拉卡拉合单订单信息
|
||||
String tradeStatus = params.getOrDefault("trade_status", "");
|
||||
String accTradeNo = params.getOrDefault("acc_trade_no", "");
|
||||
String outSplitRspInfos = params.getOrDefault("out_split_rsp_infos", "");
|
||||
|
||||
logger.info("[拉卡拉支付通知] 核心参数 - 订单号:{} 状态:{} 是否合单:{}",
|
||||
orderId, tradeStatus, StrUtil.isNotBlank(outSplitRspInfos) ? "是" : "否");
|
||||
|
||||
// 提取授权签名信息
|
||||
Map<String, String> authMap = LakalaUtil.getLakalaAuthorizationMap(authorization);
|
||||
if (authMap != null && authMap.containsKey("signature")) {
|
||||
params.put("sign", authMap.get("signature"));
|
||||
logger.debug("[拉卡拉支付通知] 签名信息提取成功");
|
||||
} else {
|
||||
logger.error("缺少签名信息");
|
||||
logger.error("[拉卡拉支付通知] 缺少签名信息,authMap={}", authMap);
|
||||
return lklNotifyMsg(false, "缺少签名信息");
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
logger.error("缺少out_trade_no字段");
|
||||
logger.error("[拉卡拉支付通知] 缺少out_trade_no字段,body={}", body);
|
||||
return lklNotifyMsg(false, "缺少out_trade_no字段");
|
||||
}
|
||||
|
||||
// 查询交易信息
|
||||
logger.debug("[拉卡拉支付通知] 查询交易信息: orderId={}", orderId);
|
||||
QueryWrapper<PayConsumeTrade> tradeQueryWrapper = new QueryWrapper<>();
|
||||
tradeQueryWrapper.eq("order_id", orderId);
|
||||
PayConsumeTrade trade_row_tmp = payConsumeTradeService.findOne(tradeQueryWrapper);
|
||||
@ -1397,25 +1425,32 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
String orderSubject = trade_row_tmp != null ? trade_row_tmp.getTrade_title() : "";
|
||||
Integer userId = trade_row_tmp != null ? trade_row_tmp.getBuyer_id() : 0;
|
||||
|
||||
logger.debug("[拉卡拉支付通知] 交易信息查询完成: storeId={} userId={}", payment_store_id, userId);
|
||||
|
||||
// 更新交易记录的 原支付交易对应的微信订单号 transaction_id
|
||||
if (trade_row_tmp != null && StrUtil.isNotBlank(accTradeNo)) {
|
||||
logger.debug("[拉卡拉支付通知] 更新交易记录的微信订单号: tradeId={} accTradeNo={}",
|
||||
trade_row_tmp.getConsume_trade_id(), accTradeNo);
|
||||
PayConsumeTrade payConsumeTradeUpd = new PayConsumeTrade();
|
||||
payConsumeTradeUpd.setConsume_trade_id(trade_row_tmp.getConsume_trade_id()).setTransaction_id(accTradeNo);
|
||||
payConsumeTradeService.updateTradeByPrimaryKey(payConsumeTradeUpd);
|
||||
}
|
||||
|
||||
// 查询支付渠道
|
||||
logger.debug("[拉卡拉支付通知] 查询支付渠道: channelCode=lakala");
|
||||
QueryWrapper<PayPaymentChannel> channelQueryWrapper = new QueryWrapper<>();
|
||||
channelQueryWrapper.eq("payment_channel_code", "lakala");
|
||||
PayPaymentChannel payPaymentChannel = payPaymentChannelService.findOne(channelQueryWrapper);
|
||||
|
||||
if (payPaymentChannel == null) {
|
||||
logger.error("支付渠道不存在");
|
||||
logger.error("[拉卡拉支付通知] 支付渠道不存在: channelCode=lakala");
|
||||
return lklNotifyMsg(false, "支付渠道不存在");
|
||||
}
|
||||
Integer payment_channel_id = payPaymentChannel.getPayment_channel_id();
|
||||
logger.debug("[拉卡拉支付通知] 支付渠道查询完成: channelId={}", payment_channel_id);
|
||||
|
||||
// 插入充值记录
|
||||
logger.debug("[拉卡拉支付通知] 创建充值记录");
|
||||
PayConsumeDeposit payConsumeDeposit = createNotify(params, payPaymentChannel);
|
||||
payConsumeDeposit.setOrder_id(orderId);
|
||||
payConsumeDeposit.setStore_id(payment_store_id); // 所属店铺
|
||||
@ -1425,47 +1460,89 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
payConsumeDeposit.setDeposit_body(orderSubject);
|
||||
payConsumeDeposit.setUser_id(userId);
|
||||
|
||||
// 设置拉卡拉相关参数
|
||||
String merchantNo = lklNotifyRespJSON.getStr("merchant_no");
|
||||
lklNotifyRespJSON.set("out_separate_no", orderId);// 默认非合单主单订单号
|
||||
lklNotifyRespJSON.set("lkl_sub_trade_no", lklNotifyRespJSON.getStr("trade_no"));// 默认非合单主单交易号
|
||||
lklNotifyRespJSON.set("lkl_sub_log_no", lklNotifyRespJSON.getStr("log_no")); // 默认非合单主单的对账流水号
|
||||
lklNotifyRespJSON.set("split_amt", lklNotifyRespJSON.getStr("total_amount")); // 默认非合单主单支付金额
|
||||
|
||||
logger.debug("[拉卡拉支付通知] 基础参数设置完成: out_separate_no={} lkl_sub_trade_no={} lkl_sub_log_no={} split_amt={}",
|
||||
lklNotifyRespJSON.getStr("out_separate_no"),
|
||||
lklNotifyRespJSON.getStr("lkl_sub_trade_no"),
|
||||
lklNotifyRespJSON.getStr("lkl_sub_log_no"),
|
||||
lklNotifyRespJSON.getStr("split_amt"));
|
||||
|
||||
// 拉卡拉订单合单信息
|
||||
if (StrUtil.isNotBlank(outSplitRspInfos)) {
|
||||
logger.debug("[拉卡拉支付通知] 处理合单信息");
|
||||
// [{"sub_trade_no":"20250830110113130266250034401288","merchant_no":"822584059990FYP","amount":"1","settle_type":"0","sub_log_no":"66250034401288","out_sub_trade_no":"DF-DD-20250830-21","term_no":"N5811590"},{"sub_trade_no":"20250830110113130266250034401289","merchant_no":"8226330541100GU","amount":"1","settle_type":"0","sub_log_no":"66250034401289","out_sub_trade_no":"ORD-DD-20250830-21","term_no":"N5817779"}]
|
||||
payConsumeDeposit.setLkl_combine_params(outSplitRspInfos);
|
||||
|
||||
Pair<String, String> subTradeNos = getLklSubTradeNo(outSplitRspInfos);
|
||||
if (subTradeNos != null) {
|
||||
// 商品子订单号
|
||||
payConsumeDeposit.setOrd_sub_trade_no(subTradeNos.getFirst());
|
||||
// 商品运费子订单号
|
||||
payConsumeDeposit.setDf_sub_trade_no(subTradeNos.getSecond());
|
||||
logger.debug("[拉卡拉支付通知] 子订单号解析完成: ord={} df={}",
|
||||
subTradeNos.getFirst(), subTradeNos.getSecond());
|
||||
}
|
||||
|
||||
// 获取拉卡拉合单商品子订单信息
|
||||
cn.hutool.json.JSONObject goodsOrderInfo = CommonService.getLklCombineSplitRespInfo(merchantNo, outSplitRspInfos, false);
|
||||
if (goodsOrderInfo != null) {
|
||||
lklNotifyRespJSON.set("out_separate_no", goodsOrderInfo.getStr("out_sub_trade_no"));// 合单商品子订单号
|
||||
lklNotifyRespJSON.set("lkl_sub_trade_no", goodsOrderInfo.getStr("sub_trade_no")); // 合单商品子订单的流水号
|
||||
lklNotifyRespJSON.set("lkl_sub_log_no", goodsOrderInfo.getStr("sub_log_no")); // 合单商品子订单的流水号
|
||||
lklNotifyRespJSON.set("split_amt", goodsOrderInfo.getStr("amount")); // 合单子商品订单支付金额
|
||||
|
||||
logger.debug("[拉卡拉支付通知] 合单信息更新完成: out_separate_no={} lkl_sub_trade_no={} lkl_sub_log_no={} split_amt={}",
|
||||
lklNotifyRespJSON.getStr("out_separate_no"),
|
||||
lklNotifyRespJSON.getStr("lkl_sub_trade_no"),
|
||||
lklNotifyRespJSON.getStr("lkl_sub_log_no"),
|
||||
lklNotifyRespJSON.getStr("split_amt"));
|
||||
}
|
||||
}
|
||||
|
||||
// 判断是否联合支付
|
||||
logger.debug("[拉卡拉支付通知] 查询联合支付信息: orderId={}", orderId);
|
||||
PayConsumeTradeCombine tradeCombine = payConsumeTradeCombineService.get(orderId);
|
||||
|
||||
TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
|
||||
try {
|
||||
logger.debug("[拉卡拉支付通知] 开始事务处理");
|
||||
|
||||
// 修改 拉卡拉的订单记录 shop_order_lkl
|
||||
logger.debug("[拉卡拉支付通知] 调用shopService更新拉卡拉订单记录");
|
||||
shopService.lklPayNotifyUpdateShopOrderLkl(lklNotifyRespJSON);
|
||||
log.debug("[拉卡拉支付通知] 拉卡拉订单记录更新完成");
|
||||
|
||||
if (tradeCombine != null && StrUtil.isNotBlank(tradeCombine.getOrder_ids())) {
|
||||
logger.debug("[拉卡拉支付通知] 处理联合支付: combinedOrderIds={}", tradeCombine.getOrder_ids());
|
||||
payConsumeDeposit.setOrder_id(tradeCombine.getOrder_ids());
|
||||
}
|
||||
|
||||
// 注:重要,支付完成接口调用
|
||||
log.debug("[拉卡拉支付通知] 调用支付完成处理接口");
|
||||
if (!payConsumeDepositService.processDeposit(payConsumeDeposit, BigDecimal.ZERO, BigDecimal.ZERO,
|
||||
BigDecimal.ZERO, BigDecimal.ZERO, BigDecimal.ZERO)) {
|
||||
log.error("[processDeposit]处理订单数据失败!");
|
||||
logger.error("[拉卡拉支付通知][processDeposit]处理订单数据失败!");
|
||||
return lklNotifyMsg(false, "支付失败!");
|
||||
}
|
||||
log.debug("[拉卡拉支付通知] 支付完成处理接口调用成功");
|
||||
|
||||
transactionManager.commit(transactionStatus);
|
||||
logger.info("[拉卡拉支付通知] 事务提交成功,支付处理完成: orderId={}", orderId);
|
||||
} catch (Exception e) {
|
||||
transactionManager.rollback(transactionStatus);
|
||||
log.error("接收拉卡拉支付异步通知出错:{}", e);
|
||||
logger.error("[拉卡拉支付通知] 事务处理出错,已回滚: orderId={}", orderId, e);
|
||||
throw new ApiException(e.getMessage());
|
||||
}
|
||||
|
||||
return lklNotifyMsg(true, "执行成功");
|
||||
} catch (Exception e) {
|
||||
logger.error("通知处理发生异常:{}", e.getMessage(), e);
|
||||
logger.error("[拉卡拉支付通知] 通知处理发生异常", e);
|
||||
return lklNotifyMsg(false, "通知处理发生异常!");
|
||||
}
|
||||
}
|
||||
@ -1900,13 +1977,13 @@ public class PayUserPayServiceImpl extends BaseServiceImpl<PayUserPayMapper, Pay
|
||||
}
|
||||
|
||||
// 6. 根据前缀分类处理
|
||||
if (outSubTradeNo.startsWith("ORD-")) {
|
||||
if (outSubTradeNo.startsWith(CommonConstant.Sep_GoodsFee_Prefix)) {
|
||||
// 处理商品订单
|
||||
if (productSubTradeNo != null) {
|
||||
logger.warn("检测到重复的商品订单子流水号:{}", outSubTradeNo);
|
||||
}
|
||||
productSubTradeNo = item.getStr("sub_trade_no");
|
||||
} else if (outSubTradeNo.startsWith("DF-")) {
|
||||
} else if (outSubTradeNo.startsWith(CommonConstant.Sep_DeliveryFee_Prefix)) {
|
||||
// 处理运费订单
|
||||
if (shippingSubTradeNo != null) {
|
||||
logger.warn("检测到重复的运费子流水号:{}", outSubTradeNo);
|
||||
|
||||
@ -131,25 +131,26 @@
|
||||
<artifactId>restful-sdk</artifactId>
|
||||
<version>1.0.0.6</version>
|
||||
</dependency>
|
||||
<!-- JavaCV库 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
<!-- 引入常用windows和linux平台 其他平台用到时在引入 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
<classifier>windows-x86_64</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
<!-- <!– JavaCV库 –>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.bytedeco</groupId>-->
|
||||
<!-- <artifactId>javacv</artifactId>-->
|
||||
<!-- <version>1.5.6</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- <!– JavaCV库 –>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.bytedeco</groupId>-->
|
||||
<!-- <artifactId>javacv-platform</artifactId>-->
|
||||
<!-- <version>1.5.6</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <!– FFmpeg平台依赖 –>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.bytedeco</groupId>-->
|
||||
<!-- <artifactId>ffmpeg-platform</artifactId>-->
|
||||
<!-- <version>4.4-1.5.6</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!-- rabbitMQ消息队列 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@ -272,6 +273,11 @@
|
||||
<version>4.1.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
|
||||
<dependency>
|
||||
@ -324,6 +330,21 @@
|
||||
<activation>
|
||||
<activeByDefault>true</activeByDefault>
|
||||
</activation>
|
||||
|
||||
<dependencies>
|
||||
<!-- 开发环境使用多平台依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg-platform</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
<!--开发环境 -->
|
||||
<profile>
|
||||
@ -334,6 +355,22 @@
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
|
||||
<dependencies>
|
||||
<!-- 开发环境使用多平台依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg-platform</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
<!--测试环境 -->
|
||||
<profile>
|
||||
@ -341,6 +378,22 @@
|
||||
<properties>
|
||||
<profiles.active>test</profiles.active>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- 开发环境使用多平台依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg-platform</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
<!--uat环境 -->
|
||||
<profile>
|
||||
@ -348,6 +401,22 @@
|
||||
<properties>
|
||||
<profiles.active>uat</profiles.active>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- 开发环境使用多平台依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv-platform</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg-platform</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
<!--生产环境 -->
|
||||
<profile>
|
||||
@ -355,6 +424,24 @@
|
||||
<properties>
|
||||
<profiles.active>prod</profiles.active>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- 生产环境仅包含Linux平台依赖 -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>javacv</artifactId>
|
||||
<version>1.5.6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Linux平台的FFmpeg -->
|
||||
<dependency>
|
||||
<groupId>org.bytedeco</groupId>
|
||||
<artifactId>ffmpeg</artifactId>
|
||||
<version>4.4-1.5.6</version>
|
||||
<classifier>linux-x86_64</classifier>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
@ -391,10 +478,12 @@
|
||||
<includeSystemScope>true</includeSystemScope>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.spotify</groupId>
|
||||
<artifactId>docker-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
|
||||
@ -39,6 +39,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -239,8 +240,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (yestodayOrderNum.getOrderNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (todayOrderNum.getOrderNum().subtract(yestodayOrderNum.getOrderNum())).divide(yestodayOrderNum.getOrderNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (todayOrderNum.getOrderNum().subtract(yestodayOrderNum.getOrderNum())).divide(yestodayOrderNum.getOrderNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -316,8 +317,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -370,8 +371,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -423,8 +424,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -483,8 +484,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -564,8 +565,8 @@ public class AnalyticsOrderServiceImpl implements AnalyticsOrderService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
@Service
|
||||
public class AnalyticsProductServiceImpl implements AnalyticsProductService {
|
||||
@ -51,8 +52,8 @@ public class AnalyticsProductServiceImpl implements AnalyticsProductService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
@ -91,8 +92,8 @@ public class AnalyticsReturnServiceImpl implements AnalyticsReturnService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -150,8 +151,8 @@ public class AnalyticsReturnServiceImpl implements AnalyticsReturnService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -205,8 +206,8 @@ public class AnalyticsReturnServiceImpl implements AnalyticsReturnService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -287,8 +288,8 @@ public class AnalyticsReturnServiceImpl implements AnalyticsReturnService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@ -52,8 +53,8 @@ public class AnalyticsStoreServiceImpl implements AnalyticsStoreService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (yestodayRegUser.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (todayRegUser.getNum().subtract(yestodayRegUser.getNum())).divide(yestodayRegUser.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (todayRegUser.getNum().subtract(yestodayRegUser.getNum())).divide(yestodayRegUser.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -113,8 +114,8 @@ public class AnalyticsStoreServiceImpl implements AnalyticsStoreService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preRegNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentRegNum.getNum().subtract(preRegNum.getNum())).divide(preRegNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@ -59,8 +60,8 @@ public class AnalyticsSysServiceImpl implements AnalyticsSysService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (yestodayVisits.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (todayVisits.getNum().subtract(yestodayVisits.getNum())).divide(yestodayVisits.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (todayVisits.getNum().subtract(yestodayVisits.getNum())).divide(yestodayVisits.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -105,8 +106,8 @@ public class AnalyticsSysServiceImpl implements AnalyticsSysService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -144,8 +145,8 @@ public class AnalyticsSysServiceImpl implements AnalyticsSysService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -207,8 +208,8 @@ public class AnalyticsSysServiceImpl implements AnalyticsSysService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
@ -270,8 +271,8 @@ public class AnalyticsSysServiceImpl implements AnalyticsSysService {
|
||||
// 计算日环比 日环比 = (当日数据 - 前一日数据) / 前一日数据 * 100%
|
||||
BigDecimal daym2m = BigDecimal.ZERO;
|
||||
if (preProductNum.getNum().compareTo(BigDecimal.ZERO) != 0) {
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, BigDecimal.ROUND_HALF_UP);
|
||||
//.multiply(new BigDecimal("100"));
|
||||
daym2m = (currentProductNum.getNum().subtract(preProductNum.getNum())).divide(preProductNum.getNum(), 2, RoundingMode.HALF_UP);
|
||||
//.multiply(new BigDecimal(100));
|
||||
} else {
|
||||
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ public class ShopBaseStoreCategoryAdminController {
|
||||
return CommonResult.failed(I18nUtil._("分成比例不能小于94"));
|
||||
}
|
||||
|
||||
if (shopBaseStoreCategory.getSplit_ratio().compareTo(new BigDecimal("100")) > 0) {
|
||||
if (shopBaseStoreCategory.getSplit_ratio().compareTo(new BigDecimal(100)) > 0) {
|
||||
return CommonResult.failed(I18nUtil._("分成比例不能大于100"));
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@ import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
import com.suisung.mall.shop.base.mapper.ShopBaseCrontabMapper;
|
||||
import com.suisung.mall.shop.base.service.ShopBaseCrontabService;
|
||||
import com.suisung.mall.shop.components.quartz.service.QuartzService;
|
||||
import com.suisung.mall.shop.components.quartz.service.impl.QuartzServiceImpl;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -24,16 +26,16 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* @author Xinze
|
||||
* @since 2021-08-16
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabMapper, ShopBaseCrontab> implements ShopBaseCrontabService {
|
||||
|
||||
@Autowired
|
||||
private QuartzService quartzService;
|
||||
|
||||
private final String JOB_NAME_PREFIX = "JOB_";
|
||||
private final String TRIGGER_NAME_PREFIX = "TRIGGER_";
|
||||
private final String JOB_GROUP_NAME = "DEFAULT_JOB_GROUP";
|
||||
private final String TRIGGER_GROUP_NAME = "DEFAULT_TRIGGER_GROUP";
|
||||
@Autowired
|
||||
private QuartzService quartzService;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@ -48,7 +50,7 @@ public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabM
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
|
||||
if (CheckUtil.isNotEmpty(crontab_enable)) {
|
||||
if (CheckUtil.isNotEmpty(crontab_enable) && crontab_enable == 1) {
|
||||
addJob(shopBaseCrontab);
|
||||
}
|
||||
} else {
|
||||
@ -58,9 +60,26 @@ public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabM
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
|
||||
quartzService.deleteJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME);
|
||||
if (crontab_enable == 1) {
|
||||
addJob(shopBaseCrontab);
|
||||
try {
|
||||
// 先尝试删除已存在的任务(如果存在)
|
||||
quartzService.deleteJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME);
|
||||
} catch (Exception e) {
|
||||
// 删除失败记录日志但不中断操作
|
||||
log.warn("删除旧任务时发生异常: 任务ID={}", crontab_id, e);
|
||||
}
|
||||
|
||||
// 如果任务启用,则添加新任务
|
||||
if (crontab_enable != null && crontab_enable == 1) {
|
||||
try {
|
||||
addJob(shopBaseCrontab);
|
||||
} catch (Exception e) {
|
||||
log.error("添加新任务时发生异常: 任务ID={}", crontab_id, e);
|
||||
// 如果添加失败,尝试从数据库同步任务
|
||||
boolean syncResult = syncSingleCrontabToScheduler(crontab_id);
|
||||
if (!syncResult) {
|
||||
throw new ApiException("添加定时任务失败,且同步任务也失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +91,13 @@ public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabM
|
||||
public boolean removeCrontab(String crontab_id) {
|
||||
boolean flag = remove(crontab_id);
|
||||
if (flag) {
|
||||
quartzService.deleteJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME);
|
||||
try {
|
||||
quartzService.deleteJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME);
|
||||
} catch (Exception e) {
|
||||
// 删除失败时,尝试从数据库同步确保一致性
|
||||
log.warn("删除任务时发生异常: 任务ID={}", crontab_id, e);
|
||||
// 即使删除失败也继续执行,避免阻塞业务流程
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
@ -84,10 +109,19 @@ public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabM
|
||||
*/
|
||||
@Override
|
||||
public void addJob(ShopBaseCrontab shopBaseCrontab) {
|
||||
String crontab_file = shopBaseCrontab.getCrontab_file();
|
||||
Integer crontab_id = shopBaseCrontab.getCrontab_id();
|
||||
String cron = getCron(shopBaseCrontab);
|
||||
quartzService.addJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME, TRIGGER_NAME_PREFIX + crontab_id, TRIGGER_GROUP_NAME, cron, crontab_file);
|
||||
try {
|
||||
String crontab_file = shopBaseCrontab.getCrontab_file();
|
||||
Integer crontab_id = shopBaseCrontab.getCrontab_id();
|
||||
String cron = getCron(shopBaseCrontab);
|
||||
quartzService.addJob(JOB_NAME_PREFIX + crontab_id, JOB_GROUP_NAME, TRIGGER_NAME_PREFIX + crontab_id, TRIGGER_GROUP_NAME, cron, crontab_file);
|
||||
} catch (Exception e) {
|
||||
log.error("添加定时任务失败,尝试从数据库同步: 任务ID={}", shopBaseCrontab.getCrontab_id(), e);
|
||||
// 如果添加失败,尝试从数据库同步任务
|
||||
boolean syncResult = syncSingleCrontabToScheduler(shopBaseCrontab.getCrontab_id());
|
||||
if (!syncResult) {
|
||||
throw new ApiException("添加定时任务失败,且同步任务也失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,12 +145,60 @@ public class ShopBaseCrontabServiceImpl extends BaseServiceImpl<ShopBaseCrontabM
|
||||
throw new ApiException(I18nUtil._("每周和每月不能同时选择!"));
|
||||
}
|
||||
|
||||
StringBuilder cron = new StringBuilder();
|
||||
String space = " ";
|
||||
|
||||
cron.append("0").append(space).append(crontab_minute).append(space).append(crontab_hour).append(space).append(crontab_day).append(space).append(crontab_month).append(space).append(crontab_week);
|
||||
return "0" + space + crontab_minute + space + crontab_hour + space + crontab_day + space + crontab_month + space + crontab_week;
|
||||
}
|
||||
|
||||
return cron.toString();
|
||||
/**
|
||||
* 从数据库同步单个任务到调度器
|
||||
*
|
||||
* @param crontabId 任务ID
|
||||
* @return 是否同步成功
|
||||
*/
|
||||
public boolean syncSingleCrontabToScheduler(Integer crontabId) {
|
||||
try {
|
||||
// 从数据库获取任务信息
|
||||
ShopBaseCrontab crontab = getById(crontabId);
|
||||
if (crontab == null) {
|
||||
log.warn("未找到ID为{}的定时任务", crontabId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查任务是否启用
|
||||
if (crontab.getCrontab_enable() == null || crontab.getCrontab_enable() != 1) {
|
||||
log.info("任务ID为{}的定时任务未启用,无需同步到调度器", crontabId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 构造任务和触发器名称
|
||||
String jobName = JOB_NAME_PREFIX + crontabId;
|
||||
String jobGroup = JOB_GROUP_NAME;
|
||||
String triggerName = TRIGGER_NAME_PREFIX + crontabId;
|
||||
String triggerGroup = TRIGGER_GROUP_NAME;
|
||||
|
||||
// 生成cron表达式
|
||||
String cron = getCron(crontab);
|
||||
|
||||
// 获取任务类名
|
||||
String jobClass = crontab.getCrontab_file();
|
||||
if (jobClass != null && jobClass.contains(".")) {
|
||||
jobClass = jobClass.substring(0, jobClass.lastIndexOf("."));
|
||||
}
|
||||
|
||||
// 通过QuartzService添加任务
|
||||
if (quartzService instanceof QuartzServiceImpl) {
|
||||
return ((QuartzServiceImpl) quartzService).addOrUpdateJobFromDatabase(
|
||||
jobName, jobGroup, triggerName, triggerGroup, cron, jobClass);
|
||||
} else {
|
||||
// 兼容其他实现
|
||||
quartzService.addJob(jobName, jobGroup, triggerName, triggerGroup, cron, jobClass);
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("同步单个定时任务到调度器失败: 任务ID={}", crontabId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ public class ShopBaseStateCodeServiceImpl extends BaseServiceImpl<ShopBaseStateC
|
||||
* 根据商品状态码 获取状态名称 (注意在该类内部调用此方法缓存则失效 并且 此表数据如果发生变动则需要靠重启JVM来刷新缓存)
|
||||
* todo 可以优化(这里缓存的是一个字段,不是一条数据)
|
||||
* todo 一般情况此表不会发生变化,如果追求数据一致性,可以考虑使用redis Hash来存储,使用JVM来缓存是更好的选择
|
||||
*
|
||||
* <p>
|
||||
* update 2024-12-14 切换到redis缓存 30秒
|
||||
*
|
||||
* @param code 商品状态码
|
||||
@ -72,12 +72,12 @@ public class ShopBaseStateCodeServiceImpl extends BaseServiceImpl<ShopBaseStateC
|
||||
/**
|
||||
* @param code
|
||||
* @param type 默认值 "state_code_code"
|
||||
* todo 加缓存
|
||||
* todo 加缓存
|
||||
*/
|
||||
@Override
|
||||
public String getCode(Integer code, String type) {
|
||||
if (code != null) {
|
||||
// String code_key = String.format("statecode-%s", code);
|
||||
// String code_key = String.format("statecode_%s", code);
|
||||
Map rows = Convert.toMap(String.class, Object.class, get(code));
|
||||
|
||||
if (rows != null) {
|
||||
|
||||
@ -5,13 +5,14 @@ import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.suisung.mall.common.modules.base.ShopBaseStoreCategory;
|
||||
import com.suisung.mall.common.utils.CheckUtil;
|
||||
import com.suisung.mall.common.utils.CommonUtil;
|
||||
import com.suisung.mall.core.web.service.RedisService;
|
||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
import com.suisung.mall.shop.base.mapper.ShopBaseStoreCategoryMapper;
|
||||
import com.suisung.mall.shop.base.service.ShopBaseStoreCategoryService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import com.suisung.mall.shop.store.service.ShopStoreBaseService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.context.request.RequestAttributes;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
@ -66,7 +67,7 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl<ShopBaseSt
|
||||
cache_key = cache_key + ":category_is_enable:" + category_is_enable;
|
||||
queryWrapper.eq("category_is_enable", category_is_enable);
|
||||
}
|
||||
Integer pageSize =ObjectUtil.defaultIfNull(getParameter("pageSize",Integer.class),10) ;
|
||||
Integer pageSize = ObjectUtil.defaultIfNull(getParameter("pageSize", Integer.class), 10);
|
||||
cache_key = cache_key + ":" + LANG;
|
||||
|
||||
// 设置cache todo 关闭缓存(修改分类的时候没删除缓存)
|
||||
@ -79,39 +80,39 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl<ShopBaseSt
|
||||
category_rows = Convert.toList(Map.class, categories);
|
||||
|
||||
List<CompletableFuture<Void>> futures = new ArrayList<>();
|
||||
Map<String,Map> storeLis = new HashMap<>();
|
||||
if(openFindStore){
|
||||
Map<String, Map> storeLis = new HashMap<>();
|
||||
if (openFindStore) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
for (Map category_row : category_rows) {
|
||||
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||
CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> {
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes,true);
|
||||
RequestContextHolder.setRequestAttributes(requestAttributes, true);
|
||||
Integer store_category_id = (Integer) category_row.get("store_category_id");
|
||||
Map<String, Object> row = new HashMap<>();
|
||||
row.put("store_category_id", String.valueOf(store_category_id));
|
||||
row.put("findStore",true);
|
||||
row.put("store_type","1");
|
||||
Map storeListMap= shopStoreBaseService.getStoreList(1, pageSize,row);
|
||||
row.put("findStore", true);
|
||||
row.put("store_type", "1");
|
||||
Map storeListMap = shopStoreBaseService.getStoreList(1, pageSize, row);
|
||||
storeLis.put(String.valueOf(store_category_id), storeListMap);
|
||||
return null;
|
||||
});
|
||||
futures.add(future);
|
||||
}
|
||||
|
||||
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.thenRun(() -> {
|
||||
long endTime = System.currentTimeMillis();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
log.debug("异步调用完成! 总耗时: "+(endTime - startTime) + "ms\n");
|
||||
CompletableFuture<Void> allOfFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
|
||||
.thenRun(() -> {
|
||||
long endTime = System.currentTimeMillis();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
log.debug("异步调用完成! 总耗时: " + (endTime - startTime) + "ms\n");
|
||||
});
|
||||
});
|
||||
});
|
||||
allOfFuture.join();
|
||||
}
|
||||
|
||||
for (Map category_row : category_rows) {
|
||||
Integer store_category_id = (Integer) category_row.get("store_category_id");
|
||||
List<Map> rs = getCategoryTree(store_category_id, category_is_enable);
|
||||
if(openFindStore){
|
||||
if (openFindStore) {
|
||||
category_row.put("storeList", storeLis.get(String.valueOf(store_category_id)));
|
||||
}
|
||||
category_row.put("id", category_row.get("store_category_id"));
|
||||
@ -122,7 +123,7 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl<ShopBaseSt
|
||||
|
||||
}
|
||||
if (CollUtil.isNotEmpty(category_rows)) {
|
||||
// redisService.set(cache_key, category_rows);
|
||||
// redisService.set(cache_key, category_rows);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,17 +168,17 @@ public class ShopBaseStoreCategoryServiceImpl extends BaseServiceImpl<ShopBaseSt
|
||||
@Override
|
||||
public BigDecimal getStoreCategoryRatio(Integer storeCategoryId) {
|
||||
// 默认分账比例为100%
|
||||
BigDecimal defaultRatio = new BigDecimal("100");
|
||||
BigDecimal defaultRatio = new BigDecimal(94);
|
||||
|
||||
try {
|
||||
// 检查参数是否为空
|
||||
if (ObjectUtil.isEmpty(storeCategoryId)) {
|
||||
if (CheckUtil.isEmpty(storeCategoryId)) {
|
||||
return defaultRatio;
|
||||
}
|
||||
|
||||
// 根据ID获取店铺分类信息
|
||||
ShopBaseStoreCategory shopBaseStoreCategory = get(storeCategoryId);
|
||||
if (ObjectUtil.isEmpty(shopBaseStoreCategory)) {
|
||||
if (shopBaseStoreCategory == null) {
|
||||
return defaultRatio;
|
||||
}
|
||||
|
||||
|
||||
@ -30,6 +30,12 @@ public class IpUtil implements ApplicationRunner {
|
||||
* ip2region搜索器实例
|
||||
*/
|
||||
private static Searcher searcher = null;
|
||||
|
||||
/**
|
||||
* 数据文件加载状态
|
||||
*/
|
||||
private static boolean dataFileLoaded = false;
|
||||
private static String dataFileInfo = "未加载";
|
||||
|
||||
/**
|
||||
* 根据IP地址获取地区信息
|
||||
@ -39,18 +45,23 @@ public class IpUtil implements ApplicationRunner {
|
||||
*/
|
||||
public static String getRegion(String ip) {
|
||||
if (Objects.isNull(searcher)) {
|
||||
log.error("IP2RegionUtils 没有成功加载数据文件");
|
||||
log.error("IP2RegionUtils 没有成功加载数据文件. 数据文件状态: {}, 文件信息: {}",
|
||||
dataFileLoaded ? "已加载但searcher为null" : "未加载", dataFileInfo);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (CheckUtil.isEmpty(ip)) {
|
||||
log.debug("传入的IP地址为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return searcher.search(ip);
|
||||
String result = searcher.search(ip);
|
||||
log.debug("IP地址 {} 解析成功,结果: {}", ip, result);
|
||||
return result;
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
log.error("IP:{} 解析时发生数组越界异常,可能是数据文件损坏或不兼容", ip, e);
|
||||
log.error("IP:{} 解析时发生数组越界异常,可能是数据文件损坏或不兼容. 数据文件状态: {}, 文件信息: {}",
|
||||
ip, dataFileLoaded ? "已加载" : "未加载", dataFileInfo, e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
String errorMsg = e.getMessage();
|
||||
@ -60,7 +71,13 @@ public class IpUtil implements ApplicationRunner {
|
||||
errorMsg += " -> " + e.getCause().getMessage();
|
||||
}
|
||||
}
|
||||
log.error("IP:{} 格式错误:{}", ip, errorMsg, e);
|
||||
log.error("IP:{} 格式错误:{}. 数据文件状态: {}, 文件信息: {}",
|
||||
ip, errorMsg, dataFileLoaded ? "已加载" : "未加载", dataFileInfo, e);
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
// 捕获所有可能的异常,确保不会传播到调用方
|
||||
log.error("IP:{} 解析时发生未知错误. 数据文件状态: {}, 文件信息: {}",
|
||||
ip, dataFileLoaded ? "已加载" : "未加载", dataFileInfo, t);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -97,17 +114,24 @@ public class IpUtil implements ApplicationRunner {
|
||||
districtVo.setCountry(parts[0] != null ? parts[0] : ""); // 设置国家
|
||||
districtVo.setProvince(parts[2] != null ? parts[2] : ""); // 设置省份
|
||||
districtVo.setCity(parts[3] != null ? parts[3] : ""); // 设置城市
|
||||
|
||||
log.debug("IP地址 {} 解析为地区信息成功. 国家: {}, 省份: {}, 城市: {}",
|
||||
ip, districtVo.getCountry(), districtVo.getProvince(), districtVo.getCity());
|
||||
} else {
|
||||
log.debug("IP:{} 解析结果格式不符合要求,原始数据: {}", ip, ss);
|
||||
return null;
|
||||
}
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
log.error("解析IP地址 {} 的地区信息时发生数组越界异常,原始数据: {}", ip, ss, e);
|
||||
log.error("解析IP地址 {} 的地区信息时发生数组越界异常,原始数据: {}. 数据文件状态: {}, 文件信息: {}",
|
||||
ip, ss, dataFileLoaded ? "已加载" : "未加载", dataFileInfo, e);
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
log.error("解析IP地址 {} 的地区信息时发生异常: {}", ip, e.getMessage(), e);
|
||||
log.error("解析IP地址 {} 的地区信息时发生异常: {}. 数据文件状态: {}, 文件信息: {}",
|
||||
ip, e.getMessage(), dataFileLoaded ? "已加载" : "未加载", dataFileInfo, e);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
log.debug("IP地址 {} 未解析到地区信息", ip);
|
||||
}
|
||||
|
||||
return districtVo;
|
||||
@ -137,16 +161,29 @@ public class IpUtil implements ApplicationRunner {
|
||||
buffer.flush();
|
||||
byte[] bytes = buffer.toByteArray();
|
||||
|
||||
// 记录数据文件信息
|
||||
dataFileInfo = String.format("文件大小: %d 字节", bytes.length);
|
||||
log.info("读取到 ip2region 数据文件,{}", dataFileInfo);
|
||||
|
||||
inputStream.close();
|
||||
searcher = Searcher.newWithBuffer(bytes);
|
||||
log.info("成功加载 ip2region 数据文件。");
|
||||
dataFileLoaded = true;
|
||||
|
||||
// 测试搜索器
|
||||
if (searcher != null) {
|
||||
String testResult = searcher.search("8.8.8.8");
|
||||
log.info("成功加载 ip2region 数据文件。测试搜索 8.8.8.8 结果: {}", testResult);
|
||||
} else {
|
||||
log.error("加载 ip2region 数据文件失败,searcher 实例为 null");
|
||||
}
|
||||
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
log.error("解析IP地址的地区信息时发生数组越界异常,原始数据: {}", e);
|
||||
dataFileLoaded = true;
|
||||
log.error("解析IP地址的地区信息时发生数组越界异常。数据文件状态: 已加载, 文件信息: {}", dataFileInfo, e);
|
||||
} catch (IOException e) {
|
||||
log.error("加载 ip2region 失败。", e);
|
||||
log.error("加载 ip2region 失败。数据文件状态: 未加载, 文件信息: {}", dataFileInfo, e);
|
||||
} catch (Exception e) {
|
||||
log.error("初始化 ip2region 搜索器失败。", e);
|
||||
log.error("初始化 ip2region 搜索器失败。数据文件状态: 未加载, 文件信息: {}", dataFileInfo, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +1,11 @@
|
||||
package com.suisung.mall.shop.components.quartz.job;
|
||||
|
||||
import com.suisung.mall.common.utils.LogUtil;
|
||||
import com.suisung.mall.shop.config.SpringUtil;
|
||||
import com.suisung.mall.shop.order.service.ShopOrderReturnService;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 支付宝/微信退款 定时任务
|
||||
*/
|
||||
@ -22,20 +18,19 @@ public class UpdateOrderRefundJob extends QuartzJobBean {
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
String activeProfile = activeProfiles[0];
|
||||
if (!activeProfile.equals("prod")) {
|
||||
return;
|
||||
}
|
||||
|
||||
ShopOrderReturnService orderReturnService = SpringUtil.getBean(ShopOrderReturnService.class);
|
||||
|
||||
// 获取支付宝/微信退款订单
|
||||
List<String> return_ids = orderReturnService.getOnlineRefundReturnId();
|
||||
for (String return_id : return_ids) {
|
||||
if (!orderReturnService.doOnlineRefund(return_id)) {
|
||||
LogUtil.error(String.format("return_id: %s 原路退回出错", return_id));
|
||||
|
||||
// 处理异常标记
|
||||
}
|
||||
}
|
||||
// ShopOrderReturnService orderReturnService = SpringUtil.getBean(ShopOrderReturnService.class);
|
||||
//
|
||||
// // 获取支付宝/微信退款订单
|
||||
// List<String> return_ids = orderReturnService.getOnlineRefundReturnId();
|
||||
// for (String return_id : return_ids) {
|
||||
// if (!orderReturnService.doOnlineRefund(return_id)) {
|
||||
// LogUtil.error(String.format("return_id: %s 原路退回出错", return_id));
|
||||
//
|
||||
// // 处理异常标记
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,40 @@
|
||||
package com.suisung.mall.shop.components.quartz.job;
|
||||
|
||||
import com.suisung.mall.shop.config.SpringUtil;
|
||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobExecutionException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.quartz.QuartzJobBean;
|
||||
|
||||
/**
|
||||
* 分账状态更改补偿 定时任务
|
||||
*/
|
||||
public class UpdateOrderSeparateJob extends QuartzJobBean {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(UpdateOrderSeparateJob.class);
|
||||
|
||||
@Override
|
||||
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
|
||||
logger.info("[分账状态修复定时任务] 开始执行");
|
||||
|
||||
try {
|
||||
LakalaApiService lakalaApiService = SpringUtil.getBean(LakalaApiService.class);
|
||||
|
||||
if (lakalaApiService == null) {
|
||||
logger.error("[分账状态修复定时任务] 无法获取LakalaApiService实例");
|
||||
return;
|
||||
}
|
||||
|
||||
Integer processedCount = lakalaApiService.fixedUnSuccessSeparateStatusJob();
|
||||
|
||||
logger.info("[分账状态修复定时任务] 执行完成,共处理 {} 条记录", processedCount);
|
||||
} catch (Exception e) {
|
||||
logger.error("[分账状态修复定时任务] 执行过程中发生异常", e);
|
||||
throw new JobExecutionException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -4,17 +4,19 @@ import com.suisung.mall.common.exception.ApiException;
|
||||
import com.suisung.mall.common.utils.I18nUtil;
|
||||
import com.suisung.mall.shop.components.quartz.service.QuartzService;
|
||||
import org.quartz.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class QuartzServiceImpl implements QuartzService {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(QuartzServiceImpl.class);
|
||||
private final String PATH_PREFIX = "com.suisung.mall.shop.components.quartz.job.";
|
||||
@Autowired
|
||||
private Scheduler scheduler;
|
||||
|
||||
private final String PATH_PREFIX = "com.suisung.mall.shop.components.quartz.job.";
|
||||
|
||||
/**
|
||||
* 新增一个定时任务
|
||||
*
|
||||
@ -27,24 +29,126 @@ public class QuartzServiceImpl implements QuartzService {
|
||||
*/
|
||||
@Override
|
||||
public void addJob(String jName, String jGroup, String tName, String tGroup, String cron, String cName) {
|
||||
logger.info("[Quartz] 开始添加定时任务: 任务名称={}, 任务组={}, 触发器名称={}, 触发器组={}, cron表达式={}, 类名={}",
|
||||
jName, jGroup, tName, tGroup, cron, cName);
|
||||
|
||||
Class<Job> clazz = null;
|
||||
// 1. 参数校验
|
||||
if (jName == null || jName.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] 任务名称不能为空: {}", jName);
|
||||
throw new ApiException(I18nUtil._("任务名称不能为空!"));
|
||||
}
|
||||
if (jGroup == null || jGroup.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] 任务组不能为空: {}", jGroup);
|
||||
throw new ApiException(I18nUtil._("任务组不能为空!"));
|
||||
}
|
||||
if (tName == null || tName.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] 触发器名称不能为空: {}", tName);
|
||||
throw new ApiException(I18nUtil._("触发器名称不能为空!"));
|
||||
}
|
||||
if (tGroup == null || tGroup.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] 触发器组不能为空: {}", tGroup);
|
||||
throw new ApiException(I18nUtil._("触发器组不能为空!"));
|
||||
}
|
||||
if (cron == null || cron.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] cron表达式不能为空: {}", cron);
|
||||
throw new ApiException(I18nUtil._("cron表达式不能为空!"));
|
||||
}
|
||||
if (cName == null || cName.trim().isEmpty()) {
|
||||
logger.warn("[Quartz] 任务实现类名称不能为空: {}", cName);
|
||||
throw new ApiException(I18nUtil._("任务实现类名称不能为空!"));
|
||||
}
|
||||
|
||||
// 2. 加载任务类
|
||||
Class<Job> clazz;
|
||||
try {
|
||||
clazz = (Class<Job>) Class.forName(PATH_PREFIX + cName);
|
||||
String fullClassName = PATH_PREFIX + cName;
|
||||
logger.debug("[Quartz] 尝试加载任务类: {}", fullClassName);
|
||||
clazz = (Class<Job>) Class.forName(fullClassName);
|
||||
logger.debug("[Quartz] 成功加载任务类: {}", fullClassName);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new ApiException(I18nUtil._("定时任务脚本不存在!"));
|
||||
logger.error("[Quartz] 定时任务脚本不存在!类名: {}{}", PATH_PREFIX, cName, e);
|
||||
throw new ApiException(I18nUtil._("定时任务脚本不存在!类名: " + PATH_PREFIX + cName), e);
|
||||
}
|
||||
|
||||
try {
|
||||
JobDetail jobDetail = JobBuilder.newJob(clazz).withIdentity(jName, jGroup).build();
|
||||
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(tName, tGroup).startNow().withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
|
||||
scheduler.start();
|
||||
// 3. 构建任务详情和触发器
|
||||
JobKey jobKey = new JobKey(jName, jGroup);
|
||||
JobDetail jobDetail = JobBuilder.newJob(clazz)
|
||||
.withIdentity(jobKey)
|
||||
.build();
|
||||
|
||||
TriggerKey triggerKey = new TriggerKey(tName, tGroup);
|
||||
CronTrigger trigger = TriggerBuilder.newTrigger()
|
||||
.withIdentity(triggerKey)
|
||||
.startNow()
|
||||
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
|
||||
.build();
|
||||
|
||||
logger.debug("[Quartz] 构建任务详情和触发器完成: jobKey={}, triggerKey={}", jobKey, triggerKey);
|
||||
|
||||
// 4. 检查任务是否已存在
|
||||
if (scheduler.checkExists(jobKey)) {
|
||||
logger.info("[Quartz] 任务已存在,先删除旧任务: jobKey={}", jobKey);
|
||||
scheduler.deleteJob(jobKey);
|
||||
}
|
||||
|
||||
// 5. 调度任务
|
||||
logger.info("[Quartz] 开始调度任务: jobKey={}, triggerKey={}", jobKey, triggerKey);
|
||||
scheduler.scheduleJob(jobDetail, trigger);
|
||||
logger.info("[Quartz] 任务调度成功: jobKey={}, triggerKey={}", jobKey, triggerKey);
|
||||
|
||||
// 6. 启动调度器(如果尚未启动)
|
||||
if (!scheduler.isStarted()) {
|
||||
logger.info("[Quartz] 调度器尚未启动,正在启动...");
|
||||
scheduler.start();
|
||||
logger.info("[Quartz] 调度器启动成功");
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
logger.error("[Quartz] 添加定时任务失败!任务名称={}, 任务组={}, 触发器名称={}, 触发器组={}",
|
||||
jName, jGroup, tName, tGroup, e);
|
||||
throw new ApiException(I18nUtil._("添加定时任务失败!"), e);
|
||||
} catch (Exception e) {
|
||||
throw new ApiException(I18nUtil._("添加定时任务失败!"));
|
||||
logger.error("[Quartz] 添加定时任务时发生未知异常!任务名称={}, 任务组={}, 触发器名称={}, 触发器组={}",
|
||||
jName, jGroup, tName, tGroup, e);
|
||||
throw new ApiException(I18nUtil._("添加定时任务失败!"), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加或更新任务,如果调度器中不存在任务则从数据库获取信息添加
|
||||
*
|
||||
* @param jobName 任务名称
|
||||
* @param jobGroup 任务组
|
||||
* @param triggerName 触发器名称
|
||||
* @param triggerGroup 触发器组
|
||||
* @param dbCron 从数据库获取的cron表达式
|
||||
* @param dbJobClass 从数据库获取的任务类名
|
||||
* @return 是否添加成功
|
||||
*/
|
||||
public boolean addOrUpdateJobFromDatabase(String jobName, String jobGroup, String triggerName,
|
||||
String triggerGroup, String dbCron, String dbJobClass) {
|
||||
logger.info("[Quartz] 尝试从数据库信息添加或更新任务: 任务名称={}, 任务组={}", jobName, jobGroup);
|
||||
|
||||
try {
|
||||
JobKey jobKey = new JobKey(jobName, jobGroup);
|
||||
|
||||
// 检查任务是否已存在
|
||||
if (scheduler.checkExists(jobKey)) {
|
||||
logger.debug("[Quartz] 任务已存在于调度器中: jobKey={}", jobKey);
|
||||
return true; // 任务已存在,无需添加
|
||||
}
|
||||
|
||||
// 任务不存在,从数据库信息创建新任务
|
||||
logger.info("[Quartz] 调度器中不存在任务,从数据库信息创建: jobKey={}", jobKey);
|
||||
addJob(jobName, jobGroup, triggerName, triggerGroup, dbCron, dbJobClass);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
logger.error("[Quartz] 从数据库信息添加或更新任务失败: 任务名称={}, 任务组={}", jobName, jobGroup, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 暂停定时任务
|
||||
*
|
||||
@ -53,10 +157,24 @@ public class QuartzServiceImpl implements QuartzService {
|
||||
*/
|
||||
@Override
|
||||
public void pauseJob(String jName, String jGroup) {
|
||||
logger.info("[Quartz] 开始暂停定时任务: 任务名称={}, 任务组={}", jName, jGroup);
|
||||
try {
|
||||
scheduler.pauseJob(JobKey.jobKey(jName, jGroup));
|
||||
JobKey jobKey = JobKey.jobKey(jName, jGroup);
|
||||
|
||||
// 检查任务是否存在,不存在则记录日志并返回,不抛出异常
|
||||
if (!scheduler.checkExists(jobKey)) {
|
||||
logger.info("[Quartz] 任务不存在,无需暂停: jobKey={},可能任务已手动删除或尚未创建", jobKey);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.pauseJob(jobKey);
|
||||
logger.info("[Quartz] 定时任务暂停成功: jobKey={}", jobKey);
|
||||
} catch (SchedulerException e) {
|
||||
logger.error("[Quartz] 暂停定时任务时发生调度异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使暂停失败也继续执行,避免阻塞业务流程
|
||||
} catch (Exception e) {
|
||||
throw new ApiException(I18nUtil._("暂停定时任务失败!"));
|
||||
logger.error("[Quartz] 暂停定时任务时发生未知异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使暂停失败也继续执行,避免阻塞业务流程
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,10 +186,24 @@ public class QuartzServiceImpl implements QuartzService {
|
||||
*/
|
||||
@Override
|
||||
public void resumeJob(String jName, String jGroup) {
|
||||
logger.info("[Quartz] 开始继续定时任务: 任务名称={}, 任务组={}", jName, jGroup);
|
||||
try {
|
||||
scheduler.resumeJob(JobKey.jobKey(jName, jGroup));
|
||||
JobKey jobKey = JobKey.jobKey(jName, jGroup);
|
||||
|
||||
// 检查任务是否存在,不存在则记录日志并返回,不抛出异常
|
||||
if (!scheduler.checkExists(jobKey)) {
|
||||
logger.info("[Quartz] 任务不存在,无需继续: jobKey={},可能任务已手动删除或尚未创建", jobKey);
|
||||
return;
|
||||
}
|
||||
|
||||
scheduler.resumeJob(jobKey);
|
||||
logger.info("[Quartz] 定时任务继续成功: jobKey={}", jobKey);
|
||||
} catch (SchedulerException e) {
|
||||
throw new ApiException(I18nUtil._("继续定时任务失败!"));
|
||||
logger.error("[Quartz] 继续定时任务时发生调度异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使继续失败也继续执行,避免阻塞业务流程
|
||||
} catch (Exception e) {
|
||||
logger.error("[Quartz] 继续定时任务时发生未知异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使继续失败也继续执行,避免阻塞业务流程
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,10 +215,30 @@ public class QuartzServiceImpl implements QuartzService {
|
||||
*/
|
||||
@Override
|
||||
public void deleteJob(String jName, String jGroup) {
|
||||
logger.info("[Quartz] 开始删除定时任务: 任务名称={}, 任务组={}", jName, jGroup);
|
||||
try {
|
||||
scheduler.deleteJob(JobKey.jobKey(jName, jGroup));
|
||||
JobKey jobKey = JobKey.jobKey(jName, jGroup);
|
||||
|
||||
// 检查任务是否存在,不存在则记录日志并返回,不抛出异常
|
||||
if (!scheduler.checkExists(jobKey)) {
|
||||
logger.info("[Quartz] 任务不存在,无需删除: jobKey={},可能任务已手动删除或尚未创建", jobKey);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean deleted = scheduler.deleteJob(jobKey);
|
||||
if (deleted) {
|
||||
logger.info("[Quartz] 定时任务删除成功: jobKey={}", jobKey);
|
||||
} else {
|
||||
logger.warn("[Quartz] 定时任务删除失败: jobKey={}", jobKey);
|
||||
}
|
||||
} catch (SchedulerException e) {
|
||||
throw new ApiException(I18nUtil._("删除定时任务失败!"));
|
||||
logger.error("[Quartz] 删除定时任务时发生调度异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使删除失败也继续执行,避免阻塞业务流程
|
||||
} catch (Exception e) {
|
||||
logger.error("[Quartz] 删除定时任务时发生未知异常!任务名称={}, 任务组={}", jName, jGroup, e);
|
||||
// 即使删除失败也继续执行,避免阻塞业务流程
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,12 +1,51 @@
|
||||
package com.suisung.mall.shop.config;
|
||||
|
||||
import com.suisung.mall.core.config.BaseRedisConfig;
|
||||
import com.suisung.mall.shop.order.listener.RedisKeyExpiredListener;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.listener.PatternTopic;
|
||||
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
|
||||
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
|
||||
|
||||
/**
|
||||
* Redis相关配置
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
public class RedisConfig extends BaseRedisConfig {
|
||||
@Bean
|
||||
RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
|
||||
MessageListenerAdapter keyExpiredListener) {
|
||||
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
|
||||
container.setConnectionFactory(connectionFactory);
|
||||
container.addMessageListener(keyExpiredListener, new PatternTopic("__keyevent@*__:expired"));
|
||||
return container;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MessageListenerAdapter keyExpiredListener(RedisKeyExpiredListener redisKeyExpiredListener) {
|
||||
return new MessageListenerAdapter(redisKeyExpiredListener);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RedisKeyExpiredListener redisKeyExpiredListener() {
|
||||
return new RedisKeyExpiredListener();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 监听Redis键过期事件
|
||||
// */
|
||||
// public static class KeyExpiredListener implements MessageListener {
|
||||
// @Override
|
||||
// public void onMessage(org.springframework.data.redis.connection.Message message,
|
||||
// byte[] pattern) {
|
||||
// String expiredKey = new String(message.getBody());
|
||||
// // 在这里处理过期键的逻辑
|
||||
// log.info("Redis 过期事件监听 Key expired: {}", expiredKey);
|
||||
// // 您可以在这里添加自己的业务逻辑,比如清理相关资源、发送通知等
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@ -310,7 +310,7 @@ public class EsignContractServiceImpl extends BaseServiceImpl<EsignContractMappe
|
||||
if (success && StrUtil.isNotBlank(downloadUrl)) {
|
||||
|
||||
// 1、(电子合同)给商家申请分账功能使用;务必检查是否申请过?申请过忽略
|
||||
Pair<Boolean, String> retPair = lakalaApiService.innerApplyLedgerMer(esignContract.getMch_mobile());
|
||||
Pair<Boolean, String> retPair = lakalaApiService.innerApplyLedgerMer("", false);
|
||||
if (!retPair.getFirst()) {
|
||||
log.error("商家申请分账业务异常:{}", retPair.getSecond());
|
||||
}
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||
*/
|
||||
|
||||
package com.suisung.mall.shop.lakala.controller.admin;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.suisung.mall.common.api.CommonResult;
|
||||
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Api(tags = "拉卡拉相关接口 - 后端控制器")
|
||||
@RestController
|
||||
@RequestMapping("/admin/shop/lakala")
|
||||
public class LakalaAdminController extends BaseControllerImpl {
|
||||
|
||||
@Resource
|
||||
private LakalaApiService lakalaPayService;
|
||||
|
||||
@ApiOperation(value = "查询拉卡拉商户可分账的金额", notes = "查询拉卡拉商户可分账的金额")
|
||||
@RequestMapping(value = "/sacs/queryMchSplitAmt", method = RequestMethod.POST)
|
||||
public CommonResult queryMchCanSplitAmt(@RequestBody JSONObject paramsJSON) {
|
||||
JSONObject resultJSON = lakalaPayService.queryMchCanSplitAmtInner(paramsJSON.getStr("merchantNo"), paramsJSON.getStr("logNo"), paramsJSON.getStr("logDate"));
|
||||
if (resultJSON != null) {
|
||||
return CommonResult.success(resultJSON);
|
||||
}
|
||||
return CommonResult.failed();
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,6 +13,7 @@ import cn.hutool.json.JSONUtil;
|
||||
import com.suisung.mall.common.api.CommonResult;
|
||||
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
||||
import com.suisung.mall.shop.lakala.service.LklLedgerEcService;
|
||||
import com.suisung.mall.shop.library.service.LibraryProductService;
|
||||
import com.suisung.mall.shop.message.service.MqMessageService;
|
||||
import com.suisung.mall.shop.message.service.PushMessageService;
|
||||
@ -28,6 +29,7 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.util.Base64Utils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@ -71,6 +73,13 @@ public class LakalaController extends BaseControllerImpl {
|
||||
@Resource
|
||||
private MqMessageService mqMessageService;
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
private LakalaApiService lakalaApiService;
|
||||
|
||||
@Resource
|
||||
private LklLedgerEcService lklLedgerEcService;
|
||||
|
||||
@ApiOperation(value = "测试案例", notes = "测试案例")
|
||||
@RequestMapping(value = "/testcase", method = RequestMethod.POST)
|
||||
public Object testcase(@RequestBody JSONObject paramsJSON) {
|
||||
@ -105,8 +114,9 @@ public class LakalaController extends BaseControllerImpl {
|
||||
|
||||
// return shopOrderBaseService.sameCityOrderExpireSeconds(10000L);
|
||||
|
||||
return sfExpressApiService.createSfExpressShop(66, "能辉超市", "桂平市", "广西壮族自治区贵港市桂平市广佰汇超市(桂平店)", "谢能坤", "17777525395", "110.07165452271", "23.369069486251");
|
||||
// return sfExpressApiService.createSfExpressShop(66, "能辉超市", "桂平市", "广西壮族自治区贵港市桂平市广佰汇超市(桂平店)", "谢能坤", "17777525395", "110.07165452271", "23.369069486251");
|
||||
|
||||
return lakalaApiService.sacsQuery("8226330541100GU", "20250918770188017227140800").toString();
|
||||
}
|
||||
|
||||
@ApiOperation(value = "批量发送推送消息 - 测试案例", notes = "批量发送推送消息 - 测试案例")
|
||||
@ -131,6 +141,18 @@ public class LakalaController extends BaseControllerImpl {
|
||||
return lakalaPayService.getBankCardBin(paramsJSON.getStr("bankCardNo"));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "发货类交易确认收货通知", notes = "发货类交易确认收货通知 https://o.lakala.com/#/home/document/detail?id=1003")
|
||||
@RequestMapping(value = "/trans/receive/completeNotify", method = RequestMethod.POST)
|
||||
public ResponseEntity<JSONObject> receiveCompleteNotify(HttpServletRequest request) {
|
||||
// 完整地址: https://mall.gpxscs.cn/api/mobile/shop/lakala/trans/receive/completeNotify
|
||||
JSONObject resp = lakalaPayService.receiveCompleteNotify(request);
|
||||
if (resp != null && "SUCCESS".equals(resp.get("code"))) {
|
||||
return ResponseEntity.ok(resp);
|
||||
}
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "商户入网电子合同申请回调通知", notes = "商户入网电子合同申请回调通知")
|
||||
@RequestMapping(value = "/ec/applyNotify", method = RequestMethod.POST)
|
||||
public ResponseEntity<JSONObject> ecApplyNotify(HttpServletRequest request) {
|
||||
@ -142,6 +164,19 @@ public class LakalaController extends BaseControllerImpl {
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "跳转到拉卡拉签署合同链接", notes = "跳转到拉卡拉签署合同链接")
|
||||
@RequestMapping(value = "/sign/ec/{code}", method = RequestMethod.GET)
|
||||
public ModelAndView jumpToSignLklEcLink(@PathVariable Long code) {
|
||||
// 根据 code 值获取结果 URL
|
||||
String resultUrl = lklLedgerEcService.getLklEcResultUrl(code);
|
||||
|
||||
ModelAndView modelAndView = new ModelAndView("sign_lkl_ec");
|
||||
modelAndView.addObject("resultUrl", resultUrl);
|
||||
|
||||
// 返回模板名称,渲染 sign_lkl_ec.html 页面
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "商户分账业务开通申请", notes = "商户分账业务开通申请")
|
||||
@RequestMapping(value = "/ledger/applyLedgerMer", method = RequestMethod.POST)
|
||||
public CommonResult ledgerApplyLedgerMer(@RequestBody JSONObject paramsJSON) {
|
||||
@ -182,4 +217,24 @@ public class LakalaController extends BaseControllerImpl {
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 分账结果通知
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=393
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@ApiOperation(value = "分账关系绑定申请异步回调通知", notes = "分账关系绑定申请异步回调通知")
|
||||
@RequestMapping(value = "/sacs/separateNotify", method = RequestMethod.POST)
|
||||
public ResponseEntity<JSONObject> separateNotify(HttpServletRequest request) {
|
||||
JSONObject resp = lakalaPayService.sacsSeparateNotify(request, "", "");
|
||||
if (resp != null && "SUCCESS".equals(resp.get("code"))) {
|
||||
return ResponseEntity.ok(resp);
|
||||
}
|
||||
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(resp);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,15 +8,19 @@
|
||||
|
||||
package com.suisung.mall.shop.lakala.controller.mobile;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.suisung.mall.common.api.CommonResult;
|
||||
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
||||
import com.suisung.mall.shop.lakala.service.LakalaApiService;
|
||||
import com.suisung.mall.shop.lakala.service.LklBanksService;
|
||||
import com.suisung.mall.shop.lakala.service.impl.LklTkServiceImpl;
|
||||
import com.suisung.mall.shop.lakala.utils.LakalaUtil;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.data.util.Pair;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
@ -27,6 +31,7 @@ import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@Api(tags = "拉卡拉商户进件控制器")
|
||||
@RestController
|
||||
@RequestMapping("/mobile/shop/lakala/tk")
|
||||
@ -38,6 +43,10 @@ public class LklTkController extends BaseControllerImpl {
|
||||
@Resource
|
||||
private LklBanksService lklBanksService;
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
private LakalaApiService lakalaPayService;
|
||||
|
||||
@ApiOperation(value = "解密拉卡拉进件返回的异步通知", notes = "解密拉卡拉进件返回的异步通知")
|
||||
@RequestMapping(value = "/decode", method = RequestMethod.POST)
|
||||
public String decryptLklTkData(@RequestParam(name = "data") String data,
|
||||
@ -50,8 +59,33 @@ public class LklTkController extends BaseControllerImpl {
|
||||
@ApiOperation(value = "搜索国内银行(支行)分页列表", notes = "搜索国内银行(支行)分页列表,数据包含有效的收款结清行号")
|
||||
@RequestMapping(value = "/bank/search", method = RequestMethod.POST)
|
||||
public CommonResult searchLklBanksPageList(@RequestBody JSONObject paramsJSON) {
|
||||
// Page<LklBanks> list = lklBanksService.searchBranchBanksPageList(paramsJSON.getStr("keyword"), paramsJSON.getInt("pageNum"), paramsJSON.getInt("pageSize"));
|
||||
IPage<Map> list = lklBanksService.pageBranchBanksList("", "", 2, paramsJSON.getStr("keyword"), paramsJSON.getInt("pageNum"), paramsJSON.getInt("pageSize"));
|
||||
log.debug("开始搜索国内银行(支行)分页列表,参数: {}", paramsJSON);
|
||||
|
||||
String clearNo = "";
|
||||
String bankCardNo = paramsJSON.getStr("bankCardNo");
|
||||
String keyword = paramsJSON.getStr("keyword");
|
||||
Integer pageNum = paramsJSON.getInt("pageNum");
|
||||
Integer pageSize = paramsJSON.getInt("pageSize");
|
||||
|
||||
log.debug("搜索参数: bankCardNo={}, keyword={}, pageNum={}, pageSize={}", bankCardNo, keyword, pageNum, pageSize);
|
||||
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(bankCardNo)) {
|
||||
log.warn("银行卡号为空,无法获取银行信息");
|
||||
} else {
|
||||
// 通过卡号获取银行信息
|
||||
CommonResult result = lakalaPayService.getBankCardBin(bankCardNo);
|
||||
if (result != null && result.getStatus() == 200L && result.getData() != null) {
|
||||
JSONObject bankInfo = (JSONObject) result.getData();
|
||||
clearNo = bankInfo.getStr("clearingBankCode", bankInfo.getStr("bankCode"));
|
||||
log.debug("获取到银行清算行号: {}", clearNo);
|
||||
} else {
|
||||
log.warn("获取银行卡BIN信息失败,result={}", result);
|
||||
}
|
||||
}
|
||||
|
||||
IPage<Map> list = lklBanksService.pageBranchBanksList("", "", clearNo, 2, keyword, pageNum, pageSize);
|
||||
log.info("搜索国内银行(支行)分页列表完成,结果数量: {}", list != null ? list.getTotal() : 0);
|
||||
return CommonResult.success(list);
|
||||
}
|
||||
|
||||
|
||||
@ -22,5 +22,5 @@ import java.util.Map;
|
||||
@Repository
|
||||
public interface LklBanksMapper extends BaseMapper<LklBanks> {
|
||||
|
||||
IPage<Map> pageBranchBanksList(Page<Map> page, @Param("bankNo") String bankNo, @Param("branchBankNo") String branchBankNo, @Param("type") Integer type, @Param("keywords") List<String> keywords);
|
||||
IPage<Map> pageBranchBanksList(Page<Map> page, @Param("bankNo") String bankNo, @Param("branchBankNo") String branchBankNo, @Param("clearNo") String clearNo, @Param("type") Integer type, @Param("keywords") List<String> keywords);
|
||||
}
|
||||
|
||||
@ -90,10 +90,24 @@ public interface LakalaApiService {
|
||||
/**
|
||||
* 内部调用:商户分账业务开通申请
|
||||
*
|
||||
* @param merCupNo
|
||||
* @param merCupNo 商户号
|
||||
* @param forceReApply 如果已申请成功,是否可以重新申请更改?
|
||||
* @return
|
||||
*/
|
||||
Pair<Boolean, String> innerApplyLedgerMer(String merCupNo);
|
||||
Pair<Boolean, String> innerApplyLedgerMer(String merCupNo, Boolean forceReApply);
|
||||
|
||||
/**
|
||||
* 发货类交易确认收货通知
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=1003
|
||||
* 注意:
|
||||
* (1)交易通知接口是交易成功完成后会向 complete_notify_url 这个地址(主扫交易或者主扫合单请求中的complete_notify_url字段)发起交易结果通知。拉卡拉系统通知时,如果商户的应答没有按照以下“响应参考报文”示例返回成功状态时,则系统认为通知失败,系统会通过一定的策略定期重新发起通知。
|
||||
* (2)同样的通知可能会多次发送给商户系统,商户系统必须能够正确处理重复的通知。
|
||||
* (3)在没有收到拉卡拉支付交易通知的情况下,建议商户主动调用【06查询交易】确认交易状态。
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
JSONObject receiveCompleteNotify(HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* 商户入网电子合同申请回调通知
|
||||
@ -172,14 +186,39 @@ public interface LakalaApiService {
|
||||
*/
|
||||
Pair<Boolean, String> innerDoOrderSeparate(String orderId, String storeId);
|
||||
|
||||
/**
|
||||
* 根据商户号、交易号和收货流水号执行订单分账操作
|
||||
* <p>
|
||||
* 该方法用于处理拉卡拉订单的分账逻辑。在用户确认收货后,系统会根据订单信息执行分账操作,
|
||||
* 将订单金额按照预设比例分配给平台、商家和代理商(如果存在)。
|
||||
* </p>
|
||||
* <p>
|
||||
* 分账操作流程:
|
||||
* 1. 参数校验和订单查询
|
||||
* 2. 检查订单状态(是否已确认收货)
|
||||
* 3. 检查是否已分账,避免重复处理
|
||||
* 4. 计算分账金额
|
||||
* 5. 构建分账请求并发送至拉卡拉
|
||||
* 6. 保存分账结果
|
||||
* </p>
|
||||
*
|
||||
* @param lklMerchantNo 拉卡拉商户号
|
||||
* @param receiveTradeNo 收货交易号(对应拉卡拉的trade_no)
|
||||
* @param receiveLogNo 收货流水号(对应拉卡拉的log_no)
|
||||
* @return Pair<Boolean, String> 处理结果对,first为是否成功,second为结果描述信息
|
||||
*/
|
||||
Pair<Boolean, String> innerDoOrderSeparateByMerchantAndLogNo(String lklMerchantNo, String receiveTradeNo, String receiveLogNo);
|
||||
|
||||
/**
|
||||
* 分账结果通知
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=393
|
||||
*
|
||||
* @param request
|
||||
* @param merchantNo 分账方商户号,非空表示是事后补偿
|
||||
* @param separateNo 分账指令流水号,非空表示是事后补偿
|
||||
* @return
|
||||
*/
|
||||
JSONObject sacsSeparateNotify(HttpServletRequest request);
|
||||
JSONObject sacsSeparateNotify(HttpServletRequest request, String merchantNo, String separateNo);
|
||||
|
||||
/**
|
||||
* 查询拉卡拉商户可分账的金额
|
||||
@ -199,6 +238,36 @@ public interface LakalaApiService {
|
||||
*/
|
||||
Pair<String, String> queryMchCanSplitAmt(String merchantNo, String logNo, String logDate);
|
||||
|
||||
/**
|
||||
* 查询拉卡拉商户可分账的金额
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=394
|
||||
*
|
||||
* @param merchantNo 拉卡拉外部商户号
|
||||
* @param logNo 拉卡拉对账单流水号
|
||||
* @param logDate 拉卡拉对账单交易日期 yyyyMMdd
|
||||
* @return 响应结果 {
|
||||
* "merchant_no": "82229005943096D",
|
||||
* "total_separate_amt": "9900",
|
||||
* "can_separate_amt": "0",
|
||||
* "log_date": "20221220",
|
||||
* "log_no": "66210306990190"
|
||||
* }
|
||||
*/
|
||||
JSONObject queryMchCanSplitAmtInner(String merchantNo, String logNo, String logDate);
|
||||
|
||||
|
||||
/**
|
||||
* 分账查询结果
|
||||
* <p>
|
||||
* 用于查询已发起的分账交易的处理结果状态
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=392
|
||||
*
|
||||
* @param merchantNo 分账方商户号
|
||||
* @param separateNo 分账指令流水号
|
||||
* @return 分账结果数据,包含分账状态等信息;查询失败时返回null
|
||||
*/
|
||||
JSONObject sacsQuery(String merchantNo, String separateNo);
|
||||
|
||||
/**
|
||||
* 订单分账撤销
|
||||
* 参考:https://o.lakala.com/#/home/document/detail?id=390
|
||||
@ -212,4 +281,11 @@ public interface LakalaApiService {
|
||||
*/
|
||||
Boolean sacsSeparateCancel(String merchantNo, String originSeparateNo, String originOutSeparateNo, String outSeparateNo, String totalAmt);
|
||||
|
||||
/**
|
||||
* 更改已经分账的状态(定时任务用途)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Integer fixedUnSuccessSeparateStatusJob();
|
||||
|
||||
}
|
||||
|
||||
@ -32,13 +32,14 @@ public interface LklBanksService {
|
||||
*
|
||||
* @param bankNo 总行号
|
||||
* @param branchBankNo 支行号
|
||||
* @param clearNo 清算行号
|
||||
* @param type 1-店铺地区;2-银行地区
|
||||
* @param keyword 查询关键字,做分词
|
||||
* @param pageNum
|
||||
* @param pageSize
|
||||
* @return
|
||||
*/
|
||||
IPage<Map> pageBranchBanksList(String bankNo, String branchBankNo, Integer type, String keyword, Integer pageNum, Integer pageSize);
|
||||
IPage<Map> pageBranchBanksList(String bankNo, String branchBankNo, String clearNo, Integer type, String keyword, Integer pageNum, Integer pageSize);
|
||||
|
||||
/**
|
||||
* 根据支行号查询支行信息(带省市地区)
|
||||
|
||||
@ -50,4 +50,12 @@ public interface LklLedgerEcService extends IBaseService<LklLedgerEc> {
|
||||
LklLedgerEc getByMchId(Long mchId, String ecStatus, Integer status);
|
||||
|
||||
|
||||
/**
|
||||
* 获取拉卡拉商户签署合同页面URL
|
||||
*
|
||||
* @param mchId
|
||||
* @return
|
||||
*/
|
||||
String getLklEcResultUrl(Long mchId);
|
||||
|
||||
}
|
||||
|
||||
@ -57,7 +57,7 @@ public interface LklLedgerMerReceiverBindService extends IBaseService<LklLedgerM
|
||||
* @param applyId
|
||||
* @return
|
||||
*/
|
||||
LklLedgerMerReceiverBind getPlatformByApplyId(String applyId);
|
||||
LklLedgerMerReceiverBind getMerReceiverByApplyId(String applyId);
|
||||
|
||||
/**
|
||||
* 根据商户编号查询一条代理商绑定记录
|
||||
@ -91,4 +91,22 @@ public interface LklLedgerMerReceiverBindService extends IBaseService<LklLedgerM
|
||||
* @return
|
||||
*/
|
||||
Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String receiverNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark);
|
||||
|
||||
/**
|
||||
* 根据入驻号和商户编号查询商户分账接收方绑定记录
|
||||
*
|
||||
* @param mchId 商户ID
|
||||
* @param merCupNo 商户银联编号
|
||||
* @return 删除成功返回true,失败返回false
|
||||
*/
|
||||
List<LklLedgerMerReceiverBind> selectByMchIdAndMerCupNo(Long mchId, String merCupNo);
|
||||
|
||||
/**
|
||||
* 根据入驻编号和商户编号删除商户分账接收方绑定记录
|
||||
*
|
||||
* @param mchId
|
||||
* @param merCupNo
|
||||
* @return
|
||||
*/
|
||||
Boolean delByMchIdAndMerCupNo(Long mchId, String merCupNo);
|
||||
}
|
||||
|
||||
@ -11,6 +11,9 @@ package com.suisung.mall.shop.lakala.service;
|
||||
import com.suisung.mall.common.modules.lakala.LklOrderSeparate;
|
||||
import com.suisung.mall.core.web.service.IBaseService;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface LklOrderSeparateService extends IBaseService<LklOrderSeparate> {
|
||||
|
||||
/**
|
||||
@ -28,7 +31,7 @@ public interface LklOrderSeparateService extends IBaseService<LklOrderSeparate>
|
||||
* @param outSeparateNo
|
||||
* @return
|
||||
*/
|
||||
LklOrderSeparate getByOutTradeNo(String logNo, String outSeparateNo);
|
||||
LklOrderSeparate getByLogNoAndOutTradeNo(String logNo, String outSeparateNo);
|
||||
|
||||
/**
|
||||
* 根据商户号和平台订单号查询记录
|
||||
@ -47,4 +50,36 @@ public interface LklOrderSeparateService extends IBaseService<LklOrderSeparate>
|
||||
* @return
|
||||
*/
|
||||
Boolean updateRemark(Long id, String remark);
|
||||
|
||||
|
||||
/**
|
||||
* 根据唯一组合键 logNo和 separateNo 修改备注
|
||||
*
|
||||
* @param logNo
|
||||
* @param separateNo
|
||||
* @param remark
|
||||
* @return
|
||||
*/
|
||||
Boolean updateRemark(String logNo, String separateNo, String remark);
|
||||
|
||||
|
||||
/**
|
||||
* 分页获取未成功分账的记录
|
||||
*
|
||||
* @param beginDate 开始时间(必须)
|
||||
* @param endDate 结束时间(可选,为空时默认为当前时间)
|
||||
* @param page 页码(从1开始)
|
||||
* @param size 每页大小
|
||||
* @return 未成功分账的记录列表,参数无效时返回空列表而非null
|
||||
*/
|
||||
List<LklOrderSeparate> getUnSuccessSeparateList(Date beginDate, Date endDate, Integer page, Integer size);
|
||||
|
||||
/**
|
||||
* 判断订单是否已经分账
|
||||
* 记录不存在或 status final_status 不是 SUCCESS 都表示没有分账
|
||||
*
|
||||
* @param orderId
|
||||
* @return
|
||||
*/
|
||||
Boolean isOrderSeparated(String orderId);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -83,6 +83,7 @@ public class LklBanksServiceImpl extends BaseServiceImpl<LklBanksMapper, LklBank
|
||||
/**
|
||||
* @param bankNo
|
||||
* @param branchBankNo
|
||||
* @param clearNo
|
||||
* @param type
|
||||
* @param keyword
|
||||
* @param pageNum
|
||||
@ -90,9 +91,9 @@ public class LklBanksServiceImpl extends BaseServiceImpl<LklBanksMapper, LklBank
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public IPage<Map> pageBranchBanksList(String bankNo, String branchBankNo, Integer type, String keyword, Integer pageNum, Integer pageSize) {
|
||||
public IPage<Map> pageBranchBanksList(String bankNo, String branchBankNo, String clearNo, Integer type, String keyword, Integer pageNum, Integer pageSize) {
|
||||
Page<Map> page = new Page<>(pageNum, pageSize);
|
||||
return lklBanksMapper.pageBranchBanksList(page, bankNo, branchBankNo, type, jiebaUtils.segmentForSearch(keyword));
|
||||
return lklBanksMapper.pageBranchBanksList(page, bankNo, branchBankNo, clearNo, type, jiebaUtils.segmentForSearch(keyword));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -107,7 +108,7 @@ public class LklBanksServiceImpl extends BaseServiceImpl<LklBanksMapper, LklBank
|
||||
return null;
|
||||
}
|
||||
|
||||
IPage<Map> list = lklBanksMapper.pageBranchBanksList(new Page<>(1, 1), null, branchBankNo, 2, null);
|
||||
IPage<Map> list = lklBanksMapper.pageBranchBanksList(new Page<>(1, 1), null, branchBankNo, "", 2, null);
|
||||
if (list == null || CollectionUtil.isEmpty(list.getRecords())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -135,4 +135,19 @@ public class LklLedgerEcServiceImpl extends BaseServiceImpl<LklLedgerEcMapper, L
|
||||
return findOne(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取拉卡拉商户签署合同页面URL
|
||||
*
|
||||
* @param mchId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String getLklEcResultUrl(Long mchId) {
|
||||
LklLedgerEc record = getByMchId(mchId, "", null);
|
||||
if (record != null) {
|
||||
return record.getResult_url();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -14,41 +14,78 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.modules.lakala.LklLedgerMerReceiverBind;
|
||||
import com.suisung.mall.common.utils.CheckUtil;
|
||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
import com.suisung.mall.shop.lakala.mapper.LklLedgerMerReceiverBindMapper;
|
||||
import com.suisung.mall.shop.lakala.service.LklLedgerMerReceiverBindService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl<LklLedgerMerReceiverBindMapper, LklLedgerMerReceiverBind> implements LklLedgerMerReceiverBindService {
|
||||
/**
|
||||
* 根据接收方编号新增或更新记录
|
||||
* 根据商户编号和接收方编号新增或更新记录
|
||||
*
|
||||
* @param record
|
||||
* @return
|
||||
* @param record 分账绑定记录
|
||||
* @return 操作结果,成功返回true,失败返回false
|
||||
*/
|
||||
@Override
|
||||
public Boolean addOrUpdateByMerCupNoReceiverNo(LklLedgerMerReceiverBind record) {
|
||||
if (record == null || StrUtil.isBlank(record.getMer_cup_no()) || StrUtil.isBlank(record.getReceiver_no())) {
|
||||
// 参数校验
|
||||
if (record == null || StrUtil.hasBlank(record.getMer_cup_no(), record.getReceiver_no())) {
|
||||
log.warn("分账绑定记录参数校验失败: record为空或关键字段缺失, mer_cup_no={}, receiver_no={}",
|
||||
record != null ? record.getMer_cup_no() : "null",
|
||||
record != null ? record.getReceiver_no() : "null");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 构造查询条件
|
||||
QueryWrapper<LklLedgerMerReceiverBind> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("mer_cup_no", record.getMer_cup_no())
|
||||
.eq("receiver_no", record.getReceiver_no())
|
||||
.eq("audit_status", CommonConstant.Enable);
|
||||
List<LklLedgerMerReceiverBind> existsRecordList = list(queryWrapper);
|
||||
if (!CollectionUtil.isEmpty(existsRecordList)
|
||||
&& existsRecordList.get(0) != null
|
||||
&& existsRecordList.get(0).getId() > 0) {
|
||||
// update
|
||||
record.setId(existsRecordList.get(0).getId());
|
||||
return updateById(record);
|
||||
.eq("audit_status", CommonConstant.Enable)
|
||||
.orderByAsc("id");
|
||||
|
||||
// 如果商户ID不为空,则增加商户ID查询条件
|
||||
if (CheckUtil.isNotEmpty(record.getMch_id())) {
|
||||
queryWrapper.eq("mch_id", record.getMch_id());
|
||||
}
|
||||
|
||||
return add(record);
|
||||
// 查询已存在的记录
|
||||
LklLedgerMerReceiverBind existsRecord = findOne(queryWrapper);
|
||||
|
||||
// 如果存在有效记录,则更新该记录
|
||||
if (existsRecord != null && existsRecord.getId() != null && existsRecord.getId() > 0) {
|
||||
log.debug("分账绑定记录已存在,执行更新操作: mer_cup_no={}, receiver_no={}, id={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no(), existsRecord.getId());
|
||||
record.setId(existsRecord.getId());
|
||||
boolean updateResult = updateById(record);
|
||||
if (updateResult) {
|
||||
log.info("分账绑定记录更新成功: mer_cup_no={}, receiver_no={}, id={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no(), record.getId());
|
||||
} else {
|
||||
log.error("分账绑定记录更新失败: mer_cup_no={}, receiver_no={}, id={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no(), record.getId());
|
||||
}
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
// 不存在有效记录,则新增记录
|
||||
log.debug("分账绑定记录不存在,执行新增操作: mer_cup_no={}, receiver_no={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no());
|
||||
boolean addResult = add(record);
|
||||
if (addResult) {
|
||||
log.info("分账绑定记录新增成功: mer_cup_no={}, receiver_no={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no());
|
||||
} else {
|
||||
log.error("分账绑定记录新增失败: mer_cup_no={}, receiver_no={}",
|
||||
record.getMer_cup_no(), record.getReceiver_no());
|
||||
}
|
||||
return addResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -122,7 +159,7 @@ public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl<LklLedg
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public LklLedgerMerReceiverBind getPlatformByApplyId(String applyId) {
|
||||
public LklLedgerMerReceiverBind getMerReceiverByApplyId(String applyId) {
|
||||
if (StrUtil.isBlank(applyId)) {
|
||||
return null;
|
||||
}
|
||||
@ -171,50 +208,106 @@ public class LklLedgerMerReceiverBindServiceImpl extends BaseServiceImpl<LklLedg
|
||||
/**
|
||||
* 更新审核结果
|
||||
*
|
||||
* @param applyId
|
||||
* @param merInnerNo
|
||||
* @param merCupNo
|
||||
* @param receiverNo
|
||||
* @param entrustFileName
|
||||
* @param entrustFilePath
|
||||
* @param auditStatus
|
||||
* @param auditStatusText
|
||||
* @param remark
|
||||
* @return
|
||||
* @param applyId 申请ID
|
||||
* @param merInnerNo 商户内部编号
|
||||
* @param merCupNo 商户银联编号
|
||||
* @param receiverNo 接收方编号
|
||||
* @param entrustFileName 委托文件名
|
||||
* @param entrustFilePath 委托文件路径
|
||||
* @param auditStatus 审核状态
|
||||
* @param auditStatusText 审核状态文本
|
||||
* @param remark 备注
|
||||
* @return 更新成功返回true,失败返回false
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String receiverNo, String entrustFileName, String entrustFilePath, String auditStatus, String auditStatusText, String remark) {
|
||||
if (StrUtil.isBlank(applyId) || StrUtil.isBlank(merCupNo) || StrUtil.isBlank(receiverNo) || StrUtil.isBlank(auditStatus)) {
|
||||
public Boolean updateAuditResult(String applyId, String merInnerNo, String merCupNo, String receiverNo,
|
||||
String entrustFileName, String entrustFilePath, String auditStatus,
|
||||
String auditStatusText, String remark) {
|
||||
// 检查必需参数是否为空
|
||||
if (StrUtil.hasBlank(applyId, merCupNo, receiverNo, auditStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateWrapper<LklLedgerMerReceiverBind> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("apply_id", applyId);
|
||||
if (StrUtil.isNotBlank(merInnerNo)) {
|
||||
updateWrapper.set("mer_inner_no", merInnerNo);
|
||||
}
|
||||
if (StrUtil.isNotBlank(merCupNo)) {
|
||||
updateWrapper.set("mer_cup_no", merCupNo);
|
||||
}
|
||||
if (StrUtil.isNotBlank(receiverNo)) {
|
||||
updateWrapper.set("receiver_no", receiverNo);
|
||||
}
|
||||
if (StrUtil.isNotBlank(entrustFileName)) {
|
||||
updateWrapper.set("entrust_file_name", entrustFileName);
|
||||
}
|
||||
if (StrUtil.isNotBlank(entrustFilePath)) {
|
||||
updateWrapper.set("entrust_file_path", entrustFilePath);
|
||||
}
|
||||
if (StrUtil.isNotBlank(auditStatus)) {
|
||||
updateWrapper.set("audit_status", auditStatus);
|
||||
}
|
||||
if (StrUtil.isNotBlank(auditStatusText)) {
|
||||
updateWrapper.set("audit_status_text", auditStatusText);
|
||||
}
|
||||
if (StrUtil.isNotBlank(remark)) {
|
||||
updateWrapper.set("remark", remark);
|
||||
}
|
||||
|
||||
// 只有当参数不为空时才更新对应字段
|
||||
updateWrapper.set("mer_inner_no", merInnerNo);
|
||||
updateWrapper.set("mer_cup_no", merCupNo);
|
||||
updateWrapper.set("receiver_no", receiverNo);
|
||||
updateWrapper.set("entrust_file_name", entrustFileName);
|
||||
updateWrapper.set("entrust_file_path", entrustFilePath);
|
||||
updateWrapper.set("audit_status", auditStatus);
|
||||
updateWrapper.set("audit_status_text", auditStatusText);
|
||||
updateWrapper.set("remark", remark);
|
||||
|
||||
return update(updateWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据入驻号和商户编号查询商户分账接收方绑定记录
|
||||
*
|
||||
* @param mchId 商户ID
|
||||
* @param merCupNo 商户银联编号
|
||||
* @return 符合条件的商户分账接收方绑定记录列表
|
||||
*/
|
||||
@Override
|
||||
public List<LklLedgerMerReceiverBind> selectByMchIdAndMerCupNo(Long mchId, String merCupNo) {
|
||||
log.debug("开始查询商户分账接收方绑定记录,mchId={}, merCupNo={}", mchId, merCupNo);
|
||||
|
||||
if (CheckUtil.isEmpty(mchId) && StrUtil.isBlank(merCupNo)) {
|
||||
log.warn("查询商户分账接收方绑定记录参数校验失败,mchId和merCupNo不能同时为空");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
QueryWrapper<LklLedgerMerReceiverBind> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("mch_id", mchId)
|
||||
.eq("mer_cup_no", merCupNo);
|
||||
|
||||
List<LklLedgerMerReceiverBind> result = list(queryWrapper);
|
||||
log.debug("查询商户分账接收方绑定记录完成,mchId={}, merCupNo={}, 结果数量={}", mchId, merCupNo, result.size());
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("查询商户分账接收方绑定记录时发生异常,mchId={}, merCupNo={}", mchId, merCupNo, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据入驻号和商户编号删除商户分账接收方绑定记录
|
||||
*
|
||||
* @param mchId 商户ID
|
||||
* @param merCupNo 商户银联编号
|
||||
* @return 删除成功返回true,失败返回false
|
||||
*/
|
||||
@Override
|
||||
public Boolean delByMchIdAndMerCupNo(Long mchId, String merCupNo) {
|
||||
try {
|
||||
log.debug("开始删除商户分账接收方绑定记录,mchId={}, merCupNo={}", mchId, merCupNo);
|
||||
|
||||
if (CheckUtil.isEmpty(mchId) && StrUtil.isBlank(merCupNo)) {
|
||||
log.warn("删除商户分账接收方绑定记录参数校验失败,mchId和merCupNo不能同时为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
QueryWrapper<LklLedgerMerReceiverBind> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("mch_id", mchId).eq("mer_cup_no", merCupNo);
|
||||
|
||||
boolean removeResult = remove(queryWrapper);
|
||||
|
||||
if (removeResult) {
|
||||
log.info("商户分账接收方绑定记录删除成功,mchId={}, merCupNo={}", mchId, merCupNo);
|
||||
} else {
|
||||
log.warn("商户分账接收方绑定记录删除失败或未找到匹配记录,mchId={}, merCupNo={}", mchId, merCupNo);
|
||||
}
|
||||
|
||||
return removeResult;
|
||||
} catch (Exception e) {
|
||||
log.error("删除商户分账接收方绑定记录时发生异常,mchId={}, merCupNo={}", mchId, merCupNo, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -137,14 +137,14 @@ public class LklLedgerReceiverServiceImpl extends BaseServiceImpl<LklLedgerRecei
|
||||
* 通过平台方(代理商) ID 记录信息转成 Lakala 接收方记录
|
||||
* 平台方一定要获取,如果有代理商也要获取
|
||||
*
|
||||
* @param platformId
|
||||
* @param distributorId 代理商id(非平台Id)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public JSONArray buildApplyLedgerReceiverReqParams(Long platformId) {
|
||||
public JSONArray buildApplyLedgerReceiverReqParams(Long distributorId) {
|
||||
List<Long> ids = new ArrayList<>();
|
||||
if (platformId != null && platformId > 0) {
|
||||
ids.add(platformId);
|
||||
if (distributorId != null && distributorId > 0) {
|
||||
ids.add(distributorId);
|
||||
}
|
||||
// 获取平台记录和代理商记录
|
||||
List<EsignPlatformInfo> esignPlatformInfoList = esignPlatformInfoService.getDistributorAndPlatformByIds(ids.toArray(new Long[0]));
|
||||
@ -216,14 +216,14 @@ public class LklLedgerReceiverServiceImpl extends BaseServiceImpl<LklLedgerRecei
|
||||
* 内部调用:申请一个或多个分账接收方(平台方和代理商)
|
||||
*
|
||||
* @param mchId
|
||||
* @param merCupNo 用于标记接收方绑定merCupNo商家
|
||||
* @param platformId
|
||||
* @param merCupNo 用于标记接收方绑定merCupNo商家
|
||||
* @param distributorId 代理商id(非平台id)
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Boolean innerApplyLedgerReceiver(Long mchId, String merCupNo, Long platformId) {
|
||||
public Boolean innerApplyLedgerReceiver(Long mchId, String merCupNo, Long distributorId) {
|
||||
// 接收方至少有一个平台方
|
||||
JSONArray buildApplyLedgerReceiverReqParams = buildApplyLedgerReceiverReqParams(platformId);
|
||||
JSONArray buildApplyLedgerReceiverReqParams = buildApplyLedgerReceiverReqParams(distributorId);
|
||||
if (buildApplyLedgerReceiverReqParams == null || buildApplyLedgerReceiverReqParams.isEmpty()) {
|
||||
log.error("先新增平台或代理商信息");
|
||||
return false;
|
||||
|
||||
@ -11,6 +11,7 @@ package com.suisung.mall.shop.lakala.service.impl;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.suisung.mall.common.modules.lakala.LklOrderSeparate;
|
||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
import com.suisung.mall.shop.lakala.mapper.LklOrderSeparateMapper;
|
||||
@ -18,6 +19,8 @@ import com.suisung.mall.shop.lakala.service.LklOrderSeparateService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
@Slf4j
|
||||
@ -54,12 +57,12 @@ public class LklOrderSeparateServiceImpl extends BaseServiceImpl<LklOrderSeparat
|
||||
/**
|
||||
* 根据拉卡拉对账单流水号和平台订单号查询记录
|
||||
*
|
||||
* @param logNo
|
||||
* @param outSeparateNo
|
||||
* @param logNo 分账对账流水号
|
||||
* @param outSeparateNo 分账商家的订单号
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public LklOrderSeparate getByOutTradeNo(String logNo, String outSeparateNo) {
|
||||
public LklOrderSeparate getByLogNoAndOutTradeNo(String logNo, String outSeparateNo) {
|
||||
try {
|
||||
if (StrUtil.isBlank(logNo) || StrUtil.isBlank(outSeparateNo)) {
|
||||
log.warn("查询参数为空:logNo={}, outSeparateNo={}", logNo, outSeparateNo);
|
||||
@ -130,4 +133,121 @@ public class LklOrderSeparateServiceImpl extends BaseServiceImpl<LklOrderSeparat
|
||||
lklOrderSeparate.setRemark(remark);
|
||||
return updateById(lklOrderSeparate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据唯一组合键 logNo和 separateNo 修改备注
|
||||
*
|
||||
* @param logNo
|
||||
* @param separateNo
|
||||
* @param remark
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateRemark(String logNo, String separateNo, String remark) {
|
||||
if (StrUtil.isEmpty(logNo) || StrUtil.isEmpty(separateNo) || StrUtil.isEmpty(remark)) {
|
||||
return false;
|
||||
}
|
||||
LklOrderSeparate lklOrderSeparate = new LklOrderSeparate();
|
||||
lklOrderSeparate.setLog_no(logNo);
|
||||
lklOrderSeparate.setSeparate_no(separateNo);
|
||||
lklOrderSeparate.setRemark(remark);
|
||||
return updateById(lklOrderSeparate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页获取未成功分账的记录
|
||||
*
|
||||
* @param beginDate 开始时间(必须)
|
||||
* @param endDate 结束时间(可选,为空时默认为当前时间)
|
||||
* @param page 页码(从1开始)
|
||||
* @param pageSize 每页大小
|
||||
* @return 未成功分账的记录列表,参数无效时返回空列表而非null
|
||||
*/
|
||||
@Override
|
||||
public List<LklOrderSeparate> getUnSuccessSeparateList(Date beginDate, Date endDate, Integer page, Integer pageSize) {
|
||||
// 1. 参数校验
|
||||
if (beginDate == null) {
|
||||
log.warn("[分页查询未成功分账记录] 开始时间不能为空");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (page == null || page < 1) {
|
||||
log.warn("[分页查询未成功分账记录] 页码必须大于0,当前页码: {}", page);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (pageSize == null || pageSize <= 0) {
|
||||
log.warn("[分页查询未成功分账记录] 页面大小必须大于0,当前大小: {}", pageSize);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 2. 处理结束时间默认值
|
||||
Date actualEndDate = (endDate != null) ? endDate : new Date();
|
||||
|
||||
// 3. 参数合理性校验
|
||||
if (beginDate.after(actualEndDate)) {
|
||||
log.warn("[分页查询未成功分账记录] 开始时间{}不能晚于结束时间{}", beginDate, actualEndDate);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. 构造查询条件
|
||||
QueryWrapper<LklOrderSeparate> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("id", "merchant_no", "separate_no", "status", "final_status", "created_at")
|
||||
.ne("status", "SUCCESS")
|
||||
.ne("final_status", "SUCCESS")
|
||||
.ge("created_at", beginDate)
|
||||
.le("created_at", actualEndDate)
|
||||
.orderByDesc("id");
|
||||
|
||||
// 5. 执行分页查询
|
||||
Page<LklOrderSeparate> resultPage = lists(queryWrapper, page, pageSize);
|
||||
List<LklOrderSeparate> result = resultPage.getRecords();
|
||||
|
||||
log.info("[分页查询未成功分账记录] 查询完成,开始时间={},结束时间={},页码={},页面大小={},结果数量={}",
|
||||
beginDate, actualEndDate, page, pageSize, (result != null ? result.size() : 0));
|
||||
|
||||
return result != null ? result : Collections.emptyList();
|
||||
} catch (Exception e) {
|
||||
log.error("[分页查询未成功分账记录] 查询过程中发生异常,开始时间={},结束时间={},页码={},页面大小={}",
|
||||
beginDate, endDate, page, pageSize, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断订单是否已经分账完成
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @return Boolean true-分账已完成,false-分账未完成或不存在分账记录
|
||||
*/
|
||||
@Override
|
||||
public Boolean isOrderSeparated(String orderId) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
log.warn("订单ID为空,无法判断分账状态");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 直接查询分账成功的记录
|
||||
LklOrderSeparate lklOrderSeparate = findOne(new QueryWrapper<LklOrderSeparate>()
|
||||
.eq("order_id", orderId)
|
||||
.eq("status", "SUCCESS")
|
||||
.eq("final_status", "SUCCESS")
|
||||
.orderByDesc("id"));
|
||||
|
||||
// 如果查询到分账成功的记录,返回true
|
||||
if (lklOrderSeparate == null) {
|
||||
log.debug("订单[{}]分账未完成或不存在分账记录", orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug("订单[{}]分账已完成", orderId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("查询订单[{}]分账状态异常", orderId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ import com.suisung.mall.common.modules.store.ShopMchEntry;
|
||||
import com.suisung.mall.common.pojo.to.AddressParseResultTO;
|
||||
import com.suisung.mall.common.service.impl.CommonService;
|
||||
import com.suisung.mall.common.utils.AddressUtil;
|
||||
import com.suisung.mall.common.utils.CheckUtil;
|
||||
import com.suisung.mall.common.utils.RestTemplateHttpUtil;
|
||||
import com.suisung.mall.common.utils.UploadUtil;
|
||||
import com.suisung.mall.core.web.service.RedisService;
|
||||
@ -318,44 +317,54 @@ public class LklTkServiceImpl {
|
||||
}
|
||||
|
||||
/**
|
||||
* (重要) 请求拉卡拉进件,
|
||||
* TODO 已经进件过了,一般不涉及更改企业(小微)的信息,可以不要重新进件了
|
||||
* <p>
|
||||
* 参考拉卡拉给的独立文档:2、拓客SAAS商户管理接口(新).docx
|
||||
* (重要) 请求拉卡拉进件
|
||||
*
|
||||
* @param mchId 入驻商家自增Id
|
||||
* @return
|
||||
* <p>此方法用于向拉卡拉提交商户进件申请,包括完整的商户信息和相关附件。
|
||||
* 已经进件过的商户一般不涉及更改企业(小微)的信息,可以不要重新进件。
|
||||
*
|
||||
* <p>参考拉卡拉给的独立文档:2、拓客SAAS商户管理接口(新).docx
|
||||
*
|
||||
* @param mchId 入驻商家自增Id,不能为空
|
||||
* @return Pair对象,第一个元素表示操作是否成功,第二个元素为操作结果信息
|
||||
*/
|
||||
public Pair<Boolean, String> registrationMerchant(Long mchId) {
|
||||
logger.info("开始执行拉卡拉商户进件流程,商户ID: {}", mchId);
|
||||
|
||||
// 参数校验
|
||||
if (ObjectUtil.isEmpty(mchId)) {
|
||||
return Pair.of(false, "入驻商户Id不能为空");
|
||||
logger.warn("商户进件失败:入驻编号不能为空");
|
||||
return Pair.of(false, "入驻编号不能为空");
|
||||
}
|
||||
|
||||
String authorization = getLklTkAuthorization();
|
||||
if (StrUtil.isBlank(authorization)) {
|
||||
logger.error("商户进件失败:获取拉卡拉token失败");
|
||||
return Pair.of(false, "获取拉卡拉token失败");
|
||||
}
|
||||
|
||||
JSONObject header = new JSONObject();
|
||||
header.put("Authorization", getLklTkAuthorization());
|
||||
header.put("Authorization", authorization);
|
||||
|
||||
// 获取商家入驻信息,组成请求参数
|
||||
ShopMchEntry shopMchEntry = shopMchEntryService.shopMerchEntryById(mchId);
|
||||
if (ObjectUtil.isEmpty(shopMchEntry)) {
|
||||
logger.warn("商户进件失败:商家入驻信息不存在,商户ID: {}", mchId);
|
||||
return Pair.of(false, "商家入驻信息不存在");
|
||||
}
|
||||
|
||||
if (!CommonConstant.Enable.equals(shopMchEntry.getHas_ec_signed())
|
||||
|| StrUtil.isBlank(shopMchEntry.getContract_download_url())) {
|
||||
logger.warn("商户进件失败:商家未签署合同,商户ID: {}", mchId);
|
||||
return Pair.of(false, "商家先签署合同,再来进件!");
|
||||
}
|
||||
|
||||
// 判断是否已经进件过?进件过,执行下一步操作(进件异步通知的相关操作)
|
||||
|
||||
//密集操作:进件审核通过之后,要下一步流程操作:申请分账业务、创建分账接收方
|
||||
if (CommonConstant.Enable.equals(shopMchEntry.getHas_apply_mer()) && CheckUtil.isNotEmpty(shopMchEntry.getDistributor_id())
|
||||
&& StrUtil.isAllNotBlank(shopMchEntry.getLkl_mer_cup_no(), shopMchEntry.getLkl_term_no())) {
|
||||
// 密集操作:进件审核通过之后,要下一步流程操作:申请分账业务、创建分账接收方
|
||||
// && CheckUtil.isNotEmpty(shopMchEntry.getDistributor_id())
|
||||
if (CommonConstant.Enable.equals(shopMchEntry.getHas_apply_mer())
|
||||
&& !StrUtil.hasBlank(shopMchEntry.getLkl_mer_cup_no(), shopMchEntry.getLkl_term_no())) {
|
||||
// 已经进件过了,执行下一步操作
|
||||
logger.info("商户已进件,执行后续操作,商户ID: {}", mchId);
|
||||
registrationMerchantAfterHook(mchId, shopMchEntry.getLkl_mer_cup_no(), shopMchEntry.getDistributor_id());
|
||||
return Pair.of(true, "请勿重复提交,拉卡拉已进件成功了,准备提交分账业务申请!");
|
||||
}
|
||||
@ -415,8 +424,7 @@ public class LklTkServiceImpl {
|
||||
reqJsonBody.put("accountName", shopMchEntry.getAccount_holder_name()); //结算人账户名称
|
||||
reqJsonBody.put("accountIdCard", larIdCard);//结算⼈(法人或小微个人)证件号码(身份证)
|
||||
|
||||
String settleType = String.format("D%d", 1);
|
||||
reqJsonBody.put("settleType", settleType); //结算类型:0-秒到(不分账);1-次日结算(需要分账)
|
||||
reqJsonBody.put("settleType", "D1"); //结算类型:0-秒到(不分账);1-次日结算(需要分账)
|
||||
reqJsonBody.put("settlementType", "AUTOMATIC"); // 结算方式:MANUAL:手动结算(结算至拉卡拉APP钱包),AUTOMATIC:自动结算到银行卡,REGULAR:定时结算(仅企业商户支持)
|
||||
|
||||
// 店铺省市区信息
|
||||
@ -475,60 +483,87 @@ public class LklTkServiceImpl {
|
||||
reqJsonBody.put("bizContent", bizContent);
|
||||
|
||||
// 附件文件相关开始
|
||||
logger.debug("开始处理商户附件文件,商户ID: {}", mchId);
|
||||
JSONArray attachments = new JSONArray();
|
||||
if (isQy) {
|
||||
if (Boolean.TRUE.equals(isQy)) {
|
||||
JSONObject SETTLE_ID_CARD_FRONT = updatePhoto(shopMchEntry.getLegal_person_id_images(), "FR_ID_CARD_FRONT", false);
|
||||
if (SETTLE_ID_CARD_FRONT != null) {
|
||||
attachments.put(SETTLE_ID_CARD_FRONT); // 法人身份证正面
|
||||
logger.debug("成功添加法人身份证正面图片");
|
||||
} else {
|
||||
logger.warn("法人身份证正面图片添加失败");
|
||||
}
|
||||
|
||||
JSONObject SETTLE_ID_CARD_BEHIND = updatePhoto(shopMchEntry.getLegal_person_id_images2(), "FR_ID_CARD_BEHIND", false);
|
||||
if (SETTLE_ID_CARD_BEHIND != null) {
|
||||
attachments.put(SETTLE_ID_CARD_BEHIND); // 法人身份证国徽面
|
||||
logger.debug("成功添加法人身份证国徽面图片");
|
||||
} else {
|
||||
logger.warn("法人身份证国徽面图片添加失败");
|
||||
}
|
||||
|
||||
JSONObject BUSINESS_LICENCE = updatePhoto(shopMchEntry.getBiz_license_image(), "BUSINESS_LICENCE", false);
|
||||
if (BUSINESS_LICENCE != null) {
|
||||
attachments.put(BUSINESS_LICENCE); // 营业执照
|
||||
logger.debug("成功添加营业执照图片");
|
||||
} else {
|
||||
logger.warn("营业执照图片添加失败");
|
||||
}
|
||||
|
||||
} else {
|
||||
JSONObject ID_CARD_FRONT = updatePhoto(shopMchEntry.getIndividual_id_images(), "ID_CARD_FRONT", false);
|
||||
if (ID_CARD_FRONT != null) {
|
||||
attachments.put(ID_CARD_FRONT); // 身份证正面
|
||||
logger.debug("成功添加身份证正面图片");
|
||||
} else {
|
||||
logger.warn("身份证正面图片添加失败");
|
||||
}
|
||||
|
||||
JSONObject ID_CARD_BEHIND = updatePhoto(shopMchEntry.getIndividual_id_images2(), "ID_CARD_BEHIND", false);
|
||||
if (ID_CARD_BEHIND != null) {
|
||||
attachments.put(ID_CARD_BEHIND); // 身份证国徽面
|
||||
logger.debug("成功添加身份证国徽面图片");
|
||||
} else {
|
||||
logger.warn("身份证国徽面图片添加失败");
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject SHOP_OUTSIDE_IMG = updatePhoto(shopMchEntry.getFront_facade_image(), "SHOP_OUTSIDE_IMG", false);
|
||||
if (SHOP_OUTSIDE_IMG != null) {
|
||||
attachments.put(SHOP_OUTSIDE_IMG); // 门店门面图片
|
||||
logger.debug("成功添加门店门面图片");
|
||||
} else {
|
||||
logger.warn("门店门面图片添加失败");
|
||||
}
|
||||
|
||||
JSONObject SHOP_INSIDE_IMG = updatePhoto(shopMchEntry.getEnvironment_image(), "SHOP_INSIDE_IMG", false);
|
||||
if (SHOP_INSIDE_IMG != null) {
|
||||
attachments.put(SHOP_INSIDE_IMG); // 门店内部图片
|
||||
logger.debug("成功添加门店内部图片");
|
||||
} else {
|
||||
logger.warn("门店内部图片添加失败");
|
||||
}
|
||||
|
||||
JSONObject BANK_CARD = updatePhoto(shopMchEntry.getBank_image(), "BANK_CARD", false);
|
||||
if (BANK_CARD != null) {
|
||||
attachments.put(BANK_CARD); // 银行卡图片
|
||||
logger.debug("成功添加银行卡图片");
|
||||
} else {
|
||||
logger.warn("银行卡图片添加失败");
|
||||
}
|
||||
reqJsonBody.put("attchments", attachments);
|
||||
logger.debug("商户附件文件处理完成,共添加 {} 个附件", attachments.size());
|
||||
// 附件文件相关结束
|
||||
|
||||
String urlPath = "/sit/htkregistration/merchant";
|
||||
if (isLklProd) {
|
||||
if (Boolean.TRUE.equals(isLklProd)) {
|
||||
// 生产环境启用
|
||||
urlPath = "/registration/merchant";
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info("进件请求参数:{}", JSONUtil.toJsonStr(reqJsonBody));
|
||||
logger.info("准备提交拉卡拉进件请求,商户ID: {}", mchId);
|
||||
logger.debug("进件请求参数:{}", JSONUtil.toJsonStr(reqJsonBody));
|
||||
|
||||
JSONObject response = RestTemplateHttpUtil.sendLklPost(buildLklTkUrl(urlPath), header, reqJsonBody, JSONObject.class);
|
||||
logger.debug("拉卡拉进件响应参数:{}", response);
|
||||
@ -538,6 +573,7 @@ public class LklTkServiceImpl {
|
||||
|| !"000000".equals(response.getStr("retCode"))) {
|
||||
|
||||
String errMsg = response.getStr("retMsg") == null ? "提交拉卡拉进件,出现未知错误" : response.getStr("retMsg");
|
||||
logger.error("拉卡拉进件失败,商户ID: {},错误信息: {}", mchId, errMsg);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_NOPASS, "提交拉卡拉进件失败:" + errMsg);
|
||||
return Pair.of(false, "提交拉卡拉进件失败:" + errMsg);
|
||||
|
||||
@ -546,19 +582,24 @@ public class LklTkServiceImpl {
|
||||
// {"merchantNo": "100132349","status": "WAIT_AUDI","state": "1"}
|
||||
JSONObject rawData = response.getJSONObject("rawData"); // 进件这个接口比较特殊,不按照常规返回数据
|
||||
String lklMerInnerNo = rawData.getStr("merchantNo"); //拉卡拉内部商户号
|
||||
|
||||
logger.info("拉卡拉进件成功,商户ID: {},拉卡拉商户号: {}", mchId, lklMerInnerNo);
|
||||
|
||||
// 表中的内部外部商户号暂时都传同一个内部商户号,以便异步通知更改记录
|
||||
Boolean success = shopMchEntryService.updateMerchEntryLklMerCupNo(mchId, CommonConstant.Disable2, lklMerInnerNo, lklMerInnerNo, reqJsonBody.toString(), rawData.toString());
|
||||
if (!success) {
|
||||
if (!Boolean.TRUE.equals(success)) {
|
||||
logger.error("进件成功但更新商户号失败,商户ID: {}", mchId);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件成功,但更新商户号失败!");
|
||||
return Pair.of(false, "提交进件成功,但更新商户号失败!");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("拉卡拉进件异常:{}", e.getMessage());
|
||||
logger.error("拉卡拉进件异常,商户ID: {}", mchId, e);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件失败:" + e.getMessage());
|
||||
return Pair.of(false, "提交拉卡拉进件失败:" + e.getMessage());
|
||||
}
|
||||
|
||||
logger.info("拉卡拉进件提交成功,等待审核,商户ID: {}", mchId);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_PADDING, "提交拉卡拉进件成功,正进一步审核!");
|
||||
return Pair.of(true, "提交拉卡拉进件成功,正进一步审核!");
|
||||
}
|
||||
@ -566,91 +607,117 @@ public class LklTkServiceImpl {
|
||||
/**
|
||||
* (重要)拉卡拉进件异步通知
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* <p>处理拉卡拉平台发送的商户进件结果异步通知,包括解密通知数据、更新商户状态等操作。
|
||||
*
|
||||
* @param request HTTP请求对象,包含拉卡拉发送的异步通知数据
|
||||
* @return JSONObject 响应结果,包含处理状态码和消息
|
||||
*/
|
||||
// @Transactional
|
||||
public JSONObject registrationMerchantNotify(HttpServletRequest request) {
|
||||
logger.debug("拉卡拉进件异步通知开始");
|
||||
logger.info("开始处理拉卡拉进件异步通知");
|
||||
|
||||
// 解密请求参数
|
||||
String requestBody = LakalaUtil.getBody(request);
|
||||
logger.debug("拉卡拉进件异步通知返回参数:{}", requestBody);
|
||||
try {
|
||||
// 解密请求参数
|
||||
String requestBody = LakalaUtil.getBody(request);
|
||||
logger.debug("拉卡拉进件异步通知原始参数:{}", requestBody);
|
||||
|
||||
if (StrUtil.isBlank(requestBody)) {
|
||||
return new JSONObject().set("code", "400").set("message", "返回参数为空");
|
||||
if (StrUtil.isBlank(requestBody)) {
|
||||
logger.warn("拉卡拉进件异步通知参数为空");
|
||||
return new JSONObject().set("code", "400").set("message", "返回参数为空");
|
||||
}
|
||||
|
||||
JSONObject reqBodyJSON = JSONUtil.parseObj(requestBody);
|
||||
if (reqBodyJSON.isEmpty() || reqBodyJSON.get("data") == null) {
|
||||
logger.warn("拉卡拉进件异步通知参数格式有误,无法解析: {}", requestBody);
|
||||
return new JSONObject().set("code", "400").set("message", "参数格式有误,无法解析");
|
||||
}
|
||||
|
||||
String srcData = reqBodyJSON.getStr("data");
|
||||
if (StrUtil.isBlank(srcData)) {
|
||||
logger.warn("拉卡拉进件异步通知关键参数为空值");
|
||||
return new JSONObject().set("code", "400").set("message", "关键参数为空值");
|
||||
}
|
||||
|
||||
// 公钥解密出来的数据
|
||||
logger.debug("开始解密拉卡拉通知数据");
|
||||
String notifyPubKey = LakalaUtil.getResourceFile(notifyPubKeyPath, false, false);
|
||||
String data = LakalaUtil.decryptNotifyData(notifyPubKey, srcData);
|
||||
if (StrUtil.isBlank(data)) {
|
||||
logger.error("拉卡拉进件异步通知数据解密失败");
|
||||
return new JSONObject().set("code", "400").set("message", "数据解密出错!");
|
||||
}
|
||||
|
||||
logger.info("拉卡拉进件异步通知数据解密成功,开始处理业务逻辑");
|
||||
|
||||
// 逻辑处理
|
||||
JSONObject dataJSON = JSONUtil.parseObj(data);
|
||||
String auditStatus = dataJSON.getStr("status");
|
||||
String merCupNo = dataJSON.getStr("externalCustomerNo"); //拉卡拉外部商户号
|
||||
String merInnerNo = dataJSON.getStr("customerNo"); //拉卡拉内部商户号
|
||||
String termNos = dataJSON.getStr("termNos"); //拉卡拉分配的业务终端号
|
||||
|
||||
logger.debug("解析通知数据完成 - 审核状态: {},外部商户号: {},内部商户号: {},终端号: {}",
|
||||
auditStatus, merCupNo, merInnerNo, termNos);
|
||||
|
||||
// 合并参数校验
|
||||
if (dataJSON.isEmpty() ||
|
||||
StrUtil.isBlank(auditStatus) ||
|
||||
StrUtil.isBlank(merCupNo) ||
|
||||
StrUtil.isBlank(merInnerNo)) {
|
||||
logger.warn("拉卡拉进件异步通知参数解析出错,数据: {}", data);
|
||||
return new JSONObject().set("code", "500").set("message", "参数解析出错");
|
||||
}
|
||||
|
||||
// 给商家入驻表增加拉卡拉的商户号和拉卡拉返回的数据
|
||||
logger.debug("开始查询商户入驻信息,内部商户号: {}", merInnerNo);
|
||||
ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerInnerNo(merInnerNo);
|
||||
if (shopMchEntry == null) {
|
||||
logger.error("拉卡拉进件异步通知:{}内部商户号入驻信息不存在!", merInnerNo);
|
||||
return new JSONObject().put("code", "500").put("message", merInnerNo + "内部商户号入驻信息不存在");
|
||||
}
|
||||
|
||||
Long mchId = shopMchEntry.getId();
|
||||
logger.info("找到商户入驻信息,商户ID: {}", mchId);
|
||||
|
||||
// 校验审核状态
|
||||
logger.debug("校验审核状态: {}", auditStatus);
|
||||
if (!"SUCCESS".equals(auditStatus) && !"REVIEW_PASS".equals(auditStatus)) {
|
||||
String remark = dataJSON.getStr("remark");
|
||||
logger.warn("拉卡拉进件审核未通过,审核状态: {},备注: {}", auditStatus, remark);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(mchId, CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件失败:" + remark);
|
||||
return new JSONObject().set("code", "FAIL").set("message", "返回审核状态有误");
|
||||
}
|
||||
|
||||
logger.info("拉卡拉进件审核通过,商户ID: {},开始更新商户信息", mchId);
|
||||
|
||||
// RMK 拉卡拉进价提交成功,边处理周边的数据,边等待审核异步通知
|
||||
|
||||
// 更新已进件成功的商户号和设备号
|
||||
logger.debug("开始更新商户拉卡拉审核状态");
|
||||
Boolean success = shopMchEntryService.updateMerchEntryLklAuditStatusByLklMerCupNo(
|
||||
merInnerNo, merCupNo, termNos, CommonConstant.Enable, null, data);
|
||||
|
||||
if (!Boolean.TRUE.equals(success)) {
|
||||
logger.error("拉卡拉进件审核通过但更新商户号失败,商户ID: {}", mchId);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(mchId, CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件时更新商户号失败");
|
||||
return new JSONObject().set("code", "500").set("message", "更新商户号失败");
|
||||
}
|
||||
|
||||
logger.info("商户拉卡拉审核状态更新成功,商户ID: {}", mchId);
|
||||
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_PADDING,
|
||||
"进件、申请分账业务、创建分账接收方均已成功,等待拉卡拉审核分账业务请求!");
|
||||
|
||||
//密集操作:进件审核通过之后,要下一步流程操作:申请分账业务、创建分账接收方
|
||||
logger.info("开始执行进件后续操作,商户ID: {},拉卡拉商户号: {}", mchId, merCupNo);
|
||||
registrationMerchantAfterHook(mchId, merCupNo, shopMchEntry.getDistributor_id());
|
||||
|
||||
logger.info("拉卡拉进件异步通知处理完成,商户ID: {}", mchId);
|
||||
return new JSONObject().set("code", "200").set("message", "处理成功");
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("处理拉卡拉进件异步通知异常", e);
|
||||
return new JSONObject().set("code", "500").set("message", "处理异常:" + e.getMessage());
|
||||
}
|
||||
|
||||
JSONObject reqBodyJSON = JSONUtil.parseObj(requestBody);
|
||||
if (reqBodyJSON.isEmpty() || reqBodyJSON.get("data") == null) {
|
||||
return new JSONObject().set("code", "400").set("message", "参数格式有误,无法解析");
|
||||
}
|
||||
|
||||
String srcData = reqBodyJSON.getStr("data");
|
||||
if (StrUtil.isBlank(srcData)) {
|
||||
return new JSONObject().set("code", "400").set("message", "关键参数为空值");
|
||||
}
|
||||
|
||||
// 公钥解密出来的数据
|
||||
String notifyPubKey = LakalaUtil.getResourceFile(notifyPubKeyPath, false, false);
|
||||
String data = LakalaUtil.decryptNotifyData(notifyPubKey, srcData);
|
||||
if (StrUtil.isBlank(data)) {
|
||||
return new JSONObject().set("code", "400").set("message", "数据解密出错!");
|
||||
}
|
||||
|
||||
logger.debug("拉卡拉进件异步通知data解密成功,开始处理逻辑");
|
||||
|
||||
// 逻辑处理
|
||||
JSONObject dataJSON = JSONUtil.parseObj(data);
|
||||
String auditStatus = dataJSON.getStr("status");
|
||||
String merCupNo = dataJSON.getStr("externalCustomerNo"); //拉卡拉外部商户号
|
||||
String merInnerNo = dataJSON.getStr("customerNo"); //拉卡拉内部商户号
|
||||
String termNos = dataJSON.getStr("termNos"); //拉卡拉分配的业务终端号
|
||||
|
||||
// 合并参数校验
|
||||
if (dataJSON.isEmpty() ||
|
||||
StrUtil.isBlank(auditStatus) ||
|
||||
StrUtil.isBlank(merCupNo) ||
|
||||
StrUtil.isBlank(merInnerNo)) {
|
||||
return new JSONObject().set("code", "500").set("message", "参数解析出错");
|
||||
}
|
||||
|
||||
// 给商家入驻表增加拉卡拉的商户号和拉卡拉返回的数据
|
||||
ShopMchEntry shopMchEntry = shopMchEntryService.getShopMerchEntryByMerInnerNo(merInnerNo);
|
||||
if (shopMchEntry == null) {
|
||||
logger.error("拉卡拉进件异步通知:{}内部商户号入驻信息不存在!", merInnerNo);
|
||||
return new JSONObject().put("code", "500").put("message", merInnerNo + "内部商户号入驻信息不存在");
|
||||
}
|
||||
|
||||
Long mchId = shopMchEntry.getId();
|
||||
|
||||
|
||||
// 校验审核状态
|
||||
if (!"SUCCESS".equals(auditStatus) && !"REVIEW_PASS".equals(auditStatus)) {
|
||||
logger.debug("返回的审核状态:{}", auditStatus);
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(mchId, CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件失败:" + dataJSON.getStr("remark"));
|
||||
return new JSONObject().set("code", "FAIL").set("message", "返回审核状态有误");
|
||||
}
|
||||
|
||||
// RMK 拉卡拉进价提交成功,边处理周边的数据,边等待审核异步通知
|
||||
|
||||
// 更新已进件成功的商户号和设备号
|
||||
Boolean success = shopMchEntryService.updateMerchEntryLklAuditStatusByLklMerCupNo(
|
||||
merInnerNo, merCupNo, termNos, CommonConstant.Enable, null, data);
|
||||
|
||||
if (!success) {
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(mchId, CommonConstant.MCH_APPR_STA_LKL_NOPASS, "进件时更新商户号失败");
|
||||
return new JSONObject().set("code", "500").set("message", "更新商户号失败");
|
||||
}
|
||||
|
||||
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(shopMchEntry.getId(), CommonConstant.MCH_APPR_STA_LKL_PADDING,
|
||||
"进件、申请分账业务、创建分账接收方均已成功,等待拉卡拉审核分账业务请求!");
|
||||
|
||||
//密集操作:进件审核通过之后,要下一步流程操作:申请分账业务、创建分账接收方
|
||||
registrationMerchantAfterHook(mchId, merCupNo, shopMchEntry.getDistributor_id());
|
||||
|
||||
return new JSONObject().set("code", "200").set("message", "处理成功");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -664,7 +731,7 @@ public class LklTkServiceImpl {
|
||||
logger.info("商家进件已成功,下一步申请拉卡拉分账业务,再创建分账接收方!");
|
||||
|
||||
// 重要:给商家申请分账业务;务必检查是否申请过?申请过忽略
|
||||
Pair<Boolean, String> applyRetPair = lakalaApiService.innerApplyLedgerMer(merCupNo);
|
||||
Pair<Boolean, String> applyRetPair = lakalaApiService.innerApplyLedgerMer(merCupNo, true);
|
||||
if (!applyRetPair.getFirst()) {
|
||||
String message = "提交分账业务申请异常:" + applyRetPair.getSecond();
|
||||
shopMchEntryService.updateMerchEntryApprovalByMchId(mchId, null, message);
|
||||
@ -857,5 +924,44 @@ public class LklTkServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject bankList(String areaCode, String bankName) {
|
||||
if (StrUtil.isBlank(areaCode)) {
|
||||
}
|
||||
|
||||
JSONObject header = new JSONObject();
|
||||
header.put("Authorization", getLklTkAuthorization());
|
||||
|
||||
// Base64Utils.encodeToString(file.getBytes());
|
||||
|
||||
JSONObject requestBody = new JSONObject();
|
||||
requestBody.put("areaCode", areaCode);
|
||||
requestBody.put("bankName", bankName);
|
||||
|
||||
String urlPath = "/sit/registration/bank";
|
||||
if (isLklProd) {
|
||||
// 生产环境启用
|
||||
urlPath = "/registration/bank";
|
||||
}
|
||||
|
||||
try {
|
||||
ResponseEntity<JSONObject> updResponse = RestTemplateHttpUtil.sendPostBodyBackEntity(buildLklTkUrl(urlPath), header, requestBody, JSONObject.class);
|
||||
if (ObjectUtil.isEmpty(updResponse)
|
||||
|| updResponse.getStatusCode() != HttpStatus.OK) {
|
||||
logger.error("上传文件返回值有误");
|
||||
return null;
|
||||
}
|
||||
|
||||
JSONObject result = updResponse.getBody();
|
||||
if (ObjectUtil.isEmpty(result)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JSONObject().put("id", result.get("url")).put("type", "");
|
||||
} catch (Exception e) {
|
||||
logger.error("上传文件异常:{}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
|
||||
package com.suisung.mall.shop.lakala.utils;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.lkl.laop.sdk.Config2;
|
||||
import com.lkl.laop.sdk.LKLSDK;
|
||||
import com.suisung.mall.common.exception.ApiException;
|
||||
@ -32,6 +35,7 @@ import java.security.cert.*;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
@ -262,6 +266,9 @@ public class LakalaUtil {
|
||||
try {
|
||||
log.debug("拉卡拉 Authorization 内容:{}", authorization);
|
||||
Map<String, String> headerMap = getLakalaAuthorizationMap(authorization);
|
||||
if (headerMap == null) {
|
||||
return false;
|
||||
}
|
||||
String timestamp = headerMap.get("timestamp");
|
||||
String nonceStr = headerMap.get("nonce_str");
|
||||
String signature = headerMap.get("signature");
|
||||
@ -304,6 +311,12 @@ public class LakalaUtil {
|
||||
*/
|
||||
public static Map<String, String> getLakalaAuthorizationMap(String authorization) {
|
||||
Map<String, String> map = new HashMap();
|
||||
if (StrUtil.isBlank(authorization)) {
|
||||
log.error("请求头中无 Authorization 授权信息");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
authorization = authorization.trim();
|
||||
int bpos = authorization.indexOf(" ");
|
||||
String authType = authorization.substring(0, bpos);
|
||||
@ -465,14 +478,14 @@ public class LakalaUtil {
|
||||
if (passVerifySign) {
|
||||
// 如果不需要验签,直接返回请求体内容
|
||||
String requestBody = getBody(request);
|
||||
log.debug("###{} 异步回调通知请求过来的参数:{}###", request.getRequestURL(), requestBody);
|
||||
log.debug("###{} 异步回调通知返回的数据:{}###", request.getRequestURL(), requestBody);
|
||||
return Pair.of(true, requestBody);
|
||||
}
|
||||
|
||||
// 验签
|
||||
String authorization = request.getHeader("Authorization");
|
||||
String requestBody = getBody(request);
|
||||
log.info("###{} 异步回调通知请求过来的参数:{}\n authorization参数:{}###", request.getRequestURL(), requestBody, authorization);
|
||||
log.info("###{} 异步回调通知返回的数据:{}\n authorization参数:{}###", request.getRequestURL(), requestBody, authorization);
|
||||
|
||||
boolean checkSuccess = LakalaUtil.verify(authorization, requestBody, lklNotifyCerPath);
|
||||
if (!checkSuccess) {
|
||||
@ -482,4 +495,31 @@ public class LakalaUtil {
|
||||
|
||||
return Pair.of(true, requestBody);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建拉卡拉接口公共参数
|
||||
* <p>
|
||||
* 公共参数包括:
|
||||
* - reqTime: 时间戳,格式为yyyyMMddHHmmss
|
||||
* - version: 版本号,固定为1.0.0
|
||||
* - reqId: 请求序列号,使用UUID生成唯一标识
|
||||
*
|
||||
* @return 包含公共参数的JSONObject
|
||||
*/
|
||||
public static JSONObject buildParams() {
|
||||
JSONObject params = new JSONObject();
|
||||
|
||||
// 时间戳,格式为yyyyMMddHHmmss
|
||||
String reqTime = DateUtil.format(new Date(), "yyyyMMddHHmmss");
|
||||
params.set("reqTime", reqTime);
|
||||
|
||||
// 版本号,固定为1.0.0
|
||||
params.set("version", "1.0.0");
|
||||
|
||||
// 请求序列号,使用UUID生成唯一标识并去除横线
|
||||
String reqId = IdUtil.simpleUUID();
|
||||
params.set("reqId", reqId);
|
||||
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ import java.util.stream.Collectors;
|
||||
public class PushMessageServiceImpl implements PushMessageService {
|
||||
|
||||
private static final String appName = "小发同城";
|
||||
|
||||
@Lazy
|
||||
@Resource
|
||||
private UniCloudPushService uniCloudPushService;
|
||||
@ -138,7 +139,7 @@ public class PushMessageServiceImpl implements PushMessageService {
|
||||
|
||||
Pair<Boolean, String> result = uniCloudPushService.sendPushMessageBatch(cidList,
|
||||
appName + "邀请您签署入驻合同",
|
||||
"恭喜您的开店入驻申请已审核通过!请尽快登录APP平台签署电子合同,签署链接24小时内有效(逾期需重新提交申请)。如有疑问请联系客服,感谢您的支持!",
|
||||
"恭喜您的开店入驻申请已审核通过!请尽快登录小发商家 APP 平台签署电子合同,签署链接24小时内有效(逾期需重新提交申请)。如有疑问请联系客服,感谢您的支持!",
|
||||
payload);
|
||||
|
||||
if (!result.getFirst()) {
|
||||
@ -167,7 +168,7 @@ public class PushMessageServiceImpl implements PushMessageService {
|
||||
public void noticeMerchantEmployeeOrderAction(Integer storeId, String orderId, String title, String content, JSONObject payload) {
|
||||
try {
|
||||
List<String> cidList = shopStoreEmployeeService.selectEmployeeGeTuiCidByStoreId(storeId, orderId, null);
|
||||
log.debug("[订单推送] cid 列表:{}", cidList);
|
||||
// log.debug("[订单推送] cid 列表:{}", cidList);
|
||||
// 获取 商家的 cid
|
||||
uniCloudPushService.sendPushMessageBatch(cidList, title, content, payload);
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -38,12 +38,11 @@ import java.util.stream.LongStream;
|
||||
@lombok.extern.slf4j.Slf4j
|
||||
public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMapper, ShopNumberSeq> implements ShopNumberSeqService {
|
||||
|
||||
private final String CACHE_PREFIX = "shop_number_seq:%S";
|
||||
@Autowired
|
||||
private ShopNumberSeqMapper shopNumberSeqMapper;
|
||||
@Autowired
|
||||
private RedisService redisService;
|
||||
private String CACHE_PREFIX = "shop_number_seq:%S";
|
||||
|
||||
@Autowired
|
||||
private ShopProductSpecItemService shopProductSpecItemService;
|
||||
|
||||
@ -53,6 +52,10 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
@Autowired
|
||||
private AccountService accountService;
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.printf(IntStream.rangeClosed(1, 1).boxed().collect(Collectors.toList()).toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 得到下一个Id
|
||||
* 方法走到这里会产生串行化,集群部署这里不能使用单机锁
|
||||
@ -65,7 +68,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
public synchronized String createNextSeq(String prefix) {
|
||||
|
||||
String ymd = DateUtil.format(new Date(), "yyyyMMdd");
|
||||
String id = String.format("%s-%s-", prefix, ymd);
|
||||
String id = String.format("%s_%s_", prefix, ymd);
|
||||
ShopNumberSeq shopNumberSeq = this.baseMapper.selectById(id);
|
||||
if (shopNumberSeq == null) {
|
||||
shopNumberSeq = new ShopNumberSeq();
|
||||
@ -76,7 +79,7 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
}
|
||||
}
|
||||
|
||||
String order_id = String.format("%s-%s-%s", prefix, ymd, shopNumberSeq.getNumber());
|
||||
String order_id = String.format("%s_%s_%s", prefix, ymd, shopNumberSeq.getNumber());
|
||||
shopNumberSeq.setPrefix(id);
|
||||
boolean flag = edit(shopNumberSeq);
|
||||
if (flag) {
|
||||
@ -86,7 +89,6 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 得到下一个Id
|
||||
*
|
||||
@ -128,25 +130,26 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
|
||||
/**
|
||||
* 校验同步数据是否同步完成,就是两个库的主键是否一致
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private void checkPrimaryKey(){
|
||||
boolean flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size()>1;
|
||||
while (!flag){
|
||||
private void checkPrimaryKey() {
|
||||
boolean flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size() > 1;
|
||||
while (!flag) {
|
||||
try {
|
||||
Thread.sleep(3600);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("checkPrimaryKey枷锁失败--"+e.getMessage());
|
||||
log.error("checkPrimaryKey枷锁失败--" + e.getMessage());
|
||||
break;
|
||||
}
|
||||
flag=shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size()>1;
|
||||
flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size() > 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量获取id
|
||||
*
|
||||
* @param seqName
|
||||
* @param batchSize
|
||||
* @return
|
||||
@ -156,8 +159,8 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
if (batchSize <= 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
long number= 0;
|
||||
if (null==redisService.get(String.format(CACHE_PREFIX, seqName))) {
|
||||
long number = 0;
|
||||
if (null == redisService.get(String.format(CACHE_PREFIX, seqName))) {
|
||||
// 1. 获取当前序列值
|
||||
QueryWrapper<ShopNumberSeq> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("prefix", seqName);
|
||||
@ -171,9 +174,9 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
return LongStream.range(1, batchSize + 1).boxed().collect(Collectors.toList());
|
||||
}
|
||||
number = seq.getNumber();
|
||||
}else {
|
||||
int numberCache=(Integer) redisService.get(String.format(CACHE_PREFIX, seqName))+1;
|
||||
number= numberCache;
|
||||
} else {
|
||||
int numberCache = (Integer) redisService.get(String.format(CACHE_PREFIX, seqName)) + 1;
|
||||
number = numberCache;
|
||||
|
||||
}
|
||||
|
||||
@ -187,8 +190,8 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
return LongStream.rangeClosed(start, end).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void batchUpdateSeq(List<ShopNumberSeq> shopNumberSeqList){
|
||||
int count=0;
|
||||
public void batchUpdateSeq(List<ShopNumberSeq> shopNumberSeqList) {
|
||||
int count = 0;
|
||||
for (int i = 0; i < shopNumberSeqList.size(); i++) {
|
||||
shopNumberSeqMapper.myUpdateSeq(shopNumberSeqList.get(i));
|
||||
Long number = shopNumberSeqList.get(i).getNumber();
|
||||
@ -196,24 +199,24 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
this.edit(shopNumberSeqList.get(i));
|
||||
count++;
|
||||
}
|
||||
log.info("更新成功{}条数据",count);
|
||||
}
|
||||
log.info("更新成功{}条数据", count);
|
||||
}
|
||||
|
||||
public void clearKey(){
|
||||
redisService.del(String.format(CACHE_PREFIX,"item_id"));
|
||||
redisService.del(String.format(CACHE_PREFIX,"product_id"));
|
||||
public void clearKey() {
|
||||
redisService.del(String.format(CACHE_PREFIX, "item_id"));
|
||||
redisService.del(String.format(CACHE_PREFIX, "product_id"));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void clearKeyStoreItemSepcId(){
|
||||
public void clearKeyStoreItemSepcId() {
|
||||
redisService.del(RedisKey.STOREDATASPECITEMID);
|
||||
}
|
||||
|
||||
public void clearKeyStoreAccountBaseId(){
|
||||
public void clearKeyStoreAccountBaseId() {
|
||||
redisService.del(RedisKey.STOREDATACCOUNTBASEID);
|
||||
}
|
||||
|
||||
public void clearKeyStoreSepcId(){
|
||||
public void clearKeyStoreSepcId() {
|
||||
redisService.del(RedisKey.STOREDATASPECID);
|
||||
}
|
||||
|
||||
@ -221,31 +224,32 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
* 清除缓存,专门给并发使用,防止redis的缓存没有加载
|
||||
*/
|
||||
@Override
|
||||
public void clearRelateGoodsId(){
|
||||
boolean flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size()>1;
|
||||
if(flag){
|
||||
public void clearRelateGoodsId() {
|
||||
boolean flag = shopNumberSeqMapper.findNumberFromShopBaseAndItem(new ShopNumberSeq()).size() > 1;
|
||||
if (flag) {
|
||||
return;
|
||||
}
|
||||
List<ShopNumberSeq> shopNumberSeqList=shopNumberSeqMapper.findProductAndImtemId(new ShopNumberSeq());
|
||||
for (ShopNumberSeq shopNumberSeq:shopNumberSeqList) {
|
||||
if(shopNumberSeq.getPrefix().equals("product_id")){
|
||||
clearCache("product_id",shopNumberSeq.getNumber());
|
||||
List<ShopNumberSeq> shopNumberSeqList = shopNumberSeqMapper.findProductAndImtemId(new ShopNumberSeq());
|
||||
for (ShopNumberSeq shopNumberSeq : shopNumberSeqList) {
|
||||
if (shopNumberSeq.getPrefix().equals("product_id")) {
|
||||
clearCache("product_id", shopNumberSeq.getNumber());
|
||||
}
|
||||
if(shopNumberSeq.getPrefix().equals("item_id")){
|
||||
clearCache("item_id",shopNumberSeq.getNumber());
|
||||
if (shopNumberSeq.getPrefix().equals("item_id")) {
|
||||
clearCache("item_id", shopNumberSeq.getNumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新缓存
|
||||
*/
|
||||
private void clearCache(String seqName,long number){
|
||||
private void clearCache(String seqName, long number) {
|
||||
QueryWrapper<ShopNumberSeq> wrapper = new QueryWrapper<>();
|
||||
wrapper.eq("prefix", seqName);
|
||||
ShopNumberSeq seq = getById(seqName);
|
||||
if (seq != null) {
|
||||
seq = new ShopNumberSeq();
|
||||
seq.setNumber(number-1);
|
||||
seq.setNumber(number - 1);
|
||||
seq.setPrefix(seqName);
|
||||
}
|
||||
edit(seq);
|
||||
@ -253,88 +257,84 @@ public class ShopNumberSeqServiceImpl extends BaseServiceImpl<ShopNumberSeqMappe
|
||||
|
||||
/**
|
||||
* 存的是最大值,取的是范围,如存最大值1,批量是2,则取范围【1+1,1+2】,如果没有则从1开始算即取范围[0+1,0+2]
|
||||
*
|
||||
* @param batchSize
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public synchronized List<Integer> getBatchSpecId(int batchSize) {
|
||||
int start=0;
|
||||
if(null!=redisService.get(RedisKey.STOREDATASPECID)){
|
||||
start=(Integer) redisService.get(RedisKey.STOREDATASPECID);
|
||||
redisService.set(RedisKey.STOREDATASPECID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
int start = 0;
|
||||
if (null != redisService.get(RedisKey.STOREDATASPECID)) {
|
||||
start = (Integer) redisService.get(RedisKey.STOREDATASPECID);
|
||||
redisService.set(RedisKey.STOREDATASPECID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
QueryWrapper<ShopBaseProductSpec> queryWrapper= new QueryWrapper<>();
|
||||
QueryWrapper<ShopBaseProductSpec> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("max(spec_id) as spec_id");
|
||||
ShopBaseProductSpec shopBaseProductSpec=shopBaseProductSpecService.getOne(queryWrapper);
|
||||
if(null!=shopBaseProductSpec){
|
||||
start=shopBaseProductSpec.getSpec_id();
|
||||
redisService.set(RedisKey.STOREDATASPECID,start+batchSize);
|
||||
ShopBaseProductSpec shopBaseProductSpec = shopBaseProductSpecService.getOne(queryWrapper);
|
||||
if (null != shopBaseProductSpec) {
|
||||
start = shopBaseProductSpec.getSpec_id();
|
||||
redisService.set(RedisKey.STOREDATASPECID, start + batchSize);
|
||||
}
|
||||
if(start==0){
|
||||
redisService.set(RedisKey.STOREDATASPECID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
if (start == 0) {
|
||||
redisService.set(RedisKey.STOREDATASPECID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* 存的是最大值,取的是范围,如存最大值1,批量是2,则取范围【1+1,1+2】,如果没有则从1开始算即取范围[0+1,0+2]
|
||||
*
|
||||
* @param batchSize
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public synchronized List<Integer> getBatchSpecItemId(int batchSize) {
|
||||
int start=0;
|
||||
if(null!=redisService.get(RedisKey.STOREDATASPECITEMID)){
|
||||
start=(Integer) redisService.get(RedisKey.STOREDATASPECITEMID);
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
int start = 0;
|
||||
if (null != redisService.get(RedisKey.STOREDATASPECITEMID)) {
|
||||
start = (Integer) redisService.get(RedisKey.STOREDATASPECITEMID);
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
QueryWrapper<ShopProductSpecItem> queryWrapper= new QueryWrapper<>();
|
||||
QueryWrapper<ShopProductSpecItem> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.select("max(spec_item_id) as spec_item_id");
|
||||
ShopProductSpecItem shopProductSpecItem=shopProductSpecItemService.getOne(queryWrapper);
|
||||
if(null!=shopProductSpecItem){
|
||||
start=shopProductSpecItem.getSpec_item_id();
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
|
||||
ShopProductSpecItem shopProductSpecItem = shopProductSpecItemService.getOne(queryWrapper);
|
||||
if (null != shopProductSpecItem) {
|
||||
start = shopProductSpecItem.getSpec_item_id();
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID, start + batchSize);
|
||||
}
|
||||
if(start==0){
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
if (start == 0) {
|
||||
redisService.set(RedisKey.STOREDATASPECITEMID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 存的是最大值,取的是范围,如存最大值1,批量是2,则取范围【1+1,1+2】,如果没有则从1开始算即取范围[0+1,0+2]
|
||||
*
|
||||
* @param batchSize
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public synchronized List<Integer> getBatchUserAccountBaseId(int batchSize) {
|
||||
int start=0;
|
||||
if(null!=redisService.get(RedisKey.STOREDATACCOUNTBASEID)){
|
||||
start=(Integer) redisService.get(RedisKey.STOREDATACCOUNTBASEID);
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
int start = 0;
|
||||
if (null != redisService.get(RedisKey.STOREDATACCOUNTBASEID)) {
|
||||
start = (Integer) redisService.get(RedisKey.STOREDATACCOUNTBASEID);
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
Integer maxId= accountService.getAccountMaxId();
|
||||
if(null!=maxId){
|
||||
start=maxId;
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID,start+batchSize);
|
||||
Integer maxId = accountService.getAccountMaxId();
|
||||
if (null != maxId) {
|
||||
start = maxId;
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID, start + batchSize);
|
||||
}
|
||||
if(start==0){
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID,start+batchSize);
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
if (start == 0) {
|
||||
redisService.set(RedisKey.STOREDATACCOUNTBASEID, start + batchSize);
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
return IntStream.rangeClosed(start+1, start+batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.printf(IntStream.rangeClosed(1, 1).boxed().collect(Collectors.toList()).toString());
|
||||
return IntStream.rangeClosed(start + 1, start + batchSize).boxed().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ public class ShopOrderReturnController extends BaseControllerImpl {
|
||||
* {"order_id":"DD-20250701-1","reason":"商家协商退款","order_return_vo":{"order_id":"DD-20250701-1","return_items":[{"order_item_id":1,"return_item_num":1,"return_refund_amount":"0.01"}]}}
|
||||
* @return CommonResult 处理结果
|
||||
*/
|
||||
@ApiOperation(value = "商家退货退款", notes = "商家退货退款,支持整单或个别商品退货")
|
||||
@ApiOperation(value = "商家退款", notes = "商家退款,支持整单或个别商品退货")
|
||||
@RequestMapping(value = "/mch/order/doRefund", method = RequestMethod.POST)
|
||||
public CommonResult doRefundForMch(@RequestBody JSONObject params) {
|
||||
return shopOrderReturnService.doRefundForMch(params);
|
||||
|
||||
@ -116,7 +116,8 @@ public class DelayMessageReceiver {
|
||||
// 根据消息分类处理不同类型的消息
|
||||
if (MqConstant.DEAD_EVENT_CATE_ORDER_EXPIRED.equals(category)) {
|
||||
log.info("处理订单超时消息: {}", message);
|
||||
return handleOrderExpiredMessage(message);
|
||||
// return handleOrderExpiredMessage(message);
|
||||
return true;
|
||||
} else if (MqConstant.DEAD_EVENT_CATE_PRE_ORDER.equals(category)) {
|
||||
log.info("处理预订单消息: {}", message);
|
||||
return handlePreOrderMessage(message);
|
||||
|
||||
@ -7,9 +7,10 @@ import com.rabbitmq.client.Channel;
|
||||
import com.suisung.mall.common.api.StateCode;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.constant.MqConstant;
|
||||
import com.suisung.mall.common.constant.RedisConstant;
|
||||
import com.suisung.mall.common.modules.order.ShopOrderInfo;
|
||||
import com.suisung.mall.common.utils.DateTimeUtils;
|
||||
import com.suisung.mall.shop.message.service.MqMessageService;
|
||||
import com.suisung.mall.core.web.service.RedisService;
|
||||
import com.suisung.mall.shop.message.service.PushMessageService;
|
||||
import com.suisung.mall.shop.order.service.ShopOrderBaseService;
|
||||
import com.suisung.mall.shop.order.service.ShopOrderInfoService;
|
||||
@ -41,6 +42,9 @@ public class OrderPayedListener {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(OrderPayedListener.class);
|
||||
|
||||
// 最大重试次数
|
||||
private static final int MAX_RETRY_COUNT = 5;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private ShopOrderBaseService shopOrderBaseService;
|
||||
@ -56,7 +60,7 @@ public class OrderPayedListener {
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private MqMessageService mqMessageService;
|
||||
private RedisService redisService;
|
||||
|
||||
@RabbitHandler
|
||||
public void listener(byte[] data, Channel channel, Message message) throws IOException, InterruptedException {
|
||||
@ -69,53 +73,72 @@ public class OrderPayedListener {
|
||||
public void listener(String data, Channel channel, Message message) throws IOException, InterruptedException {
|
||||
// String messageId = message.getMessageProperties().getMessageId();
|
||||
if (StrUtil.isBlank(data)) {
|
||||
logger.info("收到空订单消息");
|
||||
logger.info("[订单支付监听] 收到空订单消息");
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> order_id_row = Convert.toList(String.class, data);
|
||||
try {
|
||||
boolean flag = false;
|
||||
logger.info("收到微信异步通知消息data:{}-chanel:{}-message:{},订单ID:{}", data, channel, message, order_id_row);
|
||||
long deliveryTag = message.getMessageProperties().getDeliveryTag();
|
||||
boolean isRedelivered = message.getMessageProperties().isRedelivered();
|
||||
|
||||
String retryCountKey = RedisConstant.Order_Pay_Retry_Count_Key + order_id_row.toString();
|
||||
|
||||
try {
|
||||
logger.info("[订单支付监听] 收到微信异步通知消息. 数据: {}, 订单ID: {}, 是否重投: {}", data, order_id_row, isRedelivered);
|
||||
|
||||
// 检查重试次数
|
||||
int retryCount = getRetryCount(retryCountKey);
|
||||
|
||||
if (retryCount >= MAX_RETRY_COUNT) {
|
||||
logger.error("[订单支付监听] 订单处理已达到最大重试次数: {}, 订单ID: {}, 不再处理该消息", MAX_RETRY_COUNT, order_id_row);
|
||||
channel.basicAck(deliveryTag, false); // 确认消息,避免无限重试
|
||||
redisService.del(retryCountKey); // 清除重试计数
|
||||
return;
|
||||
}
|
||||
|
||||
boolean flag = false;
|
||||
for (String orderId : order_id_row) {
|
||||
//判断是否为线下支付订单
|
||||
ShopOrderInfo orderInfoOld = shopOrderInfoService.get(orderId);
|
||||
if (orderInfoOld == null) {
|
||||
// 记录重试次数
|
||||
incrementRetryCount(retryCountKey);
|
||||
logger.warn("[订单支付监听] 订单在数据库中不存在,增加重试计数. 订单ID: {}, 当前重试次数: {}", orderId, retryCount + 1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 订单状态
|
||||
int order_state_id = orderInfoOld.getOrder_state_id().intValue();
|
||||
logger.info("#### 当前订单状态: {} ####", order_state_id);
|
||||
logger.info("[订单支付监听] 当前订单状态: {}, 订单ID: {}", order_state_id, orderId);
|
||||
|
||||
// 检查订单是否已经处理过(幂等性检查)
|
||||
// if (isOrderPaid(orderInfoOld)) {
|
||||
// logger.info("订单已支付,无需重复处理,订单ID: {}", orderId);
|
||||
// flag = true;
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (isOrderPaid(orderInfoOld)) {
|
||||
logger.info("[订单支付监听] 订单已支付,无需重复处理,订单ID: {}", orderId);
|
||||
flag = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (order_state_id == StateCode.ORDER_STATE_WAIT_PAY) {
|
||||
// 待支付状态
|
||||
logger.info("#### 待支付业务分支 ####");
|
||||
logger.info("[订单支付监听] 处理待支付订单. 订单ID: {}", orderId);
|
||||
flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId));
|
||||
} else {
|
||||
//判断是否线下支付
|
||||
if (StateCode.PAYMENT_TYPE_OFFLINE == orderInfoOld.getPayment_type_id().intValue()) {
|
||||
//线下支付,直接处理订单支付状态, 不处理订单状态
|
||||
logger.info("#### 线下业务分支 ####");
|
||||
logger.info("[订单支付监听] 处理线下支付订单. 订单ID: {}", orderId);
|
||||
ShopOrderInfo orderInfo = new ShopOrderInfo();
|
||||
orderInfo.setOrder_id(orderId);
|
||||
orderInfo.setOrder_is_paid(StateCode.ORDER_PAID_STATE_YES);
|
||||
flag = shopOrderInfoService.edit(orderInfo);
|
||||
} else {
|
||||
logger.info("#### 非线下业务分支 ####");
|
||||
logger.info("[订单支付监听] 处理其他支付订单. 订单ID: {}", orderId);
|
||||
flag = shopOrderBaseService.setPaidYes(Collections.singletonList(orderId));
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("#### 支付异步通知回调处理是否成功:{} ####", flag);
|
||||
logger.info("[订单支付监听] 支付异步通知回调处理结果: {}, 订单ID: {}", flag, orderId);
|
||||
// 生成取单号和打印小票
|
||||
if (flag) {
|
||||
// TODO 以下仅处理下单打印的情况,还需要处理退单打印分支。
|
||||
@ -134,16 +157,16 @@ public class OrderPayedListener {
|
||||
// 发送顺丰同城快递
|
||||
Pair<Boolean, String> pairCreateSfOrder = sfExpressApiService.innerCreateSfExpressOrder(orderId, orderPickupNum);
|
||||
if (pairCreateSfOrder == null) {
|
||||
logger.error("顺丰同城下单失败!pairCreateSfOrder 返回空值");
|
||||
return;
|
||||
logger.error("[订单支付监听] 顺丰同城下单失败!pairCreateSfOrder 返回空值. 订单ID: {}", orderId);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pairCreateSfOrder.getFirst()) {
|
||||
logger.error("顺丰同城下单失败:{}", pairCreateSfOrder.getSecond());
|
||||
return;
|
||||
logger.error("[订单支付监听] 顺丰同城下单失败: {}, 订单ID: {}", pairCreateSfOrder.getSecond(), orderId);
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.info("顺丰同城下单成功");
|
||||
logger.info("[订单支付监听] 顺丰同城下单成功. 订单ID: {}", orderId);
|
||||
|
||||
}
|
||||
|
||||
@ -158,18 +181,16 @@ public class OrderPayedListener {
|
||||
payload.put("orderId", orderId);
|
||||
pushMessageService.noticeMerchantEmployeeOrderAction(orderInfoOld.getStore_id(), orderId, title, content, payload);
|
||||
|
||||
// 发送 预过期 MQ 的推送消息
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
jsonObject.put("category", MqConstant.DEAD_EVENT_CATE_ORDER_EXPIRED); // 消息分类:1-订单超时消息
|
||||
jsonObject.put("orderId", orderId); // 订单ID
|
||||
jsonObject.put("storeId", orderInfoOld.getStore_id()); // 店铺ID
|
||||
jsonObject.put("title", "您有一笔将超时的订单。"); // 消息标题
|
||||
jsonObject.put("message", String.format("您有一笔将超时的订单:%s,请您及时处理。", orderId)); // 消息内容
|
||||
// 发送延迟消息(提前10分钟)
|
||||
Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 600;
|
||||
mqMessageService.sendDelayMessage(jsonObject.toString(), mchOrderExpireSeconds * 1000); // 转换为毫秒
|
||||
// 发送延迟消息 25分钟拣货配送时间(提前5分钟,下单20分钟后,提醒商家及时拣货)
|
||||
Long mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L) - 300;
|
||||
redisService.set(RedisConstant.SF_Order_Proc_WillExpire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds);
|
||||
|
||||
// 发送延迟消息 25分钟拣货配送时间(下单25分钟后,提醒商家及时拣货)
|
||||
mchOrderExpireSeconds = shopOrderBaseService.sameCityOrderExpireSeconds(1500L);
|
||||
redisService.set(RedisConstant.SF_Order_Proc_Expire_Key + String.format("%d&%s", orderInfoOld.getStore_id(), orderId), orderId, mchOrderExpireSeconds);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("发送推送消息失败:{}", e.getMessage());
|
||||
log.error("[订单支付监听] 发送推送消息失败. 订单ID: {}", orderId, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -177,15 +198,22 @@ public class OrderPayedListener {
|
||||
|
||||
if (flag) {
|
||||
// 操作成功,标记消息消费成功
|
||||
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
|
||||
logger.info("[订单支付监听] 消息处理成功,确认消息. 订单ID: {}", order_id_row);
|
||||
channel.basicAck(deliveryTag, false);
|
||||
// 清除重试计数
|
||||
redisService.del(RedisConstant.Order_Pay_Retry_Count_Key + order_id_row);
|
||||
} else {
|
||||
log.error("消息消费失败,执行setPaidYes异常,当前订单编号:{}", order_id_row);
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
log.error("[订单支付监听] 消息处理失败,执行setPaidYes异常,当前订单编号:{}", order_id_row);
|
||||
// 增加重试计数
|
||||
incrementRetryCount(retryCountKey);
|
||||
channel.basicReject(deliveryTag, true);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("消息消费失败,执行setPaidYes异常,当前订单编号:{},失败原因:", order_id_row, e);
|
||||
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
|
||||
log.error("[订单支付监听] 消息处理异常,当前订单编号:{},异常信息:", order_id_row, e);
|
||||
// 增加重试计数
|
||||
incrementRetryCount(retryCountKey);
|
||||
channel.basicReject(deliveryTag, true);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
@ -197,12 +225,33 @@ public class OrderPayedListener {
|
||||
* @return 是否已支付
|
||||
*/
|
||||
private boolean isOrderPaid(ShopOrderInfo orderInfo) {
|
||||
// public static final int ORDER_PAID_STATE_NO = 3010; //未付款
|
||||
// public static final int ORDER_PAID_STATE_FINANCE_REVIEW = 3011; //待付款审核
|
||||
// public static final int ORDER_PAID_STATE_PART = 3012; //部分付款
|
||||
// public static final int ORDER_PAID_STATE_YES = 3013; //已付款
|
||||
// ORDER_PAID_STATE_NO = 3010; //未付款
|
||||
// ORDER_PAID_STATE_FINANCE_REVIEW = 3011; //待付款审核
|
||||
// ORDER_PAID_STATE_PART = 3012; //部分付款
|
||||
// ORDER_PAID_STATE_YES = 3013; //已付款
|
||||
return orderInfo.getOrder_is_paid() != null &&
|
||||
orderInfo.getOrder_is_paid().equals(StateCode.ORDER_PAID_STATE_YES);
|
||||
orderInfo.getOrder_is_paid().intValue() == StateCode.ORDER_PAID_STATE_YES;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取重试次数
|
||||
*
|
||||
* @param retryCountKey 重试计数键
|
||||
* @return 当前重试次数
|
||||
*/
|
||||
private int getRetryCount(String retryCountKey) {
|
||||
Object countObj = redisService.get(retryCountKey);
|
||||
return countObj == null ? 0 : Convert.toInt(countObj, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加重试次数
|
||||
*
|
||||
* @param retryCountKey 重试计数键
|
||||
*/
|
||||
private void incrementRetryCount(String retryCountKey) {
|
||||
int currentCount = getRetryCount(retryCountKey);
|
||||
redisService.set(retryCountKey, currentCount + 1, 3600); // 1小时过期
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||
*/
|
||||
|
||||
package com.suisung.mall.shop.order.listener;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.suisung.mall.common.constant.CommonConstant;
|
||||
import com.suisung.mall.common.constant.RedisConstant;
|
||||
import com.suisung.mall.shop.message.service.PushMessageService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.redis.connection.Message;
|
||||
import org.springframework.data.redis.connection.MessageListener;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Slf4j
|
||||
public class RedisKeyExpiredListener implements MessageListener {
|
||||
|
||||
@Resource
|
||||
private PushMessageService pushMessageService;
|
||||
|
||||
/**
|
||||
* Callback for processing received objects through Redis.
|
||||
*
|
||||
* @param message message must not be {@literal null}.
|
||||
* @param pattern pattern matching the channel (if specified) - can be {@literal null}.
|
||||
*/
|
||||
@Override
|
||||
public void onMessage(Message message, byte[] pattern) {
|
||||
String expiredKey = new String(message.getBody());
|
||||
String patternStr = pattern != null ? new String(pattern) : "无模式";
|
||||
|
||||
// 基本日志记录
|
||||
log.debug("[Redis过期监听] 接收到Redis键过期事件. 过期键: {}, 订阅模式: {}", expiredKey, patternStr);
|
||||
|
||||
// 参数验证
|
||||
if (StrUtil.isBlank(expiredKey)) {
|
||||
log.warn("[Redis过期监听] 接收到空的过期键. 订阅模式: {}", patternStr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (expiredKey.startsWith(RedisConstant.SF_Order_Proc_Expire_Key)) {
|
||||
log.debug("[Redis过期监听] 开始处理订单超时事件. 过期键: {}", expiredKey);
|
||||
|
||||
// storeId&orderId
|
||||
String[] args = expiredKey.replace(RedisConstant.SF_Order_Proc_Expire_Key, "").split("&");
|
||||
if (ArrayUtil.isEmpty(args) || args.length < 2) {
|
||||
log.error("[Redis过期监听] 订单处理超时键格式错误. 键: {} 不符合预期格式", expiredKey);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[Redis过期监听] 处理订单超时消息. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
boolean result = handleOrderExpiredMessage(args[0], args[1], false);
|
||||
|
||||
if (result) {
|
||||
log.info("[Redis过期监听] 订单超时事件处理成功. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
} else {
|
||||
log.error("[Redis过期监听] 订单超时事件处理失败. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
}
|
||||
} else if (expiredKey.startsWith(RedisConstant.SF_Order_Proc_WillExpire_Key)) {
|
||||
log.debug("[Redis过期监听] 开始处理订单即将(5分钟后)超时事件. 过期键: {}", expiredKey);
|
||||
|
||||
// storeId&orderId
|
||||
String[] args = expiredKey.replace(RedisConstant.SF_Order_Proc_WillExpire_Key, "").split("&");
|
||||
if (ArrayUtil.isEmpty(args) || args.length < 2) {
|
||||
log.error("[Redis过期监听] 订单处理超时键格式错误. 键: {} 不符合预期格式", expiredKey);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("[Redis过期监听] 处理订单即将超时消息. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
boolean result = handleOrderExpiredMessage(args[0], args[1], true);
|
||||
|
||||
if (result) {
|
||||
log.info("[Redis过期监听] 订单即将超时事件处理成功. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
} else {
|
||||
log.error("[Redis过期监听] 订单即将超时事件处理失败. 店铺ID: {}, 订单号: {}", args[0], args[1]);
|
||||
}
|
||||
} else {
|
||||
//log.debug("[Redis过期监听] 忽略非订单超时事件. 过期键: {}", expiredKey);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理订单超时消息
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param orderId 订单ID
|
||||
* @param willExpire 此刻还是即将
|
||||
* @return 处理结果
|
||||
*/
|
||||
private boolean handleOrderExpiredMessage(String storeId, String orderId, boolean willExpire) {
|
||||
try {
|
||||
String addWord = "";
|
||||
if (willExpire) {
|
||||
addWord = "即将";
|
||||
}
|
||||
|
||||
log.debug("[订单{}超时处理] 开始处理订单超时消息. 店铺ID: {}, 订单号: {}", addWord, storeId, orderId);
|
||||
|
||||
// 参数验证
|
||||
if (StrUtil.isBlank(orderId) && StrUtil.isBlank(storeId)) {
|
||||
log.warn("[订单{}超时处理] 订单ID和店铺ID不能同时为空. 店铺ID: {}, 订单号: {}", addWord, storeId, orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(orderId)) {
|
||||
log.warn("[订单{}超时处理] 订单号为空. 店铺ID: {}", addWord, storeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StrUtil.isBlank(storeId)) {
|
||||
log.warn("[订单{}超时处理] 店铺ID为空. 订单号: {}", addWord, orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
String title = "有一笔" + addWord + "超时的订单!";
|
||||
String content = "您有一笔" + addWord + "超时的订单[" + orderId + "],请及时处理。";
|
||||
log.debug("[订单{}超时处理] 准备推送消息. 标题: {}, 内容: {}", addWord, title, content);
|
||||
|
||||
// 构造推送消息内容
|
||||
JSONObject payload = new JSONObject();
|
||||
if (willExpire) {
|
||||
payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ONLINE_ORDER_LIST);
|
||||
} else {
|
||||
payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ABNORMAL_ORDER_LIST);
|
||||
}
|
||||
payload.put("orderId", orderId);
|
||||
log.debug("[订单{}超时处理] 推送消息载荷已准备: {}", addWord, payload);
|
||||
|
||||
// 发送推送消息给商家员工
|
||||
log.debug("[订单{}超时处理] 发送推送消息给商家员工. 店铺ID: {}, 订单号: {}", addWord, storeId, orderId);
|
||||
pushMessageService.noticeMerchantEmployeeOrderAction(
|
||||
Convert.toInt(storeId), orderId, title,
|
||||
content, payload);
|
||||
|
||||
log.info("[订单{}超时处理] 订单超时消息处理完成. 订单号: {}, 店铺ID: {}, 推送类别: {}",
|
||||
addWord, orderId, storeId, CommonConstant.PUSH_MSG_CATE_MCH_ABNORMAL_ORDER_LIST);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("[订单超时处理] 处理订单超时消息时发生异常. 订单号: {}, 店铺ID: {}", orderId, storeId, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,10 +36,20 @@ public interface ShopOrderLklService extends IBaseService<ShopOrderLkl> {
|
||||
* 根据拉卡拉的回调数据,更新拉卡拉的订单信息
|
||||
*
|
||||
* @param lklPayNotifyDataJson 异步通知返回的body json数据:
|
||||
* // {"out_trade_no":"202203151637334864280014","trade_no":"2022031566210203291925","log_no":"66210203291925",
|
||||
* // 非合单返回的数据异步通知返回的 body json数据:{"out_trade_no":"202203151637334864280014","trade_no":"2022031566210203291925","log_no":"66210203291925",
|
||||
* // "acc_trade_no":"2022031522001483661454130929 ","trade_status":"SUCCESS","trade_state":"SUCCESS","total_amount":"1",
|
||||
* // "payer_amount":"1","acc_settle_amount":"1","trade_time":"20220315163808","user_id1":"app***@163.com",
|
||||
* // "user_id2":"2088432881453660","notify_url":"https://www.baidu.com","account_type":"ALIPAY","card_type":"99"}
|
||||
* <p>
|
||||
* // 合单返回的数据异步通知返回的 body json数据:{"out_trade_no":"DD-20250830-10","trade_no":"20250830110113130266250034160499","log_no":"66250034160499",
|
||||
* // "acc_trade_no":"4200002826202508306761393882","trade_status":"SUCCESS","trade_state":"SUCCESS","total_amount":"2","payer_amount":"2","acc_settle_amount":"2",
|
||||
* // "acc_mdiscount_amount":"0","acc_discount_amount":"0","trade_time":"20250830180435","user_id1":"oDVKR7T0qxg6O8tqIL9SgY6LXqqQ",
|
||||
* // "user_id2":"oVxsc1QRAqDRv_gAmXuLZwSVSL18","notify_url":"https://mall.gpxscs.cn/mobile/pay/index/lkl_wxPay_notify_url","account_type":"WECHAT",
|
||||
* // "bank_type":"OTHERS","card_type":"02","merchant_no":"8226330541100GU","remark":"","sub_mch_id":"803819329",
|
||||
* // "out_split_rsp_infos":[{"sub_trade_no":"20250830110113130266250034112794","sub_log_no":"66250034112794","out_sub_trade_no":"ORD_DD-20250830-10",
|
||||
* // "merchant_no":"8226330541100GU","term_no":"N5817779","amount":"1","settle_type":"0"},{"sub_trade_no":"20250830110113130266250034160498","sub_log_no":"66250034160498",
|
||||
* // "out_sub_trade_no":"DF_DD-20250830-10","merchant_no":"822584059990FYP","term_no":"N5811590","amount":"1","settle_type":"0"}],"trade_req_date":"20250830","gb_amount":"",
|
||||
* // "qb_amount":""}
|
||||
* @return
|
||||
*/
|
||||
Boolean addOrUpdateByLklPayNotifyDataJson(JSONObject lklPayNotifyDataJson);
|
||||
@ -53,4 +63,54 @@ public interface ShopOrderLklService extends IBaseService<ShopOrderLkl> {
|
||||
* @return
|
||||
*/
|
||||
List<ShopOrderLkl> selectByOrderId(String orderId, String lklLogNo, String storeId);
|
||||
|
||||
|
||||
/**
|
||||
* 根据店铺Id、订单编号查询一条记录
|
||||
*
|
||||
* @param storeId
|
||||
* @param orderId
|
||||
* @return
|
||||
*/
|
||||
ShopOrderLkl getByStoreIdAndOrderId(Integer storeId, String orderId);
|
||||
|
||||
/**
|
||||
* 根据商户号、交易流水号、对账单流水号查询一条记录
|
||||
*
|
||||
* @param lklMerchantNo
|
||||
* @param lklTradeNo
|
||||
* @param lklSubLogNo
|
||||
* @return
|
||||
*/
|
||||
ShopOrderLkl getByLklMchNoAndSubTradeNoAndSubLogNo(String lklMerchantNo, String lklTradeNo, String lklSubLogNo);
|
||||
|
||||
|
||||
/**
|
||||
* 根据商户号、确认收货交易流水号、确认收货对账单流水号查询一条记录
|
||||
*
|
||||
* @param lklMerchantNo 拉卡拉商户号
|
||||
* @param lklReceiveTradeNo 拉卡拉确认收货交易流水号
|
||||
* @param lklReceiveLogNo 拉卡拉确认收货对账单流水号
|
||||
* @return ShopOrderLkl 拉卡拉订单记录
|
||||
*/
|
||||
ShopOrderLkl getByLklMchNoAndReceiveTradeNoAndReceiveLogNo(String lklMerchantNo, String lklReceiveTradeNo, String lklReceiveLogNo);
|
||||
|
||||
/**
|
||||
* 根据商户号、确认收货交易流水号、确认收货对账单流水号更新确认收货状态
|
||||
*
|
||||
* @param lklReceiveLogNo 拉卡拉确认收货对账单流水号
|
||||
* @param separateStatus 分账状态:1-已分账;2-未分账;3-分账已失败;
|
||||
* @param separateRemark 分账问题备注;
|
||||
* @return
|
||||
*/
|
||||
Boolean updateSeparateStatusByReceiveLogNo(String lklReceiveLogNo, Integer separateStatus, String separateRemark);
|
||||
|
||||
|
||||
/**
|
||||
* 不抛异常更新
|
||||
*
|
||||
* @param record
|
||||
* @return
|
||||
*/
|
||||
Boolean safeUpdate(ShopOrderLkl record);
|
||||
}
|
||||
|
||||
@ -175,6 +175,17 @@ public interface ShopOrderReturnService extends IBaseService<ShopOrderReturn> {
|
||||
*/
|
||||
CommonResult addWholeItems(String orderId, Boolean isSystemOpt, String remark);
|
||||
|
||||
/**
|
||||
* 对已存在部分退款的订单,进行剩余商品的全部退款
|
||||
* 该方法用于处理订单中部分商品已经申请退款后,对剩余商品进行整单退款的场景
|
||||
*
|
||||
* @param orderId 订单ID
|
||||
* @param isSystemOpt 是否系统自动操作
|
||||
* @param remark 退单备注
|
||||
* @return CommonResult 退款申请结果
|
||||
*/
|
||||
CommonResult addRemainingItems(String orderId, Boolean isSystemOpt, String remark);
|
||||
|
||||
/**
|
||||
* 退货单转单-供应商
|
||||
*
|
||||
|
||||
@ -331,6 +331,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
@Autowired
|
||||
private ShopDistributionPlantformUserService shopDistributionPlantformUserService;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private ShopOrderLklService shopOrderLklService;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private MessageService messageService;
|
||||
@ -585,7 +589,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(order_key)) {
|
||||
if (order_key.startsWith("DD-")) {
|
||||
if (order_key.startsWith("DD_") || order_key.startsWith("DD-")) {
|
||||
params.put("order_id:eq", order_key);
|
||||
} else {
|
||||
params.put("order_title:like", order_key);
|
||||
@ -628,43 +632,61 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单详情
|
||||
* 获取订单详情
|
||||
*
|
||||
* @return
|
||||
* @param order_id 订单ID
|
||||
* @return 订单详情数据
|
||||
*/
|
||||
@Override
|
||||
public Map detail(String order_id) {
|
||||
log.debug("开始获取订单详情,订单ID: {}", order_id);
|
||||
|
||||
Map data = new HashMap();
|
||||
List<String> order_ids = Convert.toList(String.class, order_id);
|
||||
if (CollUtil.isEmpty(order_ids)) {
|
||||
log.warn("订单ID列表为空");
|
||||
return data;
|
||||
}
|
||||
|
||||
// 获取订单信息
|
||||
Map row = Convert.toMap(String.class, Object.class, get(order_ids.get(0)));
|
||||
if (CollUtil.isEmpty(row)) {
|
||||
log.warn("订单信息不存在,订单ID: {}", order_ids.get(0));
|
||||
throw new ApiException(I18nUtil._("订单不存在!"));
|
||||
}
|
||||
|
||||
//买家订单或者 // todo 分销订单
|
||||
UserDto user = getCurrentUser();
|
||||
if (user == null) {
|
||||
log.warn("用户信息异常");
|
||||
throw new ApiUserException(I18nUtil._("用户信息异常!"));
|
||||
}
|
||||
Integer user_id = user.getId();
|
||||
log.debug("当前用户ID: {}", user_id);
|
||||
|
||||
QueryWrapper<ShopDistributionUserOrder> userOrderQueryWrapper = new QueryWrapper<>();
|
||||
userOrderQueryWrapper.eq("user_id", user_id).in("order_id", order_ids);
|
||||
ShopDistributionUserOrder distributionUserOrder = shopDistributionUserOrderService.findOne(userOrderQueryWrapper);
|
||||
Map distributionUserOrderMap = Convert.toMap(String.class, Object.class, distributionUserOrder);
|
||||
|
||||
if (CheckUtil.checkDataRights(user_id, row, "buyer_user_id") || CheckUtil.checkDataRights(user_id, distributionUserOrderMap, "user_id")) {
|
||||
data = getOrderDetail(order_id, row);
|
||||
// 检查用户是否有权限访问该订单(作为买家或分销商)
|
||||
boolean hasBuyerRights = CheckUtil.checkDataRights(user_id, row, "buyer_user_id");
|
||||
boolean hasDistributionRights = CheckUtil.checkDataRights(user_id, distributionUserOrderMap, "user_id");
|
||||
log.debug("订单访问权限检查,买家权限: {}, 分销权限: {}", hasBuyerRights, hasDistributionRights);
|
||||
|
||||
//diy, 判断是否上传素材
|
||||
if (hasBuyerRights || hasDistributionRights) {
|
||||
data = getOrderDetail(order_id, row);
|
||||
log.debug("获取订单详细信息完成");
|
||||
|
||||
// diy, 判断是否上传素材
|
||||
List<Map> items = (List<Map>) data.get("items");
|
||||
for (Map item : items) {
|
||||
String design_file_images = Convert.toStr(item.get("design_file_images"));
|
||||
if (StrUtil.isNotBlank(design_file_images)) {
|
||||
// row = apply_filters("product-cart-add", row);
|
||||
if (CollUtil.isNotEmpty(items)) {
|
||||
for (Map item : items) {
|
||||
String design_file_images = Convert.toStr(item.get("design_file_images"));
|
||||
if (StrUtil.isNotBlank(design_file_images)) {
|
||||
// row = apply_filters("product-cart-add", row);
|
||||
log.debug("订单项包含设计文件图片");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -675,15 +697,19 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
Integer delivery_istimer = Convert.toInt(data.get("delivery_istimer"));
|
||||
String deliveryTime = getDeliveryTime(delivery_time, delivery_time_rang, delivery_time_h, delivery_time_i, delivery_istimer);
|
||||
data.put("delivery_time_name", deliveryTime);
|
||||
log.debug("配送时间计算完成: {}", deliveryTime);
|
||||
|
||||
// 判断活动信息
|
||||
Integer activity_type_id = Convert.toInt(data.get("activity_type_id"));
|
||||
if (ObjectUtil.equal(StateCode.ACTIVITY_TYPE_GROUPBOOKING, activity_type_id)) {
|
||||
log.debug("处理拼团活动订单");
|
||||
QueryWrapper<ShopActivityGroupbookingHistory> historyQueryWrapper = new QueryWrapper<>();
|
||||
historyQueryWrapper.eq("user_id", user_id).in("order_id", order_ids);
|
||||
ShopActivityGroupbookingHistory group_booking_row = groupbookingHistoryService.findOne(historyQueryWrapper);
|
||||
|
||||
data.put("gb_id", group_booking_row.getGb_id());
|
||||
if (group_booking_row != null) {
|
||||
data.put("gb_id", group_booking_row.getGb_id());
|
||||
log.debug("获取拼团ID: {}", group_booking_row.getGb_id());
|
||||
}
|
||||
}
|
||||
|
||||
Map queryParams = new HashMap();
|
||||
@ -693,6 +719,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
|
||||
Map result = payService.findConsumeRecordHandleName(queryParams);
|
||||
data.putAll(result);
|
||||
log.debug("支付记录处理完成");
|
||||
|
||||
// 订单倒计时
|
||||
boolean show_cancel_time = accountBaseConfigService.getConfig("show_cancel_time", false);
|
||||
@ -709,6 +736,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
order_time = DateUtil.offsetSecond(order_time, (int) (order_autocancel_time * 60 * 60));
|
||||
long remain_pay_time = (order_time.getTime() - new DateTime().getTime()) / 1000;
|
||||
data.put("remain_pay_time", remain_pay_time); // 修复以分钟扫描清除订单导致的误差
|
||||
log.debug("订单自动取消倒计时计算完成,剩余时间: {}秒", remain_pay_time);
|
||||
}
|
||||
|
||||
//自动收货倒计时 order_autofinish_time
|
||||
@ -719,10 +747,15 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
long remain_autofinish_time = (order_deal_time.getTime() - new DateTime().getTime()) / 1000;
|
||||
|
||||
data.put("remain_autofinish_time", remain_autofinish_time); // 修复以分钟扫描清除订单导致的误差
|
||||
log.debug("订单自动收货倒计时计算完成,剩余时间: {}秒", remain_autofinish_time);
|
||||
}
|
||||
|
||||
} else {
|
||||
log.warn("用户无权限访问该订单,用户ID: {}, 订单ID: {}", user_id, order_id);
|
||||
throw new ApiException(I18nUtil._("无该订单访问权限!"));
|
||||
}
|
||||
|
||||
log.info("订单详情获取完成,订单ID: {}", order_id);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -755,6 +788,14 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
if (CollUtil.isNotEmpty(order_info)) base_row.putAll(order_info);
|
||||
if (CollUtil.isNotEmpty(order_data)) base_row.putAll(order_data);
|
||||
|
||||
// update 2025-09-10 增加KLK支付信息
|
||||
ShopOrderLkl shopOrderLkl = shopOrderLklService.getByStoreIdAndOrderId(shopOrderInfo.getStore_id(), order_id);
|
||||
if (shopOrderLkl != null) {
|
||||
base_row.put("lkl_merchant_no", shopOrderLkl.getLkl_merchant_no());
|
||||
base_row.put("lkl_trade_no", shopOrderLkl.getWx_transaction_id());
|
||||
base_row.put("lkl_log_no", shopOrderLkl.getLkl_sub_log_no());
|
||||
}
|
||||
|
||||
// 是否为虚拟商品
|
||||
ShopOrderChainCode shopOrderChainCode = orderChainCodeService.get(order_id);
|
||||
Map order_chain_code = Convert.toMap(String.class, Object.class, shopOrderChainCode);
|
||||
@ -2334,10 +2375,10 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
queryWrapper.eq("order_state_id", order_state_id).in("order_is_shipped", Arrays.asList(StateCode.ORDER_SHIPPED_STATE_NO, StateCode.ORDER_SHIPPED_STATE_PART)).eq("order_is_received", 0);
|
||||
break;
|
||||
case StateCode.ORDER_STATE_SHIPPED: // 待收获确认
|
||||
shopOrderInfo.setOrder_is_received(1);
|
||||
shopOrderInfo.setOrder_is_received(CommonConstant.Enable);
|
||||
shopOrderInfo.setOrder_settlement_time(time);
|
||||
shopOrderInfo.setOrder_qs_time(time);
|
||||
queryWrapper.eq("order_state_id", order_state_id).eq("order_is_received", 0);
|
||||
queryWrapper.eq("order_state_id", order_state_id).eq("order_is_received", CommonConstant.Disable);
|
||||
break;
|
||||
default:
|
||||
shopOrderInfo.setOrder_settlement_time(time);
|
||||
@ -3246,6 +3287,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
|
||||
Integer order_item_inventory_lock = order_item_row.getOrder_item_inventory_lock();
|
||||
String item_src_id = order_item_row.getItem_src_id();
|
||||
|
||||
logger.debug("尝试执行订单商品item_src_id:{} 锁定库存:{}", item_src_id, order_item_inventory_lock);
|
||||
if (ObjectUtil.equal(1002, order_item_inventory_lock) && CheckUtil.isNotEmpty(item_src_id)) {
|
||||
Long item_id = order_item_row.getItem_id();
|
||||
if (shopProductItemService.lockSkuStock(item_id, order_item_row.getOrder_item_quantity()) <= 0) {
|
||||
@ -3253,9 +3296,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
throw new ApiException(String.format(I18nUtil._("更改: %s 冻结库存失败!"), item_id));
|
||||
}
|
||||
|
||||
logger.debug("成功执行订单商品item_src_id:{} 锁定库存:{}", item_src_id, order_item_inventory_lock);
|
||||
|
||||
// RMK 第三方数据同步相关:redis 给这个商品减去对应的库存
|
||||
Map<String, Integer> stockDeltaMap = new HashMap<>();
|
||||
stockDeltaMap.put(Convert.toStr(item_src_id), -order_item_quantity);
|
||||
stockDeltaMap.put(item_src_id, -order_item_quantity);
|
||||
syncThirdDataService.incrProductStockToRedis(stockDeltaMap);
|
||||
}
|
||||
}
|
||||
@ -4805,7 +4850,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
|
||||
// 权限判断
|
||||
// 用户权限判断
|
||||
UserDto user = getCurrentUser();
|
||||
if (user == null) {
|
||||
throw new ApiUserException(I18nUtil._("用户信息异常!"));
|
||||
@ -4902,87 +4947,145 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单确认收货
|
||||
* 确认收货处理
|
||||
*
|
||||
* @param order_ids 订单id
|
||||
* @param order_rows 订单数据
|
||||
* @param order_ids 订单ID列表
|
||||
* @param order_rows 订单数据列表
|
||||
* @return 是否处理成功
|
||||
*/
|
||||
@Override
|
||||
public boolean receive(List<String> order_ids, List<ShopOrderBase> order_rows) {
|
||||
// 检测数据是否合法,过滤允许修改的数据
|
||||
if (CollUtil.isEmpty(order_ids)) {
|
||||
log.warn("[确认收货] 参数校验失败:订单ID列表为空");
|
||||
throw new ApiException(I18nUtil._("请选择需要确认收货的订单!"));
|
||||
}
|
||||
|
||||
if (CollUtil.isEmpty(order_rows)) {
|
||||
log.debug("[确认收货] 订单数据列表为空,根据订单ID列表查询订单数据");
|
||||
order_rows = gets(order_ids);
|
||||
}
|
||||
|
||||
List<String> receive_id_row = new ArrayList<>();
|
||||
// 检查订单数据是否存在
|
||||
if (CollUtil.isEmpty(order_rows)) {
|
||||
log.warn("[确认收货] 无法获取订单数据: orderIds={}", order_ids);
|
||||
throw new ApiException(I18nUtil._("无法获取订单数据!"));
|
||||
}
|
||||
|
||||
List<String> receive_id_row = new ArrayList<>();
|
||||
List<String> not_eligible_orders = new ArrayList<>(); // 记录不符合条件的订单
|
||||
|
||||
// 遍历订单列表,处理满足收货条件的订单
|
||||
for (ShopOrderBase order_row : order_rows) {
|
||||
// 是否允许确认收货?
|
||||
// 判断订单是否可以确认收货
|
||||
if (ifReceive(order_row.getOrder_state_id())) {
|
||||
receive_id_row.add(order_row.getOrder_id());
|
||||
|
||||
// 增加积分和经验
|
||||
// todo 目前付款支付积分,此处为收货后发放
|
||||
Integer user_id = order_row.getBuyer_user_id();
|
||||
String order_id = order_row.getOrder_id();
|
||||
Integer store_id = order_row.getStore_id();
|
||||
|
||||
ShopOrderData order_data_row = shopOrderDataService.get(order_id);
|
||||
BigDecimal order_points_add = order_data_row.getOrder_points_add();
|
||||
|
||||
BigDecimal order_points_add_all = order_points_add.add(order_data_row.getOrder_double_points_add());
|
||||
|
||||
if (CheckUtil.isNotEmpty(order_points_add_all)) {
|
||||
String desc = String.format(I18nUtil._("购物获取积分 %s,订单号 %s"), order_points_add_all, order_id);
|
||||
if (!payService.points(user_id, order_points_add_all, PointsType.POINTS_TYPE_CONSUME, desc, store_id, null, order_id)) {
|
||||
throw new ApiException(I18nUtil._("积分操作失败!"));
|
||||
}
|
||||
}
|
||||
|
||||
// todo 根据送花郎插件是否开启显示是否需要分钱给不同商户
|
||||
/*
|
||||
boolean hall_enable = accountBaseConfigService.getConfig("hall_enable", false);
|
||||
if (hall_enable) {
|
||||
BigDecimal order_commission_fee = order_data_row.getOrder_commission_fee();
|
||||
sendMoneyForTransfer(order_row, order_commission_fee);
|
||||
}
|
||||
*/
|
||||
|
||||
// todo 目前付款成功发放佣金,此处为收货后发放
|
||||
// 分销功能。
|
||||
String fx_settle_type = accountBaseConfigService.getConfig("fx_settle_type", "receive");
|
||||
if (StrUtil.equals(fx_settle_type, "receive")) {
|
||||
// todo settleDistributionUserOrder
|
||||
shopDistributionUserOrderService.settleDistributionUserOrder(order_id);
|
||||
}
|
||||
|
||||
// 重要;拉卡拉给平台和代理商分账
|
||||
Pair<Boolean, String> retOrderSeparateRet = lakalaApiService.innerDoOrderSeparate(order_row.getOrder_id(), Convert.toStr(order_row.getStore_id()));
|
||||
if (!retOrderSeparateRet.getFirst()) {
|
||||
throw new ApiException(I18nUtil._("平台或代理商分账失败: " + retOrderSeparateRet.getSecond()));
|
||||
}
|
||||
|
||||
// 统计总营业额
|
||||
ShopStoreAnalytics analytics_row = shopStoreAnalyticsService.get(store_id);
|
||||
BigDecimal order_payment_amount = order_row.getOrder_payment_amount();
|
||||
analytics_row.setStore_trade_amount(NumberUtil.add(analytics_row.getStore_trade_amount(), order_payment_amount));
|
||||
if (!shopStoreAnalyticsService.edit(analytics_row)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
log.info("[确认收货] 处理订单: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
|
||||
} else {
|
||||
not_eligible_orders.add(order_row.getOrder_id());
|
||||
log.warn("[确认收货] 订单状态不满足收货条件: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否有符合条件的订单
|
||||
if (CollUtil.isEmpty(receive_id_row)) {
|
||||
throw new ApiException(I18nUtil._("无符合确认收货条件的订单!"));
|
||||
log.warn("[确认收货] 无符合确认收货条件的订单,总订单数={},不符合条件订单数={}", order_rows.size(), not_eligible_orders.size());
|
||||
// 记录所有订单的详细状态信息
|
||||
for (ShopOrderBase order_row : order_rows) {
|
||||
log.warn("[确认收货] 订单状态详情: orderId={}, currentState={}", order_row.getOrder_id(), order_row.getOrder_state_id());
|
||||
}
|
||||
|
||||
// 如果是通过顺丰通知触发的收货,且订单已经是完成状态,则直接返回成功
|
||||
if (order_rows.size() == 1 && not_eligible_orders.size() == 1) {
|
||||
ShopOrderBase order = order_rows.get(0);
|
||||
// 如果订单已经是完成状态,则认为收货成功
|
||||
if (ObjectUtil.equal(order.getOrder_state_id(), StateCode.ORDER_STATE_FINISH)) {
|
||||
log.info("[确认收货] 订单已经是完成状态,直接返回成功: orderId={}", order.getOrder_id());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// throw new ApiException(I18nUtil._("无符合确认收货条件的订单!"));
|
||||
log.info("[确认收货] 无符合确认收货条件的订单!");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.info("[确认收货] 符合条件的订单数量: eligibleCount={}, totalChecked={}", receive_id_row.size(), order_rows.size());
|
||||
|
||||
// 处理符合条件的订单
|
||||
for (ShopOrderBase order_row : order_rows) {
|
||||
// 只处理符合条件的订单
|
||||
if (!receive_id_row.contains(order_row.getOrder_id())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 增加积分和经验
|
||||
// todo 目前付款支付积分,此处为收货后发放
|
||||
Integer user_id = order_row.getBuyer_user_id();
|
||||
String order_id = order_row.getOrder_id();
|
||||
Integer store_id = order_row.getStore_id();
|
||||
|
||||
ShopOrderData order_data_row = shopOrderDataService.get(order_id);
|
||||
if (order_data_row == null) {
|
||||
log.warn("[确认收货] 无法获取订单数据: orderId={}", order_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
BigDecimal order_points_add = order_data_row.getOrder_points_add();
|
||||
BigDecimal order_double_points_add = order_data_row.getOrder_double_points_add();
|
||||
BigDecimal order_points_add_all = order_points_add != null ? order_points_add : BigDecimal.ZERO;
|
||||
order_points_add_all = order_points_add_all.add(order_double_points_add != null ? order_double_points_add : BigDecimal.ZERO);
|
||||
|
||||
// 发放购物积分
|
||||
if (CheckUtil.isNotEmpty(order_points_add_all) && order_points_add_all.compareTo(BigDecimal.ZERO) > 0) {
|
||||
String desc = String.format(I18nUtil._("购物获取积分 %s,订单号 %s"), order_points_add_all, order_id);
|
||||
log.debug("[确认收货] 发放购物积分: userId={}, points={}, desc={}", user_id, order_points_add_all, desc);
|
||||
if (!payService.points(user_id, order_points_add_all, PointsType.POINTS_TYPE_CONSUME, desc, store_id, null, order_id)) {
|
||||
log.error("[确认收货] 积分操作失败: userId={}, points={}, orderId={}", user_id, order_points_add_all, order_id);
|
||||
// throw new ApiException(I18nUtil._("积分操作失败!"));
|
||||
}
|
||||
}
|
||||
|
||||
// todo 根据送花郎插件是否开启显示是否需要分钱给不同商户
|
||||
/*
|
||||
boolean hall_enable = accountBaseConfigService.getConfig("hall_enable", false);
|
||||
if (hall_enable) {
|
||||
BigDecimal order_commission_fee = order_data_row.getOrder_commission_fee();
|
||||
sendMoneyForTransfer(order_row, order_commission_fee);
|
||||
}
|
||||
*/
|
||||
|
||||
// todo 目前付款成功发放佣金,此处为收货后发放
|
||||
// 分销功能。
|
||||
String fx_settle_type = accountBaseConfigService.getConfig("fx_settle_type", "receive");
|
||||
if (StrUtil.equals(fx_settle_type, "receive")) {
|
||||
log.debug("[确认收货] 处理分销订单: orderId={}", order_id);
|
||||
// todo settleDistributionUserOrder
|
||||
shopDistributionUserOrderService.settleDistributionUserOrder(order_id);
|
||||
}
|
||||
|
||||
// 统计总营业额
|
||||
ShopStoreAnalytics analytics_row = shopStoreAnalyticsService.get(store_id);
|
||||
if (analytics_row != null) {
|
||||
BigDecimal order_payment_amount = order_row.getOrder_payment_amount();
|
||||
log.debug("[确认收货] 更新店铺营业额: storeId={}, amount={}", store_id, order_payment_amount);
|
||||
analytics_row.setStore_trade_amount(NumberUtil.add(analytics_row.getStore_trade_amount(), order_payment_amount != null ? order_payment_amount : BigDecimal.ZERO));
|
||||
if (!shopStoreAnalyticsService.edit(analytics_row)) {
|
||||
log.error("[确认收货] 更新店铺营业额失败: storeId={}", store_id);
|
||||
// throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
} else {
|
||||
log.warn("[确认收货] 无法获取店铺统计信息: storeId={}", store_id);
|
||||
}
|
||||
}
|
||||
|
||||
// 修改订单状态, 随机去一个订单获取店铺编号
|
||||
ShopOrderBase shopOrderBase = order_rows.get(0);
|
||||
ShopOrderBase shopOrderBase = order_rows.stream()
|
||||
.filter(order -> receive_id_row.contains(order.getOrder_id()))
|
||||
.findFirst()
|
||||
.orElse(order_rows.get(0));
|
||||
Integer store_id = shopOrderBase.getStore_id();
|
||||
log.debug("[确认收货] 修改订单状态: storeId={}", store_id);
|
||||
editNextState(receive_id_row, store_id, StateCode.ORDER_STATE_SHIPPED, order_rows, 0);
|
||||
|
||||
// 如果是商家,且启用供应商,则商家看到供应商店铺商品 store_type = 2
|
||||
@ -4990,7 +5093,9 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
UserDto user = getCurrentUser();
|
||||
store_id = user != null ? Convert.toInt(user.getStore_id(), 0) : 0;
|
||||
|
||||
// 处理供应商市场的库存增加逻辑
|
||||
if (ifSupplierMarket && CheckUtil.isNotEmpty(store_id)) {
|
||||
log.debug("[确认收货] 处理供应商市场库存: storeId={}", store_id);
|
||||
// 供应商商品,增加商家库存
|
||||
QueryWrapper<ShopOrderItem> itemQueryWrapper = new QueryWrapper<>();
|
||||
itemQueryWrapper.in("order_id", receive_id_row);
|
||||
@ -5002,30 +5107,39 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
productItemQueryWrapper.in("item_src_id", item_src_ids).eq("store_id", store_id);
|
||||
List<ShopProductItem> product_item_rows = shopProductItemService.find(productItemQueryWrapper);
|
||||
|
||||
// 更新供应商商品库存
|
||||
log.debug("[确认收货] 更新供应商商品库存: itemsCount={}", product_item_rows.size());
|
||||
for (ShopProductItem product_item_row : product_item_rows) {
|
||||
String item_src_id = product_item_row.getItem_src_id();
|
||||
Optional<ShopOrderItem> orderItemOpl = order_item_rows.stream().filter(s -> ObjectUtil.equal(s.getItem_id(), item_src_id)).findFirst();
|
||||
if (orderItemOpl.isPresent()) {
|
||||
ShopOrderItem shopOrderItem = orderItemOpl.get();
|
||||
Integer order_item_quantity = shopOrderItem.getOrder_item_quantity();
|
||||
product_item_row.setItem_quantity(product_item_row.getItem_quantity() + order_item_quantity);
|
||||
if (!shopProductItemService.edit(product_item_row)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
if (order_item_quantity != null && order_item_quantity > 0) {
|
||||
log.debug("[确认收货] 更新商品库存: itemId={}, oldQuantity={}, addQuantity={}",
|
||||
item_src_id, product_item_row.getItem_quantity(), order_item_quantity);
|
||||
product_item_row.setItem_quantity(product_item_row.getItem_quantity() + order_item_quantity);
|
||||
if (!shopProductItemService.edit(product_item_row)) {
|
||||
log.error("[确认收货] 更新商品库存失败: itemId={}", item_src_id);
|
||||
// throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("[确认收货] 处理完成: processedOrders={}", receive_id_row.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单确认收货(定时任务用途)
|
||||
*
|
||||
* @param order_id 订单id
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public boolean receive(String order_id, ShopOrderBase orderBase) {
|
||||
return receive(Collections.singletonList(order_id), null);
|
||||
}
|
||||
@ -7847,12 +7961,13 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
/**
|
||||
* 是否可以确认收货
|
||||
*
|
||||
* @param order_state_id 订单状态
|
||||
* @return boolean true-可,false-不可
|
||||
* @access public
|
||||
* @param orderStateId 订单状态
|
||||
* @return boolean true-可确认收货,false-不可确认收货
|
||||
*/
|
||||
private boolean ifReceive(Integer order_state_id) {
|
||||
return ObjectUtil.equal(order_state_id, StateCode.ORDER_STATE_SHIPPED);
|
||||
private boolean ifReceive(Integer orderStateId) {
|
||||
// 只有已发货已签收状态的订单才能确认收货
|
||||
return ObjectUtil.equal(orderStateId, StateCode.ORDER_STATE_SHIPPED) ||
|
||||
ObjectUtil.equal(orderStateId, StateCode.ORDER_STATE_RECEIVED);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -8005,6 +8120,14 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
order_info_row.putAll(order_data_row);
|
||||
order_info_row.put("item", order_item_temp_rows.get(order_id));
|
||||
|
||||
// update 2025-09-10 增加KLK支付信息
|
||||
ShopOrderLkl shopOrderLkl = shopOrderLklService.getByStoreIdAndOrderId(store_id, order_id);
|
||||
if (shopOrderLkl != null) {
|
||||
order_info_row.put("lkl_merchant_no", shopOrderLkl.getLkl_merchant_no());
|
||||
order_info_row.put("lkl_trade_no", shopOrderLkl.getWx_transaction_id());
|
||||
order_info_row.put("lkl_log_no", shopOrderLkl.getLkl_sub_log_no());
|
||||
}
|
||||
|
||||
Optional<Map> store_row_Opl = store_rows.stream().filter(s -> ObjectUtil.equal(store_id, Convert.toInt(s.get("store_id")))).findFirst();
|
||||
Map store_row = store_row_Opl.orElseGet(HashMap::new);
|
||||
order_info_row.put("self_support", store_row.get("store_is_selfsupport"));
|
||||
@ -8513,12 +8636,14 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
* 2、商家的详细地址、电话和经纬度
|
||||
* 3、收货人的姓名、电话、详细地址、经纬度
|
||||
*
|
||||
* @param shopOrderId 商城订单id
|
||||
* @return
|
||||
* @param devId 开发者ID
|
||||
* @param shopOrderId 商城订单id
|
||||
* @param orderPickupNum 订单取货编号
|
||||
* @return SFCreateOrderReq 顺丰同城订单请求对象,如果构建失败返回null
|
||||
*/
|
||||
public SFCreateOrderReq buildSFOrderData(Integer devId, String shopOrderId, Long orderPickupNum) {
|
||||
if (StrUtil.isBlank(shopOrderId)) {
|
||||
logger.error("缺少订单Id");
|
||||
logger.error("构建顺丰订单失败:缺少订单Id,devId={}, shopOrderId={}", devId, shopOrderId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8528,13 +8653,13 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
queryWrapper.eq("order_id", shopOrderId);
|
||||
List<ShopOrderItem> shopOrderItemList = shopOrderItemService.list(queryWrapper);
|
||||
if (shopOrderBase == null || shopOrderData == null || CollUtil.isEmpty(shopOrderItemList)) {
|
||||
logger.error("无法获取订单信息!");
|
||||
logger.error("构建顺丰订单失败:无法获取订单信息,订单ID={}", shopOrderId);
|
||||
return null;
|
||||
}
|
||||
|
||||
Integer storeId = shopOrderBase.getStore_id();
|
||||
if (storeId == null || storeId <= 0) {
|
||||
logger.error("缺少店铺 Id!");
|
||||
logger.error("构建顺丰订单失败:缺少店铺Id,订单ID={},storeId={}", shopOrderId, storeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8543,12 +8668,7 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
ShopOrderDeliveryAddress shopOrderDeliveryAddress = shopOrderDeliveryAddressService.selectByOrderId(shopOrderId);
|
||||
ShopStoreSameCityTransportBase shopStoreSameCityTransportBase = shopStoreSameCityTransportBaseService.getShopStoreSameCityTransportBaseById(storeId.longValue());
|
||||
if (shopStoreBase == null || shopStoreInfo == null || shopOrderDeliveryAddress == null || shopStoreSameCityTransportBase == null) {
|
||||
logger.error("无法获取店铺信息!");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (shopStoreSameCityTransportBase == null) {
|
||||
logger.error("请配置同城配送设置!");
|
||||
logger.error("构建顺丰订单失败:无法获取店铺信息,订单ID={},storeId={}", shopOrderId, storeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8556,11 +8676,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
String shopId = "3243279847393";
|
||||
//3269768224353 到时启用这个正式店铺 type:便利店
|
||||
Integer businessType = 6;// 生鲜分类
|
||||
if (enable_sf_express.equals(CommonConstant.Enable)) {//开启正式配送服务的时候
|
||||
if (CommonConstant.Enable.equals(enable_sf_express)) {//开启正式配送服务的时候
|
||||
// 顺丰同城业务员给的店铺id
|
||||
shopId = shopStoreSameCityTransportBase.getShop_id();
|
||||
if (StrUtil.isBlank(shopId)) {
|
||||
logger.error("请联系顺丰同城配置店铺ID!");
|
||||
logger.error("构建顺丰订单失败:请联系顺丰同城配置店铺ID,订单ID={},storeId={}", shopOrderId, storeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -8582,8 +8702,8 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
sfCreateOrderReq.setVersion(19);
|
||||
sfCreateOrderReq.setReturn_flag(511);
|
||||
|
||||
Integer productNum = 0;
|
||||
Integer productTypeNum = shopOrderItemList.size();
|
||||
Integer totalProductQuantity = 0;
|
||||
Integer productTypeCount = shopOrderItemList.size();
|
||||
|
||||
// 订单详情信息
|
||||
SFOrderDetailReq orderDetail = new SFOrderDetailReq();
|
||||
@ -8592,24 +8712,35 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
List<SFOrderProductDetailReq> orderProductList = new ArrayList<>();
|
||||
|
||||
for (ShopOrderItem shopOrderItem : shopOrderItemList) {
|
||||
productNum += shopOrderItem.getOrder_item_quantity();
|
||||
totalProductQuantity += shopOrderItem.getOrder_item_quantity();
|
||||
// 产品详情
|
||||
SFOrderProductDetailReq orderProductDetail = new SFOrderProductDetailReq();
|
||||
orderProductDetail.setProduct_id(shopOrderItem.getProduct_id());
|
||||
orderProductDetail.setProduct_name(shopOrderItem.getItem_name());
|
||||
orderProductDetail.setProduct_num(shopOrderItem.getOrder_item_quantity());
|
||||
// 单个商品金额=数量*单价,单位分
|
||||
Integer itemPrice = shopOrderItem.getOrder_item_unit_price().multiply(BigDecimal.valueOf(shopOrderItem.getOrder_item_quantity())).multiply(BigDecimal.valueOf(100)).intValue();
|
||||
// 单个商品金额=单价*数量,单位分
|
||||
BigDecimal itemPriceDecimal = shopOrderItem.getOrder_item_unit_price()
|
||||
.multiply(BigDecimal.valueOf(100))
|
||||
.multiply(BigDecimal.valueOf(shopOrderItem.getOrder_item_quantity()));
|
||||
Integer itemPrice = itemPriceDecimal.intValue();
|
||||
orderProductDetail.setProduct_price(itemPrice);
|
||||
orderProductList.add(orderProductDetail);
|
||||
|
||||
// 记录商品详情日志,便于部署后排查问题
|
||||
logger.debug("订单{}商品详情:商品ID={}, 商品名称={}, 数量={}, 单价(分)={}, 小计(分)={}",
|
||||
shopOrderId,
|
||||
shopOrderItem.getProduct_id(),
|
||||
shopOrderItem.getItem_name(),
|
||||
shopOrderItem.getOrder_item_quantity(),
|
||||
shopOrderItem.getOrder_item_unit_price().multiply(BigDecimal.valueOf(100)).intValue(),
|
||||
itemPrice);
|
||||
}
|
||||
|
||||
orderDetail.setProduct_type(businessType); // 店铺主营商品分类,参考:https://commit-openic.sf-express.com/#/apidoc
|
||||
orderDetail.setTotal_price(shopOrderBase.getOrder_payment_amount().multiply(BigDecimal.valueOf(100)).intValue()); // 单位分
|
||||
orderDetail.setWeight_gram(0); // 重量一律传 0kg 先,谢总本地运营商协商好的
|
||||
orderDetail.setProduct_num(productNum); //物品个数
|
||||
orderDetail.setProduct_type_num(productTypeNum); //物品种类个数
|
||||
|
||||
orderDetail.setProduct_num(totalProductQuantity); //物品个数
|
||||
orderDetail.setProduct_type_num(productTypeCount); //物品种类个数
|
||||
|
||||
// 订单里的所有商品列表
|
||||
orderDetail.setProduct_detail(orderProductList);
|
||||
@ -8623,6 +8754,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
|
||||
// 店铺信息(发货人信息)
|
||||
Pair<Boolean, StandardAddressDTO> pairShopAddr = shopStoreBaseService.checkStoreAddress(shopStoreBase);
|
||||
if (!pairShopAddr.getFirst()) {
|
||||
logger.error("构建顺丰订单失败:店铺地址校验失败,订单ID={},storeId={}", shopOrderId, storeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
shop.setShop_name(shopStoreBase.getStore_name());
|
||||
shop.setShop_phone(shopStoreInfo.getStore_tel());
|
||||
shop.setShop_address(pairShopAddr.getSecond().getFullAddress());
|
||||
@ -8631,6 +8767,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
|
||||
// 收货人信息
|
||||
Pair<Boolean, StandardAddressDTO> pairReceiveAddr = shopOrderDeliveryAddressService.checkAddress(shopOrderDeliveryAddress);
|
||||
if (!pairReceiveAddr.getFirst()) {
|
||||
logger.error("构建顺丰订单失败:收货地址校验失败,订单ID={},storeId={}", shopOrderId, storeId);
|
||||
return null;
|
||||
}
|
||||
|
||||
receive.setUser_name(shopOrderDeliveryAddress.getDa_name());
|
||||
receive.setUser_phone(shopOrderDeliveryAddress.getDa_mobile());
|
||||
receive.setUser_address(pairReceiveAddr.getSecond().getFullAddress());
|
||||
@ -8655,6 +8796,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
sfCreateOrderReq.setShop(shop);
|
||||
sfCreateOrderReq.setReceive(receive);
|
||||
|
||||
// 记录完整的订单信息,便于部署后排查问题
|
||||
logger.info("成功构建顺丰订单:订单ID={}, 店铺ID={}, 顺丰同城店铺ID={}, 商品种类数={}, 商品总数={}, 总价(分)={}",
|
||||
shopOrderId, storeId, shopId, productTypeCount, totalProductQuantity,
|
||||
shopOrderBase.getOrder_payment_amount().multiply(BigDecimal.valueOf(100)).intValue());
|
||||
|
||||
return sfCreateOrderReq;
|
||||
}
|
||||
|
||||
@ -9039,38 +9185,6 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 预处理发货订单超时消息(发到 mq 里,触发超时事件,发出推送消息)
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param orderId 订单ID
|
||||
* @param expireSeconds 过期时间(秒)
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
// @Async
|
||||
// @Override
|
||||
// public Boolean preSendExpiredSFOrderPushMessage(Integer storeId, String orderId, Long expireSeconds) {
|
||||
// try {
|
||||
// // 构建延迟消息内容
|
||||
// JSONObject jsonObject = new JSONObject();
|
||||
// jsonObject.put("category", MqConstant.DEAD_EVENT_CATE_ORDER_EXPIRED); // 消息分类:1-订单超时消息
|
||||
// jsonObject.put("orderId", orderId); // 订单ID
|
||||
// jsonObject.put("storeId", storeId); // 店铺ID
|
||||
// jsonObject.put("title", "有一笔已超时的订单!"); // 消息标题
|
||||
// jsonObject.put("message", "您有一笔已超时的订单[" + orderId + "],请及时处理。"); // 消息内容
|
||||
//
|
||||
// // 发送延迟消息
|
||||
// mqMessageService.sendDelayMessage(jsonObject.toString(), expireSeconds * 1000); // 转换为毫秒
|
||||
//
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error("发送延迟订单超时消息失败,店铺ID:{},订单ID:{},过期时间:{}秒",
|
||||
// storeId, orderId, expireSeconds, e);
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 取货单号格式化
|
||||
*
|
||||
@ -9112,11 +9226,11 @@ public class ShopOrderBaseServiceImpl extends BaseServiceImpl<ShopOrderBaseMappe
|
||||
}
|
||||
|
||||
// 确保比例在合理范围 [0, 100]
|
||||
storeSplitRatio = storeSplitRatio.max(BigDecimal.ZERO).min(new BigDecimal("100"));
|
||||
storeSplitRatio = storeSplitRatio.max(BigDecimal.ZERO).min(new BigDecimal(100));
|
||||
|
||||
// 分账金额 = 支付金额 × 平台和代理商分账比例 ÷ 100 (将百分比转换为小数)
|
||||
BigDecimal result = pendingAmount.multiply(new BigDecimal("100").subtract(storeSplitRatio))
|
||||
.divide(new BigDecimal("100"), 2, RoundingMode.HALF_DOWN); // 保留两位小数
|
||||
BigDecimal result = pendingAmount.multiply(new BigDecimal(100).subtract(storeSplitRatio))
|
||||
.divide(new BigDecimal(100), 2, RoundingMode.HALF_DOWN); // 保留两位小数
|
||||
|
||||
logger.debug("计算分账金额: storeId={}, pendingAmount={}, ratio={}, result={}",
|
||||
storeId, pendingAmount, storeSplitRatio, result);
|
||||
|
||||
@ -229,81 +229,127 @@ public class ShopOrderInfoServiceImpl extends BaseServiceImpl<ShopOrderInfoMappe
|
||||
/**
|
||||
* 更改商家订单的状态,出库状态,发货状态,并写入订单状态更改日志
|
||||
*
|
||||
* @param orderId
|
||||
* @param orderStatus 约定0值不更改
|
||||
* @param orderId 订单ID
|
||||
* @param orderStatus 订单状态,约定0值不更改
|
||||
* @param orderIsOutStatus 出库状态,约定0值不更改
|
||||
* @param orderIsShippedStatus 发货状态,约定0值不更改
|
||||
* @return
|
||||
* @return Boolean 是否更新成功
|
||||
*/
|
||||
@Transactional
|
||||
@Override
|
||||
public Boolean changeOrderStatus(String orderId, Integer orderStatus, Integer orderIsOutStatus, Integer orderIsShippedStatus) {
|
||||
if (orderId == null || orderStatus == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ShopOrderInfo shopOrderInfo = getById(orderId);
|
||||
if (shopOrderInfo == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Integer preOrderStatus = shopOrderInfo.getOrder_state_id();
|
||||
|
||||
int paramsCnt = 0;
|
||||
UpdateWrapper<ShopOrderInfo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("order_id", orderId);
|
||||
if (orderStatus > 0) {
|
||||
updateWrapper.set("order_state_id", orderStatus);
|
||||
paramsCnt++;
|
||||
}
|
||||
|
||||
if (orderIsOutStatus > 0 && !shopOrderInfo.getOrder_is_out().equals(orderIsOutStatus)) {
|
||||
updateWrapper.set("order_is_out", orderStatus);
|
||||
paramsCnt++;
|
||||
}
|
||||
|
||||
if (orderStatus > 0 && !shopOrderInfo.getOrder_is_shipped().equals(orderIsShippedStatus)) {
|
||||
updateWrapper.set("order_is_shipped", orderIsShippedStatus);
|
||||
paramsCnt++;
|
||||
}
|
||||
|
||||
if (paramsCnt <= 0) {
|
||||
// 无修改的状态
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!update(updateWrapper)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateWrapper<ShopOrderBase> updateShopOrderBaseWrapper = new UpdateWrapper<>();
|
||||
updateShopOrderBaseWrapper.eq("order_id", orderId);
|
||||
if (orderStatus > 0) {
|
||||
updateShopOrderBaseWrapper.set("order_state_id", orderStatus);
|
||||
if (!shopOrderBaseService.update(updateShopOrderBaseWrapper)) {
|
||||
try {
|
||||
// 参数校验
|
||||
if (orderId == null || orderStatus == null) {
|
||||
logger.warn("[订单状态变更] 参数校验失败: orderId或orderStatus为空, orderId={}, orderStatus={}", orderId, orderStatus);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 远程调用 更改交易订单状态
|
||||
if (!payService.changePayConsumeTradeState(orderId, orderStatus, 0)) {
|
||||
logger.error("远程调用更改交易订单状态失败!");
|
||||
// 获取订单信息
|
||||
ShopOrderInfo shopOrderInfo = getById(orderId);
|
||||
if (shopOrderInfo == null) {
|
||||
logger.warn("[订单状态变更] 未找到订单信息: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug("[订单状态变更] 开始处理订单状态变更: orderId={}, currentStatus={}, newStatus={}, outStatus={}, shippedStatus={}",
|
||||
orderId, shopOrderInfo.getOrder_state_id(), orderStatus, orderIsOutStatus, orderIsShippedStatus);
|
||||
|
||||
Integer preOrderStatus = shopOrderInfo.getOrder_state_id();
|
||||
|
||||
int paramsCnt = 0;
|
||||
UpdateWrapper<ShopOrderInfo> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("order_id", orderId);
|
||||
|
||||
// 更新订单状态
|
||||
if (orderStatus > 0) {
|
||||
updateWrapper.set("order_state_id", orderStatus);
|
||||
|
||||
// 已签收或已完成,更改两个时间
|
||||
if (StateCode.ORDER_STATE_RECEIVED == orderStatus.intValue() || orderStatus == StateCode.ORDER_STATE_FINISH) {
|
||||
updateWrapper.set("order_deal_time", System.currentTimeMillis());
|
||||
updateWrapper.set("order_qs_time", new Date());
|
||||
}
|
||||
|
||||
paramsCnt++;
|
||||
logger.debug("[订单状态变更] 添加订单状态更新条件: orderId={}, status={}", orderId, orderStatus);
|
||||
}
|
||||
|
||||
// 更新出库状态
|
||||
if (orderIsOutStatus != null && orderIsOutStatus > 0 &&
|
||||
!ObjectUtil.equal(shopOrderInfo.getOrder_is_out(), orderIsOutStatus)) {
|
||||
updateWrapper.set("order_is_out", orderIsOutStatus);
|
||||
paramsCnt++;
|
||||
logger.debug("[订单状态变更] 添加出库状态更新条件: orderId={}, outStatus={}", orderId, orderIsOutStatus);
|
||||
}
|
||||
|
||||
// 更新发货状态
|
||||
if (orderIsShippedStatus != null && orderIsShippedStatus > 0 &&
|
||||
!ObjectUtil.equal(shopOrderInfo.getOrder_is_shipped(), orderIsShippedStatus)) {
|
||||
updateWrapper.set("order_is_shipped", orderIsShippedStatus);
|
||||
paramsCnt++;
|
||||
logger.debug("[订单状态变更] 添加发货状态更新条件: orderId={}, shippedStatus={}", orderId, orderIsShippedStatus);
|
||||
}
|
||||
|
||||
// 检查是否有需要更新的字段
|
||||
if (paramsCnt <= 0) {
|
||||
logger.info("[订单状态变更] 无需要更新的字段: orderId={}", orderId);
|
||||
// 无修改的状态
|
||||
return false;
|
||||
}
|
||||
|
||||
// 执行订单信息更新
|
||||
logger.debug("[订单状态变更] 执行订单信息更新: orderId={}, updateFields={}", orderId, paramsCnt);
|
||||
if (!update(updateWrapper)) {
|
||||
logger.error("[订单状态变更] 订单信息更新失败: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新订单基础信息中的状态
|
||||
UpdateWrapper<ShopOrderBase> updateShopOrderBaseWrapper = new UpdateWrapper<>();
|
||||
updateShopOrderBaseWrapper.eq("order_id", orderId);
|
||||
if (orderStatus > 0) {
|
||||
updateShopOrderBaseWrapper.set("order_state_id", orderStatus);
|
||||
logger.debug("[订单状态变更] 执行订单基础信息更新: orderId={}, status={}", orderId, orderStatus);
|
||||
if (!shopOrderBaseService.update(updateShopOrderBaseWrapper)) {
|
||||
logger.error("[订单状态变更] 订单基础信息更新失败: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 写入订单状态更改日志
|
||||
logger.debug("[订单状态变更] 写入订单状态变更日志: orderId={}, preStatus={}, newStatus={}", orderId, preOrderStatus, orderStatus);
|
||||
ShopOrderStateLog shopOrderStateLog = new ShopOrderStateLog();
|
||||
shopOrderStateLog.setOrder_id(orderId);
|
||||
shopOrderStateLog.setOrder_state_is_sync(0);
|
||||
shopOrderStateLog.setOrder_state_id(orderStatus);
|
||||
shopOrderStateLog.setOrder_state_time(new Date());
|
||||
shopOrderStateLog.setOrder_state_pre_id(preOrderStatus);
|
||||
shopOrderStateLog.setOrder_state_type(shopBaseStateCodeService.getText(orderStatus, null));
|
||||
shopOrderStateLog.setOrder_state_note(CommonUtil.getOrderStateNote(preOrderStatus, orderStatus));
|
||||
shopOrderStateLog.setUser_id(0);
|
||||
shopOrderStateLog.setUser_nickname("SfExpress");
|
||||
|
||||
boolean logResult = shopOrderStateLogService.add(shopOrderStateLog);
|
||||
if (logResult) {
|
||||
logger.info("[订单状态变更] 订单状态变更成功: orderId={}, preStatus={}, newStatus={}", orderId, preOrderStatus, orderStatus);
|
||||
|
||||
// 远程调用 更改交易订单状态
|
||||
logger.debug("[订单状态变更] 调用支付服务更新交易订单状态: orderId={}, status={}", orderId, orderStatus);
|
||||
if (!payService.changePayConsumeTradeState(orderId, orderStatus, 0)) {
|
||||
logger.error("[订单状态变更] 远程调用更改交易订单状态失败: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.error("[订单状态变更] 订单状态日志写入失败: orderId={}", orderId);
|
||||
}
|
||||
|
||||
return logResult;
|
||||
} catch (Exception e) {
|
||||
logger.error("[订单状态变更] 处理过程中发生异常: orderId={}, orderStatus={}, outStatus={}, shippedStatus={}",
|
||||
orderId, orderStatus, orderIsOutStatus, orderIsShippedStatus, e);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 写入订单状态更改日志
|
||||
ShopOrderStateLog shopOrderStateLog = new ShopOrderStateLog();
|
||||
shopOrderStateLog.setOrder_id(orderId);
|
||||
shopOrderStateLog.setOrder_state_is_sync(0);
|
||||
shopOrderStateLog.setOrder_state_id(orderStatus);
|
||||
shopOrderStateLog.setOrder_state_time(new Date());
|
||||
shopOrderStateLog.setOrder_state_pre_id(preOrderStatus);
|
||||
shopOrderStateLog.setOrder_state_type(shopBaseStateCodeService.getText(orderStatus, null));
|
||||
shopOrderStateLog.setOrder_state_note(CommonUtil.getOrderStateNote(preOrderStatus, orderStatus));
|
||||
shopOrderStateLog.setUser_id(0);
|
||||
shopOrderStateLog.setUser_nickname("SfExpress");
|
||||
|
||||
return shopOrderStateLogService.add(shopOrderStateLog);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -14,8 +14,10 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.suisung.mall.common.modules.order.ShopOrderBase;
|
||||
import com.suisung.mall.common.modules.order.ShopOrderLkl;
|
||||
import com.suisung.mall.common.utils.CheckUtil;
|
||||
import com.suisung.mall.common.utils.DateTimeUtils;
|
||||
import com.suisung.mall.common.utils.JsonUtil;
|
||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||
@ -50,7 +52,6 @@ public class ShopOrderLklServiceImpl extends BaseServiceImpl<ShopOrderLklMapper,
|
||||
@Resource
|
||||
private ShopOrderDataService shopOrderDataService;
|
||||
|
||||
|
||||
@Override
|
||||
public Boolean addOrUpdateByStoreOrder(ShopOrderLkl record) {
|
||||
if (record == null
|
||||
@ -122,97 +123,157 @@ public class ShopOrderLklServiceImpl extends BaseServiceImpl<ShopOrderLklMapper,
|
||||
* "merchantNo": "8226330599900LN"
|
||||
* }
|
||||
* }
|
||||
* @return
|
||||
* @return Boolean 是否保存或更新成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean addOrUpdateByLklPayDataJson(JSONObject lklPayReqAndRespJson) {
|
||||
if (lklPayReqAndRespJson == null) {
|
||||
log.warn("参数为空,无法保存拉卡拉订单记录");
|
||||
log.warn("[拉卡拉订单保存] 参数为空,无法保存拉卡拉订单记录");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug("拉卡拉支付请求和响应数据 body:{}", lklPayReqAndRespJson);
|
||||
log.debug("[拉卡拉订单保存] 开始处理拉卡拉支付请求和响应数据: {}", lklPayReqAndRespJson);
|
||||
try {
|
||||
|
||||
// 获取请求体和响应体
|
||||
JSONObject reqDataJson = lklPayReqAndRespJson.getJSONObject("req");// 驼峰命名
|
||||
JSONObject reqDataJson = lklPayReqAndRespJson.getJSONObject("req");// 下划线命名
|
||||
JSONObject respDataJson = JSONUtil.parseObj(lklPayReqAndRespJson.getByPath("resp.resp_data"));// 下划线命名
|
||||
|
||||
if (reqDataJson == null || respDataJson == null) {
|
||||
log.error("请求或响应数据中的 req_data / resp_data 为空");
|
||||
log.error("[拉卡拉订单保存] 请求或响应数据中的 req_data / resp_data 为空");
|
||||
return false;
|
||||
}
|
||||
log.debug("[拉卡拉订单保存] 解析请求和响应数据完成");
|
||||
|
||||
// 提取订单号并校验
|
||||
String orderId = JsonUtil.getJsonValueSmart(respDataJson, "out_trade_no");
|
||||
if (StringUtils.isBlank(orderId)) {
|
||||
log.error("订单ID为空,无法保存拉卡拉支付记录");
|
||||
log.error("[拉卡拉订单保存] 订单ID为空,无法保存拉卡拉支付记录");
|
||||
return false;
|
||||
}
|
||||
log.debug("[拉卡拉订单保存] 解析订单ID: {}", orderId);
|
||||
|
||||
ShopOrderBase shopOrderBase = shopOrderBaseService.get(orderId);
|
||||
if (shopOrderBase == null) {
|
||||
log.error("订单不存在,无法保存拉卡拉支付记录");
|
||||
log.error("[拉卡拉订单保存] 订单不存在,无法保存拉卡拉支付记录: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
log.debug("[拉卡拉订单保存] 获取订单基础信息完成: storeId={}", shopOrderBase.getStore_id());
|
||||
|
||||
ShopOrderLkl record = new ShopOrderLkl();
|
||||
record.setOrder_id(orderId);
|
||||
String outSeparateNo = JsonUtil.getJsonValueSmart(reqDataJson, "outTradeNo");
|
||||
|
||||
// 平台最低内部配送费
|
||||
Integer shoppingFeeInner = Convert.toInt(JsonUtil.getJsonValueSmart(reqDataJson, "shopping_fee_inner"));
|
||||
if (shoppingFeeInner == null) {
|
||||
shoppingFeeInner = 0;
|
||||
}
|
||||
record.setOut_separate_no(outSeparateNo);
|
||||
log.debug("[拉卡拉订单保存] 设置订单ID和分离订单号: orderId={} outSeparateNo={}", orderId, outSeparateNo);
|
||||
|
||||
// 设置请求内容
|
||||
|
||||
// 订单金额安全处理
|
||||
Integer amount = JsonUtil.getJsonValueSmart(reqDataJson, "totalAmount", Integer.class);
|
||||
if (amount == null) {
|
||||
log.error("订单{}金额无效: {}", orderId, amount);
|
||||
log.error("[拉卡拉订单保存] 订单{}金额无效: amount={}", orderId, amount);
|
||||
return false;
|
||||
}
|
||||
record.setTotal_amt(amount); // 应支付总金额
|
||||
|
||||
record.setAccount_type(JsonUtil.getJsonValueSmart(reqDataJson, "accountType"));
|
||||
record.setTrans_type(JsonUtil.getJsonValueSmart(reqDataJson, "transType"));
|
||||
record.setNotify_url(JsonUtil.getJsonValueSmart(reqDataJson, "notifyUrl"));
|
||||
record.setLkl_merchant_no(JsonUtil.getJsonValueSmart(reqDataJson, "merchantNo"));
|
||||
record.setLkl_term_no(JsonUtil.getJsonValueSmart(reqDataJson, "termNo"));
|
||||
record.setLkl_req(JSONUtil.toJsonStr(reqDataJson));
|
||||
|
||||
// 设置响应内容
|
||||
record.setLkl_log_no(JsonUtil.getJsonValueSmart(respDataJson, "log_no"));
|
||||
record.setLkl_trade_no(JsonUtil.getJsonValueSmart(respDataJson, "trade_no"));
|
||||
record.setLkl_resp(JSONUtil.toJsonStr(respDataJson));
|
||||
log.debug("[拉卡拉订单保存] 设置订单金额: amount={}分", amount);
|
||||
|
||||
// 关键数据:获取店铺ID,分账比例用到
|
||||
Integer storeId = shopOrderBase.getStore_id();
|
||||
|
||||
record.setStore_id(Convert.toStr(storeId));
|
||||
//平台内部配送费
|
||||
record.setShopping_fee_inner(shoppingFeeInner);
|
||||
log.debug("[拉卡拉订单保存] 设置店铺ID: storeId={}", storeId);
|
||||
|
||||
// 运费和商家分账比例
|
||||
BigDecimal shipperFee = shopOrderDataService.getOrderShippingFee(orderId);
|
||||
record.setShopping_fee(shipperFee.multiply(BigDecimal.valueOf(100)).intValue()); // 运费,单位:分
|
||||
record.setSplit_ratio(shopStoreBaseService.getStoreSplitRatio(storeId, false)); // 商家分账比例
|
||||
BigDecimal shipperFee = shopOrderDataService.getOrderShippingFee(orderId); // 运费获取
|
||||
if (shipperFee != null) {
|
||||
int shipperFeeInCents = shipperFee.multiply(BigDecimal.valueOf(100)).intValue(); // 运费,单位:分
|
||||
record.setShopping_fee(shipperFeeInCents);
|
||||
log.debug("[拉卡拉订单保存] 商家设置运费: 元={} 分={}", shipperFee, shipperFeeInCents);
|
||||
} else {
|
||||
log.debug("[拉卡拉订单保存] 未获取到运费信息");
|
||||
}
|
||||
|
||||
return addOrUpdateByStoreOrder(record);
|
||||
BigDecimal splitRatio = shopStoreBaseService.getStoreSplitRatio(storeId, false); // 商家分账比例计算
|
||||
record.setSplit_ratio(splitRatio);
|
||||
log.debug("[拉卡拉订单保存] 设置分账比例: ratio={}", splitRatio);
|
||||
|
||||
// 设置其他请求字段
|
||||
String accountType = JsonUtil.getJsonValueSmart(reqDataJson, "accountType");
|
||||
String transType = JsonUtil.getJsonValueSmart(reqDataJson, "transType");
|
||||
String notifyUrl = JsonUtil.getJsonValueSmart(reqDataJson, "notifyUrl");
|
||||
String receiveNotifyUrl = JsonUtil.getJsonValueSmart(reqDataJson, "complete_notify_url");
|
||||
String merchantNo = JsonUtil.getJsonValueSmart(reqDataJson, "merchantNo");
|
||||
String termNo = JsonUtil.getJsonValueSmart(reqDataJson, "termNo");
|
||||
|
||||
record.setAccount_type(accountType);
|
||||
record.setTrans_type(transType);
|
||||
record.setNotify_url(notifyUrl);
|
||||
record.setReceive_notify_url(receiveNotifyUrl);
|
||||
record.setLkl_merchant_no(merchantNo);
|
||||
record.setLkl_term_no(termNo);
|
||||
|
||||
log.debug("[拉卡拉订单保存] 设置请求字段: accountType={} transType={} merchantNo={} termNo={}",
|
||||
accountType, transType, merchantNo, termNo);
|
||||
|
||||
String reqJsonStr = JSONUtil.toJsonStr(reqDataJson);
|
||||
record.setLkl_req(reqJsonStr);
|
||||
log.debug("[拉卡拉订单保存] 设置请求JSON,长度={}", reqJsonStr != null ? reqJsonStr.length() : 0);
|
||||
|
||||
// 设置响应内容
|
||||
String logNo = JsonUtil.getJsonValueSmart(respDataJson, "log_no");
|
||||
record.setLkl_log_no(logNo);
|
||||
log.debug("[拉卡拉订单保存] 设置流水号: logNo={}", logNo);
|
||||
|
||||
String tradeNo = JsonUtil.getJsonValueSmart(respDataJson, "trade_no");
|
||||
record.setLkl_trade_no(tradeNo);
|
||||
log.debug("[拉卡拉订单保存] 设置交易号: tradeNo={}", tradeNo);
|
||||
|
||||
String respJsonStr = JSONUtil.toJsonStr(respDataJson);
|
||||
record.setLkl_resp(respJsonStr);
|
||||
log.debug("[拉卡拉订单保存] 设置响应JSON,长度={}", respJsonStr != null ? respJsonStr.length() : 0);
|
||||
|
||||
log.debug("[拉卡拉订单保存] 调用addOrUpdateByStoreOrder方法保存记录: orderId={}", orderId);
|
||||
Boolean result = addOrUpdateByStoreOrder(record);
|
||||
if (result) {
|
||||
log.info("[拉卡拉订单保存] 拉卡拉订单记录保存成功: orderId={}", orderId);
|
||||
} else {
|
||||
log.error("[拉卡拉订单保存] 拉卡拉订单记录保存失败: orderId={}", orderId);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("新增拉卡拉支付记录出错", e);
|
||||
log.error("[拉卡拉订单保存] 新增拉卡拉支付记录出错", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据拉卡拉支付异步回调通知数据保存或更新拉卡拉订单记录
|
||||
* 该方法用于处理拉卡拉支付平台发送的支付通知,将通知中的数据保存到shop_order_lkl表中
|
||||
*
|
||||
* @param lklPayNotifyDataJson 拉卡拉支付通知的JSON数据
|
||||
* @return Boolean 是否保存或更新成功
|
||||
*/
|
||||
@Override
|
||||
public Boolean addOrUpdateByLklPayNotifyDataJson(JSONObject lklPayNotifyDataJson) {
|
||||
if (lklPayNotifyDataJson == null) {
|
||||
log.warn("参数为空,无法保存拉卡拉支付通知记录");
|
||||
log.warn("[拉卡拉订单更新] 参数为空,无法保存拉卡拉支付通知记录");
|
||||
return false;
|
||||
}
|
||||
|
||||
log.debug("拉卡拉支付通知回调 body:{}", lklPayNotifyDataJson);
|
||||
log.debug("[拉卡拉订单更新] 开始处理拉卡拉支付通知回调: {}", lklPayNotifyDataJson);
|
||||
try {
|
||||
|
||||
// 获取订单ID
|
||||
String orderId = lklPayNotifyDataJson.getStr("out_trade_no");
|
||||
if (StringUtils.isBlank(orderId)) {
|
||||
log.warn("订单ID为空,无法保存拉卡拉支付通知记录");
|
||||
log.warn("[拉卡拉订单更新] 订单ID为空,无法保存拉卡拉支付通知记录");
|
||||
return false;
|
||||
}
|
||||
log.debug("[拉卡拉订单更新] 解析订单ID: {}", orderId);
|
||||
|
||||
ShopOrderLkl record = new ShopOrderLkl();
|
||||
record.setOrder_id(orderId);
|
||||
@ -220,24 +281,69 @@ public class ShopOrderLklServiceImpl extends BaseServiceImpl<ShopOrderLklMapper,
|
||||
// 设置必填字段并校验
|
||||
String logNo = lklPayNotifyDataJson.getStr("log_no");
|
||||
if (StringUtils.isBlank(logNo)) {
|
||||
log.warn("log_no 为空,无法保存拉卡拉支付通知记录");
|
||||
log.warn("[拉卡拉订单更新] log_no为空,无法保存拉卡拉支付通知记录: orderId={}", orderId);
|
||||
return false;
|
||||
}
|
||||
record.setLkl_log_no(logNo);
|
||||
log.debug("[拉卡拉订单更新] 设置流水号: {}", logNo);
|
||||
|
||||
// 设置日期字段
|
||||
record.setLkl_log_date(DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyy-MM-dd"));
|
||||
String logDate = DateTimeUtils.formatDateTime(LocalDateTime.now(), "yyyyMMdd");
|
||||
record.setLkl_log_date(logDate);
|
||||
log.debug("[拉卡拉订单更新] 设置日期字段: {}", logDate);
|
||||
|
||||
// 设置可选字段
|
||||
record.setLkl_trade_no(lklPayNotifyDataJson.getStr("trade_no"));
|
||||
record.setTrade_status(lklPayNotifyDataJson.getStr("trade_status"));
|
||||
String tradeNo = lklPayNotifyDataJson.getStr("trade_no");
|
||||
String tradeStatus = lklPayNotifyDataJson.getStr("trade_status");
|
||||
String accTradeNo = lklPayNotifyDataJson.getStr("acc_trade_no");
|
||||
record.setLkl_trade_no(tradeNo);
|
||||
record.setTrade_status(tradeStatus);
|
||||
record.setWx_transaction_id(accTradeNo); //账户端交易订单号 对应微信的用户交易单号
|
||||
log.debug("[拉卡拉订单更新] 设置可选字段: tradeNo={} accTradeNo={} tradeStatus={}", tradeNo, accTradeNo, tradeStatus);
|
||||
|
||||
// 新增的订单字段,lkl_sub_log_no,out_separate_no,split_amt 四个字段无值,就给主单的值
|
||||
String outSeparateNo = JsonUtil.getJsonValueSmart(lklPayNotifyDataJson, "out_separate_no");
|
||||
if (CheckUtil.isEmpty(outSeparateNo)) {
|
||||
outSeparateNo = orderId;
|
||||
}
|
||||
record.setOut_separate_no(outSeparateNo);
|
||||
|
||||
String lklSubTradeNo = JsonUtil.getJsonValueSmart(lklPayNotifyDataJson, "lkl_sub_trade_no");
|
||||
if (CheckUtil.isEmpty(lklSubTradeNo)) {
|
||||
lklSubTradeNo = tradeNo;
|
||||
}
|
||||
record.setLkl_sub_trade_no(lklSubTradeNo);
|
||||
|
||||
String lklSubLogNo = JsonUtil.getJsonValueSmart(lklPayNotifyDataJson, "lkl_sub_log_no");
|
||||
if (CheckUtil.isEmpty(lklSubLogNo)) {
|
||||
lklSubLogNo = logNo;
|
||||
}
|
||||
record.setLkl_sub_log_no(lklSubLogNo);
|
||||
|
||||
Integer splitAmt = JsonUtil.getJsonValueSmart(lklPayNotifyDataJson, "split_amt", Integer.class);
|
||||
if (CheckUtil.isEmpty(splitAmt)) {
|
||||
splitAmt = record.getTotal_amt();
|
||||
}
|
||||
record.setSplit_amt(splitAmt);
|
||||
|
||||
log.debug("[拉卡拉订单更新] 设置订单字段: outSeparateNo={} lklSubTradeNo={} lklSubLogNo={} splitAmt={}",
|
||||
outSeparateNo, lklSubTradeNo, lklSubLogNo, splitAmt);
|
||||
|
||||
// 安全地设置响应内容
|
||||
record.setLkl_notify_resp(JSONUtil.toJsonStr(lklPayNotifyDataJson));
|
||||
String notifyResp = JSONUtil.toJsonStr(lklPayNotifyDataJson);
|
||||
record.setLkl_notify_resp(notifyResp);
|
||||
log.debug("[拉卡拉订单更新] 设置通知响应内容,长度={}", notifyResp != null ? notifyResp.length() : 0);
|
||||
|
||||
return addOrUpdateByStoreOrder(record);
|
||||
log.debug("[拉卡拉订单更新] 调用addOrUpdateByStoreOrder方法保存记录: orderId={}", orderId);
|
||||
Boolean result = addOrUpdateByStoreOrder(record);
|
||||
if (result) {
|
||||
log.info("[拉卡拉订单更新] 拉卡拉订单记录保存成功: orderId={}", orderId);
|
||||
} else {
|
||||
log.error("[拉卡拉订单更新] 拉卡拉订单记录保存失败: orderId={}", orderId);
|
||||
}
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("修改拉卡拉支付通知记录出错", e);
|
||||
log.error("[拉卡拉订单更新] 修改拉卡拉支付通知记录出错", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -273,4 +379,221 @@ public class ShopOrderLklServiceImpl extends BaseServiceImpl<ShopOrderLklMapper,
|
||||
return CollectionUtil.newArrayList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据店铺Id、订单编号查询一条记录
|
||||
*
|
||||
* @param storeId
|
||||
* @param orderId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ShopOrderLkl getByStoreIdAndOrderId(Integer storeId, String orderId) {
|
||||
if (CheckUtil.isEmpty(storeId) || StringUtils.isBlank(orderId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
QueryWrapper<ShopOrderLkl> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("store_id", storeId).eq("order_id", orderId).orderByAsc("id");
|
||||
|
||||
if (CheckUtil.isEmpty(storeId)) {
|
||||
queryWrapper.eq("store_id", storeId);
|
||||
}
|
||||
|
||||
return findOne(queryWrapper);
|
||||
} catch (Exception e) {
|
||||
log.error("getByStoreIdAndOrderId 查询异常, orderId: {}, storeId: {}", orderId, storeId, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户号、子单交易流水号、子单对账单流水号查询一条记录
|
||||
*
|
||||
* @param lklMerchantNo 拉卡拉商户号
|
||||
* @param lklSubTradeNo 原拉卡拉交易流水号
|
||||
* @param lklSubLogNo 原拉卡拉对账单流水号
|
||||
* @return ShopOrderLkl 拉卡拉订单记录
|
||||
*/
|
||||
@Override
|
||||
public ShopOrderLkl getByLklMchNoAndSubTradeNoAndSubLogNo(String lklMerchantNo, String lklSubTradeNo, String lklSubLogNo) {
|
||||
// 检查参数是否全部为空
|
||||
if (StringUtils.isAllBlank(lklMerchantNo, lklSubTradeNo, lklSubLogNo)) {
|
||||
log.warn("[拉卡拉订单查询] 参数校验失败:所有查询条件均为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug("[拉卡拉订单查询] 开始查询, 商户号={}, 子订单交易号={}, 子订单对账流水号={}",
|
||||
lklMerchantNo, lklSubTradeNo, lklSubLogNo);
|
||||
|
||||
QueryWrapper<ShopOrderLkl> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.orderByAsc("id");
|
||||
|
||||
// 根据非空参数构建查询条件
|
||||
if (StrUtil.isNotBlank(lklMerchantNo)) {
|
||||
queryWrapper.eq("lkl_merchant_no", lklMerchantNo);
|
||||
log.debug("[拉卡拉订单查询] 添加商户号查询条件: {}", lklMerchantNo);
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(lklSubTradeNo)) {
|
||||
queryWrapper.eq("lkl_sub_trade_no", lklSubTradeNo);
|
||||
log.debug("[拉卡拉订单查询] 添加子订单交易号查询条件: {}", lklSubTradeNo);
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(lklSubLogNo)) {
|
||||
queryWrapper.eq("lkl_sub_log_no", lklSubLogNo);
|
||||
log.debug("[拉卡拉订单查询] 添加子订单对账流水号查询条件: {}", lklSubLogNo);
|
||||
}
|
||||
|
||||
ShopOrderLkl result = findOne(queryWrapper);
|
||||
|
||||
log.debug("[拉卡拉订单查询] 查询完成, 商户号={}, 子订单交易号={}, 子订单对账流水号={}, 查询结果={}",
|
||||
lklMerchantNo, lklSubTradeNo, lklSubLogNo, result != null);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉订单查询] 系统异常, 子订单商户号={}, 交易号={}, 子订单对账流水号={}",
|
||||
lklMerchantNo, lklSubTradeNo, lklSubLogNo, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据商户号、交易流水号、对账单流水号查询一条记录
|
||||
*
|
||||
* @param lklMerchantNo 拉卡拉商户号
|
||||
* @param lklReceiveTradeNo 拉卡拉确认收货交易流水号
|
||||
* @param lklReceiveLogNo 拉卡拉确认收货对账单流水号
|
||||
* @return ShopOrderLkl 拉卡拉订单记录
|
||||
*/
|
||||
@Override
|
||||
public ShopOrderLkl getByLklMchNoAndReceiveTradeNoAndReceiveLogNo(String lklMerchantNo, String lklReceiveTradeNo, String lklReceiveLogNo) {
|
||||
// 检查参数是否全部为空
|
||||
if (StringUtils.isAllBlank(lklMerchantNo, lklReceiveTradeNo, lklReceiveLogNo)) {
|
||||
log.warn("[拉卡拉订单查询] 参数校验失败:所有查询条件均为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug("[拉卡拉订单查询] 开始查询, 商户号={}, 确认收货交易号={}, 确认收货对账流水号={}",
|
||||
lklMerchantNo, lklReceiveTradeNo, lklReceiveLogNo);
|
||||
|
||||
QueryWrapper<ShopOrderLkl> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.orderByAsc("id");
|
||||
|
||||
// 根据非空参数构建查询条件
|
||||
if (StrUtil.isNotBlank(lklMerchantNo)) {
|
||||
queryWrapper.eq("lkl_merchant_no", lklMerchantNo);
|
||||
log.debug("[拉卡拉订单查询] 添加商户号查询条件: lklMerchantNo={}", lklMerchantNo);
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(lklReceiveTradeNo)) {
|
||||
queryWrapper.eq("lkl_receive_trade_no", lklReceiveTradeNo);
|
||||
log.debug("[拉卡拉订单查询] 添加交易号查询条件: lklReceiveTradeNo={}", lklReceiveTradeNo);
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(lklReceiveLogNo)) {
|
||||
queryWrapper.eq("lkl_receive_log_no", lklReceiveLogNo);
|
||||
log.debug("[拉卡拉订单查询] 添加收货流水号查询条件: lklReceiveLogNo={}", lklReceiveLogNo);
|
||||
}
|
||||
|
||||
ShopOrderLkl result = findOne(queryWrapper);
|
||||
|
||||
log.debug("[拉卡拉订单查询] 查询完成, 商户号={}, 交易号={}, 流水号={}, 查询结果={}",
|
||||
lklMerchantNo, lklReceiveTradeNo, lklReceiveLogNo, result != null);
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("[拉卡拉订单查询] 系统异常, 商户号={}, 交易号={}, 流水号={}",
|
||||
lklMerchantNo, lklReceiveTradeNo, lklReceiveLogNo, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据确认收货对账单流水号更新分账状态
|
||||
*
|
||||
* @param lklReceiveLogNo 拉卡拉确认收货对账单流水号
|
||||
* @param separateStatus 分账状态:1-已分账;2-未分账;3-分账已失败;
|
||||
* @param separateRemark 分账问题备注;
|
||||
* @return 更新结果 true-成功 false-失败
|
||||
*/
|
||||
@Override
|
||||
public Boolean updateSeparateStatusByReceiveLogNo(String lklReceiveLogNo, Integer separateStatus, String separateRemark) {
|
||||
// 检查参数是否全部为空
|
||||
if (StringUtils.isBlank(lklReceiveLogNo) || separateStatus == null) {
|
||||
log.warn("[更新分账状态] 参数校验失败:缺少必要参数, lklReceiveLogNo={}, separateStatus={}", lklReceiveLogNo, separateStatus);
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
log.info("[更新分账状态] 开始更新分账状态, lklReceiveLogNo={}, separateStatus={}", lklReceiveLogNo, separateStatus);
|
||||
|
||||
UpdateWrapper<ShopOrderLkl> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("lkl_receive_log_no", lklReceiveLogNo);
|
||||
updateWrapper.set("separate_status", separateStatus);
|
||||
if (StrUtil.isNotBlank(separateRemark)) {
|
||||
updateWrapper.set("separate_remark", separateRemark);
|
||||
}
|
||||
|
||||
boolean result = update(updateWrapper);
|
||||
|
||||
if (result) {
|
||||
log.info("[更新分账状态] 分账状态更新成功, lklReceiveLogNo={}, separateStatus={}", lklReceiveLogNo, separateStatus);
|
||||
} else {
|
||||
log.warn("[更新分账状态] 分账状态更新失败,未找到匹配记录, lklReceiveLogNo={}, separateStatus={}", lklReceiveLogNo, separateStatus);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("[更新分账状态] 分账状态更新异常, lklReceiveLogNo={}, separateStatus={}", lklReceiveLogNo, separateStatus, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全更新拉卡拉订单记录
|
||||
* <p>
|
||||
* 该方法用于更新拉卡拉订单记录,不会抛出异常,而是在出现错误时返回false。
|
||||
* </p>
|
||||
*
|
||||
* @param record 拉卡拉订单记录
|
||||
* @return 更新结果 true-成功 false-失败
|
||||
*/
|
||||
@Override
|
||||
public Boolean safeUpdate(ShopOrderLkl record) {
|
||||
// 参数校验
|
||||
if (record == null) {
|
||||
log.warn("[安全更新] 参数校验失败:记录不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (record.getId() == null) {
|
||||
log.warn("[安全更新] 参数校验失败:记录ID不能为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
log.debug("[安全更新] 开始更新拉卡拉订单记录, orderId={}, id={}",
|
||||
record.getOrder_id(), record.getId());
|
||||
|
||||
boolean result = updateById(record);
|
||||
|
||||
if (result) {
|
||||
log.info("[安全更新] 拉卡拉订单记录更新成功, orderId={}, id={}",
|
||||
record.getOrder_id(), record.getId());
|
||||
} else {
|
||||
log.warn("[安全更新] 拉卡拉订单记录更新失败, orderId={}, id={}",
|
||||
record.getOrder_id(), record.getId());
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("[安全更新] 拉卡拉订单记录更新异常, orderId={}, id={}",
|
||||
record.getOrder_id(), record.getId(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -182,73 +182,91 @@ public class OssServiceImpl implements OssService {
|
||||
String ossUrl = null;
|
||||
Integer uploadType = configService.getConfig("upload", 1);
|
||||
|
||||
if (uploadType.equals(1)) {
|
||||
ossUrl = ConfigConstant.URL_BASE + "/admin/oss/upload/" + dir + "/" + uploadName; // 文件本地路径
|
||||
} else if (uploadType.equals(2)) {
|
||||
// oss 服务
|
||||
try {
|
||||
ossUrl = uploadObject2OSS(new File(uploadPath), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat("/").concat(uploadName), null, null, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("文件上传失败!", e);
|
||||
return null;
|
||||
}
|
||||
} else if (uploadType.equals(4)) {
|
||||
ossUrl = uploadObject4OSS(new File(uploadPath), TENGXUN_DEFAULT_DIR.concat("/").concat(dir).concat("/").concat(uploadName));
|
||||
}
|
||||
|
||||
String media_duration = "";
|
||||
try {
|
||||
media_duration = VideoUtil.getFormatDuration(uploadPath);
|
||||
} catch (IOException e) {
|
||||
throw new ApiException(String.format(I18nUtil._("解析音视频时长异常!url【%s】"), uploadPath));
|
||||
}
|
||||
|
||||
//截图
|
||||
//todo 放入upload中
|
||||
String thumb_file_url = "";
|
||||
if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) {
|
||||
// String cover_path = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||
String cover_upname = uploadName.replace("." + VideoUtil.getVideoFormat(uploadName), ".jpg");
|
||||
|
||||
String floder = ALIYUN_OSS_DIR_PREFIX.concat("/") + dir + "/video/frame1/";
|
||||
thumb_file_url = videoUtil.getVideoCoverV2(ossUrl, floder, cover_upname);
|
||||
/*if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) {
|
||||
if (uploadType.equals(1)) {
|
||||
ossUrl = ConfigConstant.URL_BASE + "/admin/oss/upload/" + dir + "/" + uploadName; // 文件本地路径
|
||||
} else if (uploadType.equals(2)) {
|
||||
// oss 服务
|
||||
try {
|
||||
thumb_file_url = uploadObject2OSS(new File(cover_path), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(cover_upname), null, null, null);
|
||||
ossUrl = uploadObject2OSS(new File(uploadPath), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat("/").concat(uploadName), null, null, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("文件上传失败!", e);
|
||||
return null;
|
||||
}
|
||||
} else if (uploadType.equals(4)) {
|
||||
ossUrl = uploadObject4OSS(new File(uploadPath), TENGXUN_DEFAULT_DIR.concat("/").concat(dir).concat("/").concat(uploadName));
|
||||
}
|
||||
|
||||
String media_duration = "";
|
||||
try {
|
||||
media_duration = VideoUtil.getFormatDuration(uploadPath);
|
||||
} catch (IOException e) {
|
||||
throw new ApiException(String.format(I18nUtil._("解析音视频时长异常!url【%s】"), uploadPath));
|
||||
}
|
||||
|
||||
//截图
|
||||
//todo 放入upload中
|
||||
String thumb_file_url = "";
|
||||
if (VideoUtil.videoAllowFiles.contains(VideoUtil.getVideoFormat(uploadPath))) {
|
||||
// String cover_path = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||
String cover_upname = uploadName.replace("." + VideoUtil.getVideoFormat(uploadName), ".jpg");
|
||||
|
||||
String floder = ALIYUN_OSS_DIR_PREFIX.concat("/") + dir + "/video/frame1/";
|
||||
try {
|
||||
thumb_file_url = videoUtil.getVideoCoverV2(ossUrl, floder, cover_upname);
|
||||
} catch (Exception e) {
|
||||
logger.error("获取视频封面失败: ", e);
|
||||
thumb_file_url = "";
|
||||
}
|
||||
} else {
|
||||
thumb_file_url = "";
|
||||
}*/
|
||||
/*if (VideoUtil.getVideoCover(uploadPath, cover_path, 1, "375*667")) {
|
||||
try {
|
||||
thumb_file_url = uploadObject2OSS(new File(cover_path), ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(cover_upname), null, null, null);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
thumb_file_url = "";
|
||||
}
|
||||
} else {
|
||||
thumb_file_url = "";
|
||||
}*/
|
||||
}
|
||||
|
||||
// 保存文件信息
|
||||
MediaDTO mediaDTO = new MediaDTO();
|
||||
String name = file.getOriginalFilename();
|
||||
long size = file.getSize();
|
||||
|
||||
mediaDTO.setMedia_name(name);
|
||||
mediaDTO.setMedia_alt(name);
|
||||
mediaDTO.setMedia_size(size);
|
||||
mediaDTO.setMedia_url(ossUrl);
|
||||
mediaDTO.setMedia_order(1);
|
||||
mediaDTO.setMedia_path("/".concat(dir).concat("/").concat(uploadName));
|
||||
mediaDTO.setMedia_domain(getOssUrlPrefix().concat(ALIYUN_OSS_DIR_PREFIX));
|
||||
mediaDTO.setMedia_time(new Date());
|
||||
mediaDTO.setMedia_desc("");
|
||||
mediaDTO.setMedia_duration(media_duration);
|
||||
|
||||
shopStoreMediaService.saveMedia(mediaDTO, user);
|
||||
Map result = new HashMap<>();
|
||||
result.put("media_duration", media_duration);
|
||||
result.put("media_url", ossUrl);
|
||||
result.put("thumb", thumb_file_url);
|
||||
|
||||
return result;
|
||||
} finally {
|
||||
// 删除临时文件
|
||||
try {
|
||||
File tempFile = new File(uploadPath);
|
||||
if (tempFile.exists()) {
|
||||
tempFile.delete();
|
||||
logger.info("临时文件已删除: {}", uploadPath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("删除临时文件失败: {}", uploadPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存文件信息
|
||||
MediaDTO mediaDTO = new MediaDTO();
|
||||
String name = file.getOriginalFilename();
|
||||
long size = file.getSize();
|
||||
|
||||
mediaDTO.setMedia_name(name);
|
||||
mediaDTO.setMedia_alt(name);
|
||||
mediaDTO.setMedia_size(size);
|
||||
mediaDTO.setMedia_url(ossUrl);
|
||||
mediaDTO.setMedia_order(1);
|
||||
mediaDTO.setMedia_path("/".concat(dir).concat("/").concat(fileName));
|
||||
mediaDTO.setMedia_domain(getOssUrlPrefix().concat(ALIYUN_OSS_DIR_PREFIX));
|
||||
mediaDTO.setMedia_time(new Date());
|
||||
mediaDTO.setMedia_desc("");
|
||||
mediaDTO.setMedia_duration(media_duration);
|
||||
|
||||
shopStoreMediaService.saveMedia(mediaDTO, user);
|
||||
Map result = new HashMap<>();
|
||||
result.put("media_duration", media_duration);
|
||||
result.put("media_url", ossUrl);
|
||||
result.put("thumb", thumb_file_url);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Map upload(MultipartFile file, UserDto user) {
|
||||
@ -580,7 +598,7 @@ public class OssServiceImpl implements OssService {
|
||||
ObjectListing objectListing = ossCli.listObjects(listObjectsRequest);
|
||||
List<String> commonPrefixes = objectListing.getCommonPrefixes();
|
||||
logger.info(JSONUtil.toJsonStr(commonPrefixes));
|
||||
return commonPrefixes;
|
||||
return commonPrefixes;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -632,8 +650,7 @@ public class OssServiceImpl implements OssService {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param ossFolder 如folder/example.txt
|
||||
* @param ossFolder 如folder/example.txt
|
||||
* @param localFolder 如/path/to/local/example.txt
|
||||
* @return
|
||||
*/
|
||||
|
||||
@ -16,6 +16,7 @@ import org.springframework.stereotype.Component;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -24,14 +25,7 @@ import java.util.concurrent.TimeUnit;
|
||||
@Component
|
||||
public class VideoUtil {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(VideoUtil.class);
|
||||
|
||||
@Value("${aliyun.oss.dir.prefix}")
|
||||
private String ALIYUN_OSS_DIR_PREFIX;
|
||||
|
||||
@Autowired
|
||||
private OssService ossService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(VideoUtil.class);
|
||||
public static List<String> videoAllowFiles = new ArrayList<String>() {{
|
||||
add("flv");
|
||||
add("swf");
|
||||
@ -51,6 +45,10 @@ public class VideoUtil {
|
||||
// add("wav");
|
||||
// add("mid");
|
||||
}};
|
||||
@Value("${aliyun.oss.dir.prefix}")
|
||||
private String ALIYUN_OSS_DIR_PREFIX;
|
||||
@Autowired
|
||||
private OssService ossService;
|
||||
|
||||
/**
|
||||
* 得到语音或视频文件时长,单位秒 并格式化
|
||||
@ -213,8 +211,7 @@ public class VideoUtil {
|
||||
int mm = (temp % 3600) / 60;
|
||||
int ss = (temp % 3600) % 60;
|
||||
|
||||
return hh != 0 ? ((hh < 10 ? ("0" + hh) : hh) + ":") : "" +
|
||||
(mm < 10 ? ("0" + mm) : mm) + ":" +
|
||||
return hh != 0 ? ((hh < 10 ? ("0" + hh) : hh) + ":") : (mm < 10 ? ("0" + mm) : mm) + ":" +
|
||||
(ss < 10 ? ("0" + ss) : ss);
|
||||
}
|
||||
|
||||
@ -227,24 +224,40 @@ public class VideoUtil {
|
||||
* @return
|
||||
*/
|
||||
public String getVideoCoverV2(String video, String dir, String uploadName) {
|
||||
InputStream inputStream = OssUtils.urlToInputSteam(video);
|
||||
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);
|
||||
try {
|
||||
grabber.start();
|
||||
Frame frame = grabber.grabImage();
|
||||
Java2DFrameConverter converter = new Java2DFrameConverter();
|
||||
BufferedImage bufferedImage = converter.convert(frame);
|
||||
Map map = OssUtils.toStreamMap(bufferedImage);
|
||||
grabber.stop();
|
||||
InputStream stream = (InputStream) map.get("stream");
|
||||
Long file_size = Convert.toLong(map.get("file_size"));
|
||||
InputStream inputStream = OssUtils.urlToInputSteam(video);
|
||||
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputStream);
|
||||
try {
|
||||
grabber.start();
|
||||
Frame frame = grabber.grabImage();
|
||||
Java2DFrameConverter converter = new Java2DFrameConverter();
|
||||
BufferedImage bufferedImage = converter.convert(frame);
|
||||
Map map = OssUtils.toStreamMap(bufferedImage);
|
||||
grabber.stop();
|
||||
InputStream stream = (InputStream) map.get("stream");
|
||||
Long file_size = Convert.toLong(map.get("file_size"));
|
||||
|
||||
// 上传至oos,返回文件地址
|
||||
return ossService.uploadObject2OSS(null, ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(uploadName), stream, uploadName, file_size);
|
||||
} catch (IOException e) {
|
||||
logger.error("失败原因:", e);
|
||||
// 上传至oos,返回文件地址
|
||||
return ossService.uploadObject2OSS(null, ALIYUN_OSS_DIR_PREFIX.concat("/").concat(dir).concat(uploadName), stream, uploadName, file_size);
|
||||
} catch (IOException e) {
|
||||
logger.error("处理视频封面失败:", e);
|
||||
} finally {
|
||||
try {
|
||||
if (grabber != null) {
|
||||
grabber.stop();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("关闭视频抓取器失败:", e);
|
||||
}
|
||||
}
|
||||
} catch (UnsatisfiedLinkError e) {
|
||||
logger.error("JavaCV本地库加载失败,请检查平台依赖配置:", e);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
logger.error("JavaCV类初始化失败,请检查依赖配置:", e);
|
||||
} catch (Exception e) {
|
||||
logger.error("获取视频封面时发生未知异常:", e);
|
||||
}
|
||||
return null;
|
||||
return "";
|
||||
}
|
||||
|
||||
}
|
||||
@ -255,7 +268,7 @@ class InputStreamRunnable extends Thread {
|
||||
|
||||
public InputStreamRunnable(InputStream is, String _type) {
|
||||
try {
|
||||
bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), "UTF-8"));
|
||||
bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), StandardCharsets.UTF_8));
|
||||
type = _type;
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
|
||||
@ -59,6 +59,7 @@ import com.suisung.mall.shop.store.service.*;
|
||||
import com.suisung.mall.shop.user.service.ShopUserFavoritesItemService;
|
||||
import com.suisung.mall.shop.user.service.ShopUserProductBrowseService;
|
||||
import io.seata.spring.annotation.GlobalTransactional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -90,6 +91,7 @@ import static com.suisung.mall.common.utils.I18nUtil._;
|
||||
* @author Xinze
|
||||
* @since 2021-04-07
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ShopProductItemServiceImpl extends BaseServiceImpl<ShopProductItemMapper, ShopProductItem> implements ShopProductItemService {
|
||||
|
||||
@ -2237,17 +2239,34 @@ public class ShopProductItemServiceImpl extends BaseServiceImpl<ShopProductItemM
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定库存
|
||||
* 锁定SKU库存
|
||||
*
|
||||
* @param itemId
|
||||
* @param cart_quantity
|
||||
* @return
|
||||
* @param itemId 商品SKU ID
|
||||
* @param cartQuantity 购物车商品数量
|
||||
* @return 影响的行数,1表示锁定成功,0表示锁定失败(库存不足)
|
||||
*/
|
||||
@Override
|
||||
public int lockSkuStock(Long itemId, int cart_quantity) {
|
||||
int flag = this.baseMapper.lockSkuStock(itemId, cart_quantity);
|
||||
public int lockSkuStock(Long itemId, int cartQuantity) {
|
||||
// 参数校验
|
||||
if (itemId == null || cartQuantity <= 0) {
|
||||
log.warn("锁定SKU库存参数异常: itemId={}, cartQuantity={}", itemId, cartQuantity);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 调用Mapper方法锁定库存
|
||||
int affectedRows = this.baseMapper.lockSkuStock(itemId, cartQuantity);
|
||||
|
||||
// 清理SKU库存缓存
|
||||
cleanSkuStockCache(itemId);
|
||||
return flag;
|
||||
|
||||
// 记录库存锁定操作日志
|
||||
if (affectedRows > 0) {
|
||||
log.debug("SKU库存锁定成功: itemId={}, cartQuantity={}, affectedRows={}", itemId, cartQuantity, affectedRows);
|
||||
} else {
|
||||
log.warn("SKU库存锁定失败,可能库存不足: itemId={}, cartQuantity={}", itemId, cartQuantity);
|
||||
}
|
||||
|
||||
return affectedRows;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -118,7 +118,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
|
||||
ShopMchEntry shopMchEntry;
|
||||
if (ObjectUtil.isEmpty(mchId)) {
|
||||
shopMchEntry = shopMchEntryService.shopMerchEntryByStoreId(storeId);
|
||||
shopMchEntry = shopMchEntryService.getShopMerchEntryByStoreId(storeId);
|
||||
} else {
|
||||
shopMchEntry = shopMchEntryService.shopMerchEntryById(mchId);
|
||||
}
|
||||
@ -488,6 +488,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
ShopStoreSfOrder shopStoreSfOrder = JSONUtil.toBean(sfExpressApiRes.get("result").toString(), ShopStoreSfOrder.class);
|
||||
shopStoreSfOrder.setDev_id(sfCreateOrderReq.getDev_id());
|
||||
shopStoreSfOrder.setOrder_status(SFExpressConstant.Cons_CreatedOrder);
|
||||
shopStoreSfOrder.setShop_id(sfCreateOrderReq.getShop_id());
|
||||
shopStoreSfOrder.setStatus_desc("已创建顺丰同城订单");
|
||||
|
||||
// 下单成功,保存顺丰同城订单记录到 shop_store_sf_order 表里
|
||||
@ -808,66 +809,98 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
|
||||
/**
|
||||
* 接收顺丰原因订单取消回调
|
||||
* <p>
|
||||
* 该方法用于处理顺丰同城配送订单取消的回调通知。当顺丰因为某种原因取消订单时,
|
||||
* 会调用此接口通知商城系统,商城系统需要更新订单状态为已取消,并执行相关业务逻辑。
|
||||
* </p>
|
||||
*
|
||||
* @param jsonData
|
||||
* @param sign
|
||||
* @return
|
||||
* @param jsonData 包含订单取消信息的JSON数据
|
||||
* @param sign 请求签名,用于验证请求的合法性
|
||||
* @return ThirdApiRes 处理结果响应对象
|
||||
* <ul>
|
||||
* <li>成功: {"error_code": 0, "reason": "success"}</li>
|
||||
* <li>失败: {"error_code": 错误码, "reason": "错误信息"}</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Override
|
||||
public ThirdApiRes receiveCancelOrderNotify(String jsonData, String sign) {
|
||||
// 参数校验
|
||||
if (StrUtil.isBlank(jsonData) || StrUtil.isBlank(sign)) {
|
||||
logger.warn("[顺丰订单取消回调通知] 缺少必要参数: jsonData或sign为空");
|
||||
return new ThirdApiRes().fail(1003, "缺少必要参数!");
|
||||
}
|
||||
|
||||
// 签名校验
|
||||
if (!checkOpenSign(sign, jsonData)) {
|
||||
logger.warn("[顺丰订单取消回调通知] 请求签名sign校验失败");
|
||||
return new ThirdApiRes().fail(2002, "请求签名sign校验失败!");
|
||||
}
|
||||
|
||||
logger.info("接收顺丰原因订单取消回调返回的 JSON 数据:{}", jsonData);
|
||||
logger.info("[顺丰订单取消回调通知] 接收回调数据: {}", jsonData);
|
||||
|
||||
// 更改顺丰同城订单状态
|
||||
ShopStoreSfOrder shopStoreSfOrder = toShopStoreSfOrder(jsonData);
|
||||
|
||||
String shopOrderId = shopStoreSfOrder.getShop_order_id();
|
||||
String sfOrderId = shopStoreSfOrder.getSf_order_id();
|
||||
// 判断订单的状态,是否已经取消了?已取消,不再执行
|
||||
ShopStoreSfOrder shopStoreSfOrderExist = shopStoreSfOrderService.getBySfOrderId(sfOrderId);
|
||||
if (shopStoreSfOrderExist != null && shopStoreSfOrderExist.getOrder_status() != null
|
||||
&& (shopStoreSfOrderExist.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELED) ||
|
||||
(shopStoreSfOrderExist.getOrder_status().equals(StateCode.SF_ORDER_STATUS_CANCELING)))) {
|
||||
return new ThirdApiRes().success("success");
|
||||
}
|
||||
|
||||
Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder);
|
||||
if (!success) {
|
||||
return new ThirdApiRes().fail(-1, "状态处理失败!");
|
||||
}
|
||||
|
||||
// 重要:更改商城订单状态为:已取消,注意事务问题
|
||||
success = shopOrderReturnService.sfExpressExpiredForceRefund(shopOrderId);
|
||||
if (!success) {
|
||||
return new ThirdApiRes().fail(-1, "取消订单业务处理失败!");
|
||||
}
|
||||
|
||||
// 获取顺丰同城的物流轨迹
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("order_id", sfOrderId);
|
||||
ThirdApiRes feedRes = listOrderFeed(params);
|
||||
if (feedRes != null && feedRes.getError_code().equals(0)) {
|
||||
JSONObject result = JSONUtil.parseObj(feedRes.getResult());
|
||||
if (result != null && result.get("feed") != null) {
|
||||
shopStoreSfOrder.setFeed(JSONUtil.toJsonStr(result.get("feed")));
|
||||
try {
|
||||
// 解析并更新顺丰同城订单状态
|
||||
ShopStoreSfOrder shopStoreSfOrder = toShopStoreSfOrder(jsonData);
|
||||
if (shopStoreSfOrder == null) {
|
||||
logger.error("[顺丰订单取消回调通知] 解析订单数据失败: jsonData={}", jsonData);
|
||||
return new ThirdApiRes().fail(-1, "订单数据解析失败!");
|
||||
}
|
||||
|
||||
String shopOrderId = shopStoreSfOrder.getShop_order_id();
|
||||
String sfOrderId = shopStoreSfOrder.getSf_order_id();
|
||||
|
||||
logger.info("[顺丰订单取消回调通知] 处理订单取消: shopOrderId={} sfOrderId={}", shopOrderId, sfOrderId);
|
||||
|
||||
// 判断订单的状态,是否已经取消了?已取消,不再执行
|
||||
ShopStoreSfOrder shopStoreSfOrderExist = shopStoreSfOrderService.getBySfOrderId(sfOrderId);
|
||||
if (shopStoreSfOrderExist != null && shopStoreSfOrderExist.getOrder_status() != null
|
||||
&& (ObjectUtil.equal(shopStoreSfOrderExist.getOrder_status(), StateCode.SF_ORDER_STATUS_CANCELED) ||
|
||||
ObjectUtil.equal(shopStoreSfOrderExist.getOrder_status(), StateCode.SF_ORDER_STATUS_CANCELING))) {
|
||||
logger.info("[顺丰订单取消回调通知] 订单已处于取消状态,无需重复处理: sfOrderId={} status={}",
|
||||
sfOrderId, shopStoreSfOrderExist.getOrder_status());
|
||||
return new ThirdApiRes().success("success");
|
||||
}
|
||||
|
||||
// 更新顺丰订单状态
|
||||
Boolean success = shopStoreSfOrderService.updateShopStoreSfOrderStatus(shopStoreSfOrder);
|
||||
if (!success) {
|
||||
logger.error("[顺丰订单取消回调通知] 更新顺丰订单状态失败: sfOrderId={}", sfOrderId);
|
||||
return new ThirdApiRes().fail(-1, "状态处理失败!");
|
||||
}
|
||||
logger.debug("[顺丰订单取消回调通知] 顺丰订单状态更新成功: sfOrderId={}", sfOrderId);
|
||||
|
||||
// 重要:更改商城订单状态为:已取消,注意事务问题
|
||||
success = shopOrderReturnService.sfExpressExpiredForceRefund(shopOrderId);
|
||||
if (!success) {
|
||||
logger.error("[顺丰订单取消回调通知] 取消商城订单业务处理失败: shopOrderId={}", shopOrderId);
|
||||
return new ThirdApiRes().fail(-1, "取消订单业务处理失败!");
|
||||
}
|
||||
logger.debug("[顺丰订单取消回调通知] 商城订单取消处理成功: shopOrderId={}", shopOrderId);
|
||||
|
||||
// 获取顺丰同城的物流轨迹
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("order_id", sfOrderId);
|
||||
ThirdApiRes feedRes = listOrderFeed(params);
|
||||
if (feedRes != null && ObjectUtil.equal(feedRes.getError_code(), 0)) {
|
||||
JSONObject result = JSONUtil.parseObj(feedRes.getResult());
|
||||
if (result != null && result.get("feed") != null) {
|
||||
shopStoreSfOrder.setFeed(JSONUtil.toJsonStr(result.get("feed")));
|
||||
logger.debug("[顺丰订单取消回调通知] 获取物流轨迹成功: sfOrderId={}", sfOrderId);
|
||||
}
|
||||
}
|
||||
|
||||
// 个推推送消息
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ORDER_DETAIL);
|
||||
payload.put("orderId", shopOrderId);
|
||||
pushMessageService.noticeMerchantEmployeeOrderAction(null, shopOrderId, "您有一笔取消订单", "您有一笔取消订单[" + shopOrderId + "],请及时处理。", payload);
|
||||
|
||||
logger.info("[顺丰订单取消回调通知] 处理完成: shopOrderId={} sfOrderId={}", shopOrderId, sfOrderId);
|
||||
return new ThirdApiRes().success("success");
|
||||
} catch (Exception e) {
|
||||
logger.error("[顺丰订单取消回调通知] 处理过程中发生异常", e);
|
||||
return new ThirdApiRes().fail(-1, "系统处理异常: " + e.getMessage());
|
||||
}
|
||||
|
||||
// 个推推送消息
|
||||
JSONObject payload = new JSONObject();
|
||||
payload.put("category", CommonConstant.PUSH_MSG_CATE_MCH_ORDER_DETAIL);
|
||||
payload.put("orderId", shopOrderId);
|
||||
pushMessageService.noticeMerchantEmployeeOrderAction(null, shopOrderId, "您有一笔取消订单", "您有一笔取消订单[" + shopOrderId + "],请及时处理。", null);
|
||||
|
||||
|
||||
return new ThirdApiRes().success("success");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -935,8 +968,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
pushRemark = "配送员已接单。";
|
||||
|
||||
// 上传发货信息到微信
|
||||
wxOrderShippingService.uploadShippingInfoToWx(2, shopStoreSfOrder.getShop_order_id());
|
||||
|
||||
wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderId);
|
||||
} else if (shopStoreSfOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_ARRIVED)) {
|
||||
// 顺丰同城状态:12-配送员到店;
|
||||
// 商城订单状态:从 2020-待配货 到 2030; //待发货
|
||||
@ -946,7 +978,7 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
pushRemark = "配送员已到店。";
|
||||
|
||||
// 上传发货信息到微信
|
||||
wxOrderShippingService.uploadShippingInfoToWx(2, shopStoreSfOrder.getShop_order_id());
|
||||
wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderId);
|
||||
} else if (shopStoreSfOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_RECEIVED)) {
|
||||
// 顺丰同城状态:15-配送员配送中(已取货)
|
||||
// 商城订单状态:从 2030-待发货 到 2040-已发货/待收货确认
|
||||
@ -954,26 +986,26 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
orderIsOutStatus = 0;
|
||||
orderIsShippedStatus = StateCode.ORDER_SHIPPED_STATE_YES; // 已发货
|
||||
pushRemark = "配送员已取货。";
|
||||
// 上传发货信息到微信
|
||||
wxOrderShippingService.uploadShippingInfoToWx(2, shopOrderId);
|
||||
} else if (shopStoreSfOrder.getOrder_status().equals(StateCode.SF_ORDER_STATUS_FINISH)) {
|
||||
// 顺丰同城状态:17-配送员妥投完单;
|
||||
// orderStatus = StateCode.ORDER_STATE_FINISH;
|
||||
pushRemark = "已完成配送";
|
||||
orderStatus = StateCode.ORDER_STATE_RECEIVED; //已签收
|
||||
|
||||
// 通知微信用户确认收货(同城配送不能调用微信的确认收货)
|
||||
// wxOrderShippingService.notifyConfirmReceive(shopStoreSfOrder.getShop_order_id());
|
||||
// 订单确认收货
|
||||
shopOrderBaseService.receive(shopStoreSfOrder.getShop_order_id(), null);
|
||||
// 不要提前 订单确认收货,等微信拉卡拉确认通知
|
||||
// try {
|
||||
// shopOrderBaseService.receive(shopOrderId, null);
|
||||
// } catch (Exception e) {
|
||||
// logger.error("订单确认收货失败!", e);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), orderStatus, orderIsOutStatus, orderIsShippedStatus);
|
||||
if (!success) {
|
||||
throw new ApiException(I18nUtil._("状态处理失败!"));
|
||||
}
|
||||
|
||||
// 注:状态更改之后,给 SSE 监听服务发送更改的数据
|
||||
// logger.debug("准备发送SSE消息...");
|
||||
// SseEmitterUtil.sendMessage(shopStoreSfOrder.getSf_order_id(), jsonData);
|
||||
// logger.debug("向 SSE 通道 {} 发送了:{}", shopStoreSfOrder.getSf_order_id(), jsonData);
|
||||
shopOrderInfoService.changeOrderStatus(shopOrderId, orderStatus, orderIsOutStatus, orderIsShippedStatus);
|
||||
|
||||
// 消息推送
|
||||
JSONObject payload = new JSONObject();
|
||||
@ -1010,9 +1042,9 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
return new ThirdApiRes().fail(-1, "返回数据转换失败!");
|
||||
}
|
||||
|
||||
String orderId = shopStoreSfOrder.getShop_order_id();
|
||||
|
||||
ShopStoreSfOrder order = shopStoreSfOrderService.getBySfOrderId(orderId);
|
||||
String sfOrderId = shopStoreSfOrder.getSf_order_id();
|
||||
ShopStoreSfOrder order = shopStoreSfOrderService.getBySfOrderId(sfOrderId);
|
||||
if (order == null) {
|
||||
return new ThirdApiRes().fail(-1, "订单不存在!");
|
||||
}
|
||||
@ -1050,21 +1082,20 @@ public class SFExpressApiServiceImpl implements SFExpressApiService {
|
||||
|
||||
// 更改商城订单状态为:已完成,注意事务问题
|
||||
// 配送员已取单配送中
|
||||
// 商城订单状态:从 040-已发货/待收货确认 到 2060-已完成/已签收
|
||||
success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), StateCode.ORDER_STATE_FINISH, 0, 0);
|
||||
// 商城订单状态:从 040-已发货/待收货确认 到 2060-已完成/已签收 (RMK 这里可能有点问题,需要等待用户确认收货,才能设置订单已完成)
|
||||
// success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), StateCode.ORDER_STATE_FINISH, 0, 0);
|
||||
success = shopOrderInfoService.changeOrderStatus(shopStoreSfOrder.getShop_order_id(), StateCode.ORDER_STATE_RECEIVED, 0, 0);
|
||||
if (!success) {
|
||||
throw new ApiException(I18nUtil._("状态处理失败!"));
|
||||
}
|
||||
|
||||
// 注:状态更改之后,给 SSE 监听服务发送更改的数据
|
||||
// logger.debug("准备发送SSE消息...");
|
||||
// SseEmitterUtil.sendMessage(shopStoreSfOrder.getSf_order_id(), jsonData);
|
||||
|
||||
// 通知微信用户确认收货(同城配送不能调用该微信接口)
|
||||
// wxOrderShippingService.notifyConfirmReceive(shopStoreSfOrder.getShop_order_id());
|
||||
|
||||
// 订单确认收货
|
||||
shopOrderBaseService.receive(shopStoreSfOrder.getShop_order_id(), null);
|
||||
// shopOrderBaseService.receive(shopStoreSfOrder.getShop_order_id(), null);
|
||||
|
||||
String orderId = shopStoreSfOrder.getShop_order_id();
|
||||
|
||||
// 消息推送
|
||||
JSONObject payload = new JSONObject();
|
||||
|
||||
@ -73,7 +73,7 @@ public class StoreController extends BaseControllerImpl {
|
||||
@RequestMapping(value = "/lists", method = RequestMethod.GET)
|
||||
public CommonResult lists(@RequestParam(name = "page", defaultValue = "1") Integer page,
|
||||
@RequestParam(name = "rows", defaultValue = "10") Integer rows) {
|
||||
return CommonResult.success(shopStoreBaseService.getStoreList(page, rows,new HashMap<>()));
|
||||
return CommonResult.success(shopStoreBaseService.getStoreList(page, rows, new HashMap<>()));
|
||||
}
|
||||
|
||||
@ApiOperation(value = "列出店铺分类", notes = "列出店铺分类")
|
||||
@ -314,26 +314,4 @@ public class StoreController extends BaseControllerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /**
|
||||
// * 列表查询
|
||||
// * @return
|
||||
// */
|
||||
// @ApiOperation(value = "店铺分类表-分类强调区别, 类型强调共性-分页列表查询", notes = "店铺分类表-分类强调区别, 类型强调共性-分页列表查询")
|
||||
// @RequestMapping(value = "/categoryTree", method = RequestMethod.GET)
|
||||
// public CommonResult categoryTree(ShopBaseStoreCategory category) {
|
||||
// QueryWrapper<ShopBaseStoreCategory> queryWrapper = new QueryWrapper<>();
|
||||
// if (category.getStore_category_parent_id() != null)
|
||||
// queryWrapper.eq("store_category_parent_id", category.getStore_category_parent_id());
|
||||
//
|
||||
// return CommonResult.success(shopBaseStoreCategoryService.getMobileCategoryTree(queryWrapper));
|
||||
// }
|
||||
|
||||
// @ApiOperation(value = "根据分类查询店铺", notes = "列表数据")
|
||||
// @RequestMapping(value = "/listStores", method = RequestMethod.GET)
|
||||
// public Page<ShopStoreBase> listStores(@RequestParam(name = "page", defaultValue = "1") Integer page,
|
||||
// @RequestParam(name = "rows", defaultValue = "10") Integer rows) {
|
||||
// return shopStoreBaseService.getMobileStoreList(page, rows);
|
||||
// }
|
||||
|
||||
}
|
||||
@ -13,6 +13,7 @@ import com.suisung.mall.common.api.CommonResult;
|
||||
import com.suisung.mall.common.modules.store.ShopMchEntry;
|
||||
import org.springframework.data.util.Pair;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
public interface ShopMchEntryService {
|
||||
@ -114,7 +115,7 @@ public interface ShopMchEntryService {
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
ShopMchEntry shopMerchEntryByStoreId(Integer storeId);
|
||||
ShopMchEntry getShopMerchEntryByStoreId(Integer storeId);
|
||||
|
||||
|
||||
/**
|
||||
@ -214,7 +215,7 @@ public interface ShopMchEntryService {
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
Boolean updateMerchEntryStoreId(Long id, Integer storeId);
|
||||
Boolean updateMerchEntryStoreStatus(Long id, Integer storeId);
|
||||
|
||||
/**
|
||||
* 更新商户入驻信息的拉卡拉电子合同相关信息
|
||||
@ -227,7 +228,7 @@ public interface ShopMchEntryService {
|
||||
* @param lklElectronicContractFilePath 拉卡拉电子合同文件路径
|
||||
* @return 更新成功返回 true, 失败返回 false
|
||||
*/
|
||||
Boolean updateMerchantLklElectronicContractInfo(Long merchantId, String lklElectronicContractNo, String lklElectronicContractName, String lklElectronicContractResultUrl, String contractDownloadUrl, String lklElectronicContractFilePath);
|
||||
Boolean updateMerchantLklEContractInfo(Long merchantId, String lklElectronicContractNo, String lklElectronicContractName, String lklElectronicContractResultUrl, String contractDownloadUrl, String lklElectronicContractFilePath);
|
||||
|
||||
/**
|
||||
* 更新商家入驻申请的审批状态和审批备注
|
||||
@ -334,4 +335,17 @@ public interface ShopMchEntryService {
|
||||
* @return boolean 是否成功修复至少一个商户信息
|
||||
*/
|
||||
Boolean checkAndFixMchStoreInfo(String loginMobile);
|
||||
|
||||
/**
|
||||
* 获取商户入驻分账比例
|
||||
* <p>
|
||||
* 该分账比例是实际的分账百分比,最终提交给拉卡拉的合同的分账比例是 20%。
|
||||
*
|
||||
* @param mch1stBizCategory 商户一级业务分类
|
||||
* @param mch2ndBizCategory 商户二级业务分类
|
||||
* @param srcRatio 源比例
|
||||
* @param defaultRatio 无值的时候默认比例
|
||||
* @return 分账比例
|
||||
*/
|
||||
BigDecimal getMchEntryRatioOrDefault(Integer mch1stBizCategory, Integer mch2ndBizCategory, BigDecimal srcRatio, BigDecimal defaultRatio);
|
||||
}
|
||||
@ -17,6 +17,14 @@ import java.util.Map;
|
||||
*/
|
||||
public interface ShopStoreAnalyticsService extends IBaseService<ShopStoreAnalytics> {
|
||||
|
||||
/**
|
||||
* 根据店铺id查询店铺统计信息
|
||||
*
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
ShopStoreAnalytics getByStoreId(Integer storeId);
|
||||
|
||||
List<Map> getAnalytics(List<Integer> store_id);
|
||||
|
||||
boolean saveProductAnalyticsNum(Integer store_id);
|
||||
|
||||
@ -14,5 +14,13 @@ import com.suisung.mall.core.web.service.IBaseService;
|
||||
*/
|
||||
public interface ShopStoreCompanyService extends IBaseService<ShopStoreCompany> {
|
||||
|
||||
/**
|
||||
* 获取店铺公司信息
|
||||
*
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
ShopStoreCompany getCompany(Integer storeId);
|
||||
|
||||
boolean saveOrUpdateCompany(ShopStoreCompany shopStoreCompany);
|
||||
}
|
||||
|
||||
@ -38,6 +38,17 @@ public interface ShopStoreEmployeeService extends IBaseService<ShopStoreEmployee
|
||||
*/
|
||||
List<Integer> selectEmployeeByStoreId(Integer storeId, String rightsGroupName);
|
||||
|
||||
/**
|
||||
* 根据店铺Id和员工Id获取员工信息
|
||||
*
|
||||
* @param storeId
|
||||
* @param userId
|
||||
* @param isAdmin
|
||||
* @return
|
||||
*/
|
||||
List<ShopStoreEmployee> selectEmployeeByCondition(Integer storeId, Integer userId, Boolean isAdmin);
|
||||
|
||||
|
||||
/**
|
||||
* 根据店铺Id获取店铺的所有员工的个推 CID 列表
|
||||
*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -35,6 +35,22 @@ public class ShopStoreAnalyticsServiceImpl extends BaseServiceImpl<ShopStoreAnal
|
||||
@Autowired
|
||||
private ShopProductBaseService shopProductBaseService;
|
||||
|
||||
/**
|
||||
* 根据店铺id查询店铺统计信息
|
||||
*
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ShopStoreAnalytics getByStoreId(Integer storeId) {
|
||||
try {
|
||||
return getById(storeId);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据主键值,从数据库读取数据, 必须获取主键后再读取数据
|
||||
*
|
||||
@ -103,15 +119,24 @@ public class ShopStoreAnalyticsServiceImpl extends BaseServiceImpl<ShopStoreAnal
|
||||
@Override
|
||||
public boolean saveProductAnalyticsNum(Integer store_id) {
|
||||
|
||||
ShopStoreAnalytics analytics = new ShopStoreAnalytics();
|
||||
long store_product_new_num = shopProductBaseService.getProductNum(StateCode.PRODUCT_STATE_NORMAL, store_id, -30, null);
|
||||
long store_product_num = shopProductBaseService.getProductNum(StateCode.PRODUCT_STATE_NORMAL, store_id, null, null);
|
||||
|
||||
ShopStoreAnalytics analytics = new ShopStoreAnalytics();
|
||||
analytics.setStore_id(store_id);
|
||||
analytics.setStore_product_new_num(store_product_new_num);
|
||||
analytics.setStore_product_num(store_product_num);
|
||||
|
||||
return saveOrUpdate(analytics);
|
||||
// 先尝试查询是否存在记录
|
||||
ShopStoreAnalytics existingAnalytics = getById(store_id);
|
||||
|
||||
if (existingAnalytics != null) {
|
||||
// 如果记录存在,使用updateById更新
|
||||
return updateById(analytics);
|
||||
} else {
|
||||
// 如果记录不存在,使用insert插入
|
||||
return save(analytics);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2033,7 +2033,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
// 添加默认客户等级
|
||||
InvoicingCustomerLevel invoicingCustomerLevel = new InvoicingCustomerLevel();
|
||||
invoicingCustomerLevel.setCustomer_level_name(I18nUtil._("普通(系统默认,不可删除)"));
|
||||
invoicingCustomerLevel.setCustomer_level_discountrate(new BigDecimal("100"));
|
||||
invoicingCustomerLevel.setCustomer_level_discountrate(new BigDecimal(100));
|
||||
invoicingCustomerLevel.setCustomer_level_is_buildin(1);
|
||||
invoicingCustomerLevel.setCustomer_level_desc("");
|
||||
|
||||
@ -2375,7 +2375,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
// 打包费
|
||||
BigDecimal packingFee = Convert.toBigDecimal(getParameter("packing_fee"));
|
||||
if (packingFee == null || packingFee.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
base.setPacking_fee(BigDecimal.ZERO);
|
||||
packingFee = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
if (packingFee.compareTo(new BigDecimal("10")) > 0) {
|
||||
@ -2383,6 +2383,13 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
}
|
||||
base.setPacking_fee(packingFee);
|
||||
|
||||
Integer ringtoneIsEnable = Convert.toInt(getParameter("ringtone_is_enable"));
|
||||
if (ringtoneIsEnable == null || ringtoneIsEnable <= 0) {
|
||||
ringtoneIsEnable = CommonConstant.Enable;
|
||||
}
|
||||
|
||||
base.setRingtone_is_enable(ringtoneIsEnable);
|
||||
|
||||
|
||||
// 百度坐标系BD09经纬度 转出 火星坐标系GCJ02经纬度 (因为数据库保存的经纬度统一是GCJ02经纬度,所以需要转换 )
|
||||
base = bd09ToGcj02Gps(base);
|
||||
@ -2538,7 +2545,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
// 打包费
|
||||
BigDecimal packingFee = Convert.toBigDecimal(getParameter("packing_fee"));
|
||||
if (packingFee == null || packingFee.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
base.setPacking_fee(BigDecimal.ZERO);
|
||||
packingFee = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
if (packingFee.compareTo(new BigDecimal("10")) > 0) {
|
||||
@ -2546,6 +2553,13 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
}
|
||||
base.setPacking_fee(packingFee);
|
||||
|
||||
Integer ringtoneIsEnable = Convert.toInt(getParameter("ringtone_is_enable"));
|
||||
if (ringtoneIsEnable == null || ringtoneIsEnable <= 0) {
|
||||
ringtoneIsEnable = CommonConstant.Enable;
|
||||
}
|
||||
|
||||
base.setRingtone_is_enable(ringtoneIsEnable);
|
||||
|
||||
// 百度坐标系BD09经纬度 转出 火星坐标系GCJ02经纬度 (因为数据库保存的经纬度统一是GCJ02经纬度,所以需要转换 )
|
||||
base = bd09ToGcj02Gps(base);
|
||||
|
||||
@ -2734,6 +2748,14 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
if (CheckUtil.isNotEmpty(store_id)) {
|
||||
|
||||
ShopStoreBase shopStoreBase = get(store_id);
|
||||
if (shopStoreBase == null) {
|
||||
throw new ApiException("无法找到店铺信息!");
|
||||
}
|
||||
|
||||
if (CheckUtil.isEmpty(shopStoreBase.getRingtone_is_enable())) {
|
||||
shopStoreBase.setRingtone_is_enable(CommonConstant.Enable);
|
||||
}
|
||||
|
||||
row = Convert.toMap(Object.class, Object.class, shopStoreBase);
|
||||
ShopStoreInfo shopStoreInfo = shopStoreInfoService.get(store_id);
|
||||
if (shopStoreInfo != null) {
|
||||
@ -3046,17 +3068,17 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
params.put("countyId", countyId);
|
||||
params.put("userLng", userLng);
|
||||
params.put("userLat", userLat);
|
||||
List<Integer> storeCategoryIds=new ArrayList<>();
|
||||
List<Integer> storeCategoryIds = new ArrayList<>();
|
||||
QueryWrapper<ShopBaseStoreCategory> storeCategoryQueryWrapper = new QueryWrapper<>();
|
||||
storeCategoryQueryWrapper.eq("store_category_parent_id",storeCategoryId);
|
||||
List<ShopBaseStoreCategory> shopBaseStoreCategories= shopBaseStoreCategoryService.list(storeCategoryQueryWrapper);
|
||||
if(!shopBaseStoreCategories.isEmpty()){
|
||||
storeCategoryQueryWrapper.eq("store_category_parent_id", storeCategoryId);
|
||||
List<ShopBaseStoreCategory> shopBaseStoreCategories = shopBaseStoreCategoryService.list(storeCategoryQueryWrapper);
|
||||
if (!shopBaseStoreCategories.isEmpty()) {
|
||||
storeCategoryIds.add(storeCategoryId);
|
||||
shopBaseStoreCategories.forEach(shopBaseStoreCategory -> {
|
||||
storeCategoryIds.add(shopBaseStoreCategory.getStore_category_id());
|
||||
});
|
||||
params.put("storeCategoryIds", storeCategoryIds);
|
||||
}else {
|
||||
} else {
|
||||
params.put("storeCategoryId", storeCategoryId);
|
||||
}
|
||||
|
||||
@ -3165,7 +3187,12 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
shopStoreBase.setUser_id(userId); // 店铺管理员用户Id
|
||||
shopStoreBase.setStore_name(shopMchEntry.getStore_name());
|
||||
shopStoreBase.setStore_category_id(shopMchEntry.getBiz_category()); // 重要,店铺分类id,对应 shop_base_store_category 表的分类
|
||||
shopStoreBase.setSplit_ratio(shopMchEntry.getSplit_ratio()); // 分账比例
|
||||
shopStoreBase.setStore_2nd_category_id(shopMchEntry.getBiz_second_category());
|
||||
|
||||
// 分账比例,最终提交给拉卡拉的分账比例是 20%。
|
||||
BigDecimal splitRatio = shopMchEntryService.getMchEntryRatioOrDefault(shopMchEntry.getBiz_category(), shopMchEntry.getBiz_second_category(), shopMchEntry.getSplit_ratio(), new BigDecimal(94));
|
||||
|
||||
shopStoreBase.setSplit_ratio(splitRatio); // 分账比例
|
||||
shopStoreBase.setPacking_fee(BigDecimal.ZERO);// 默认0元打包费
|
||||
String storeFacadeImage = shopMchEntry.getFront_facade_image();
|
||||
String storeLogoImage = shopMchEntry.getStore_logo();
|
||||
@ -3253,75 +3280,85 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
|
||||
// 初始化默认公司信息,避免商家编辑不了,申请入驻即使是个人类型的,也需要保存相关应有信息,公司信息不能空着
|
||||
// shop_store_company
|
||||
ShopStoreCompany shopStoreCompany = new ShopStoreCompany();
|
||||
shopStoreCompany.setUser_id(userId);
|
||||
shopStoreCompany.setStore_id(storeId);
|
||||
ShopStoreCompany shopStoreCompany = shopStoreCompanyService.getCompany(storeId);
|
||||
if (shopStoreCompany == null) {
|
||||
shopStoreCompany.setUser_id(userId);
|
||||
shopStoreCompany.setStore_id(storeId);
|
||||
|
||||
// 联系人
|
||||
shopStoreCompany.setContacts_name(shopMchEntry.getContact_name());
|
||||
shopStoreCompany.setContacts_phone(contact_mobile);
|
||||
// 联系人
|
||||
shopStoreCompany.setContacts_name(shopMchEntry.getContact_name());
|
||||
shopStoreCompany.setContacts_phone(contact_mobile);
|
||||
|
||||
// 公司名
|
||||
String companyName = StrUtil.isBlank(shopMchEntry.getBiz_license_company()) ? shopMchEntry.getStore_name() : shopMchEntry.getBiz_license_company();
|
||||
shopStoreCompany.setCompany_name(companyName);
|
||||
shopStoreCompany.setCompany_area(shopStoreBase.getStore_area());
|
||||
shopStoreCompany.setCompany_address(shopMchEntry.getStore_address());
|
||||
// 公司名
|
||||
String companyName = StrUtil.isBlank(shopMchEntry.getBiz_license_company()) ? shopMchEntry.getStore_name() : shopMchEntry.getBiz_license_company();
|
||||
shopStoreCompany.setCompany_name(companyName);
|
||||
shopStoreCompany.setCompany_area(shopStoreBase.getStore_area());
|
||||
shopStoreCompany.setCompany_address(shopMchEntry.getStore_address());
|
||||
|
||||
// 营业执照
|
||||
shopStoreCompany.setBusiness_id(shopMchEntry.getBiz_license_number());
|
||||
shopStoreCompany.setBusiness_license_electronic(shopMchEntry.getBiz_license_image());
|
||||
// 营业执照
|
||||
shopStoreCompany.setBusiness_id(shopMchEntry.getBiz_license_number());
|
||||
shopStoreCompany.setBusiness_license_electronic(shopMchEntry.getBiz_license_image());
|
||||
|
||||
// 企业法人
|
||||
shopStoreCompany.setLegal_person(shopMchEntry.getLegal_person_name());
|
||||
shopStoreCompany.setLegal_person_number(shopMchEntry.getLegal_person_id_number());
|
||||
shopStoreCompany.setLegal_person_electronic(shopMchEntry.getLegal_person_id_images());
|
||||
// 企业法人
|
||||
shopStoreCompany.setLegal_person(shopMchEntry.getLegal_person_name());
|
||||
shopStoreCompany.setLegal_person_number(shopMchEntry.getLegal_person_id_number());
|
||||
shopStoreCompany.setLegal_person_electronic(shopMchEntry.getLegal_person_id_images());
|
||||
|
||||
// 银行对公账号
|
||||
shopStoreCompany.setBank_account_name(shopMchEntry.getAccount_holder_name());
|
||||
shopStoreCompany.setBank_account_number(shopMchEntry.getAccount_number());
|
||||
shopStoreCompany.setBank_name(shopMchEntry.getBank_name());
|
||||
// 银行对公账号
|
||||
shopStoreCompany.setBank_account_name(shopMchEntry.getAccount_holder_name());
|
||||
shopStoreCompany.setBank_account_number(shopMchEntry.getAccount_number());
|
||||
shopStoreCompany.setBank_name(shopMchEntry.getBank_name());
|
||||
|
||||
Date today = new Date(); // 当前时间
|
||||
shopStoreCompany.setOrganization_code_start(today);
|
||||
shopStoreCompany.setOrganization_code_end(DateUtil.offsetDay(today, 365 * 5));// 五年
|
||||
shopStoreCompany.setEstablish_date(today);
|
||||
shopStoreCompany.setBusiness_licence_start(today);
|
||||
shopStoreCompany.setBusiness_licence_end(DateUtil.offsetDay(today, 365 * 10));
|
||||
Date today = new Date(); // 当前时间
|
||||
shopStoreCompany.setOrganization_code_start(today);
|
||||
shopStoreCompany.setOrganization_code_end(DateUtil.offsetDay(today, 365 * 5));// 五年
|
||||
shopStoreCompany.setEstablish_date(today);
|
||||
shopStoreCompany.setBusiness_licence_start(today);
|
||||
shopStoreCompany.setBusiness_licence_end(DateUtil.offsetDay(today, 365 * 10));
|
||||
|
||||
shopStoreCompany.setCompany_description(shopMchEntry.getStore_name());
|
||||
shopStoreCompany.setStore_class_ids("");
|
||||
shopStoreCompany.setStore_class_names("");
|
||||
shopStoreCompany.setStore_class_commission("");
|
||||
shopStoreCompany.setCompany_description(shopMchEntry.getStore_name());
|
||||
shopStoreCompany.setStore_class_ids("");
|
||||
shopStoreCompany.setStore_class_names("");
|
||||
shopStoreCompany.setStore_class_commission("");
|
||||
|
||||
if (!shopStoreCompanyService.save(shopStoreCompany)) {
|
||||
logger.error("生成店铺:新增店铺公司失败");
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("新增店铺公司失败"));
|
||||
if (!shopStoreCompanyService.save(shopStoreCompany)) {
|
||||
logger.error("生成店铺:新增店铺公司失败");
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("新增店铺公司失败"));
|
||||
}
|
||||
|
||||
return Pair.of(0, "新增店铺公司失败");
|
||||
}
|
||||
|
||||
return Pair.of(0, "新增店铺公司失败");
|
||||
}
|
||||
|
||||
List<ShopStoreEmployee> shopStoreEmployees = shopStoreEmployeeService.selectEmployeeByCondition(storeId, null, null);
|
||||
|
||||
QueryWrapper<ShopStoreEmployee> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("store_id", storeId).eq("user_id", userId).eq("employee_is_admin", CommonConstant.Enable);
|
||||
if (shopStoreEmployeeService.count(queryWrapper) <= 0) {
|
||||
ShopStoreEmployee shopStoreEmployee = new ShopStoreEmployee();
|
||||
shopStoreEmployee.setStore_id(storeId);
|
||||
shopStoreEmployee.setUser_id(userId);
|
||||
shopStoreEmployee.setRights_group_id(""); // 店铺管理员,店铺
|
||||
shopStoreEmployee.setEmployee_is_kefu(CommonConstant.Enable);
|
||||
|
||||
if (CollUtil.isEmpty(shopStoreEmployees)) { // 添加管理员
|
||||
// shop_store_employee 店铺员工,添加管理员
|
||||
ShopStoreEmployee shopStoreEmployee = new ShopStoreEmployee();
|
||||
shopStoreEmployee.setStore_id(storeId);
|
||||
shopStoreEmployee.setUser_id(userId);
|
||||
shopStoreEmployee.setRights_group_id(""); // 店铺管理员,店铺
|
||||
shopStoreEmployee.setEmployee_is_admin(CommonConstant.Enable);
|
||||
shopStoreEmployee.setEmployee_is_kefu(CommonConstant.Enable);
|
||||
if (!shopStoreEmployeeService.save(shopStoreEmployee)) {
|
||||
logger.error("生成店铺:新增店铺员工失败");
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("新增店铺员工失败"));
|
||||
}
|
||||
return Pair.of(0, "新增店铺员工失败");
|
||||
} else { // 添加店员或管理员
|
||||
shopStoreEmployees = shopStoreEmployeeService.selectEmployeeByCondition(storeId, userId, null);
|
||||
if (CollUtil.isEmpty(shopStoreEmployees)) {
|
||||
shopStoreEmployee.setEmployee_is_admin(CommonConstant.Disable);
|
||||
} else {
|
||||
shopStoreEmployee.setEmployee_is_admin(shopStoreEmployees.get(0).getEmployee_is_admin());
|
||||
}
|
||||
}
|
||||
|
||||
if (!shopStoreEmployeeService.save(shopStoreEmployee)) {
|
||||
logger.error("生成店铺:新增店铺员工失败");
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("新增店铺员工失败"));
|
||||
}
|
||||
return Pair.of(0, "新增店铺员工失败");
|
||||
}
|
||||
|
||||
// 生成店铺的太阳码 2025-03-31
|
||||
if (StrUtil.isBlank(shopStoreBase.getWx_qrcode())) {
|
||||
Pair<String, String> resp = wxQrCodeService.genUnlimitedWxQrCode("pagesub/index/store", "store_id=" + storeId);
|
||||
@ -3331,13 +3368,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
}
|
||||
|
||||
// 注意:关联店铺到用户,给用户增加店铺管理员权限
|
||||
initStoreExtraInfo(userId, storeId);
|
||||
initStoreExtraInfo(userId, storeId, allowThrown);
|
||||
|
||||
// 更改店铺Id
|
||||
shopMchEntryService.updateMerchEntryStoreId(shopMchEntry.getId(), storeId);
|
||||
|
||||
// 初始化同城配送默认设置
|
||||
// storeSameCityTransportBaseService.initDefaultSameCityTransport(storeId);
|
||||
// 更改店铺Id和状态
|
||||
shopMchEntryService.updateMerchEntryStoreStatus(shopMchEntry.getId(), storeId);
|
||||
|
||||
// 立即创建顺丰店铺,附带初始化同城配送默认设置
|
||||
String[] areaNames = StrUtil.isNotBlank(shopMchEntry.getStore_area()) ? shopMchEntry.getStore_area().split("/") : new String[0];
|
||||
@ -3360,7 +3394,7 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
* @param userId
|
||||
* @param storeId
|
||||
*/
|
||||
private void initStoreExtraInfo(Integer userId, Integer storeId) {
|
||||
private void initStoreExtraInfo(Integer userId, Integer storeId, Boolean allowThrown) {
|
||||
if (ObjectUtil.isNull(userId) || ObjectUtil.isNull(storeId)) {
|
||||
return;
|
||||
}
|
||||
@ -3368,10 +3402,16 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
Date today = new Date(); // 当前时间
|
||||
|
||||
// 初始化时添加一条店铺分析信息
|
||||
ShopStoreAnalytics store_analytics_column = new ShopStoreAnalytics();
|
||||
store_analytics_column.setStore_id(storeId);
|
||||
if (!shopStoreAnalyticsService.saveOrUpdate(store_analytics_column)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
ShopStoreAnalytics storeAnalytics = shopStoreAnalyticsService.getByStoreId(storeId);
|
||||
if (storeAnalytics == null) {
|
||||
storeAnalytics = new ShopStoreAnalytics();
|
||||
storeAnalytics.setStore_id(storeId);
|
||||
if (!shopStoreAnalyticsService.add(storeAnalytics)) {
|
||||
if (allowThrown) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
log.error("初始化店铺分析信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化店铺商品标签
|
||||
@ -3390,7 +3430,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
|
||||
if (CollUtil.isNotEmpty(store_product_tag_row)) {
|
||||
if (!shopStoreProductTagService.saveOrUpdate(store_product_tag_row)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
if (allowThrown) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
log.error("初始化店铺商品标签失败");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3402,7 +3445,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
invoicingWarehouseBase.setWarehouse_number("");
|
||||
invoicingWarehouseBase.setWarehouse_contact("");
|
||||
if (!invoicingWarehouseBaseService.saveOrUpdate(invoicingWarehouseBase)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
if (allowThrown) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
log.error("初始化默认店铺仓库失败");
|
||||
}
|
||||
|
||||
//初始化默认权限
|
||||
@ -3490,7 +3536,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
shopStoreEmployeeRightsGroups.add(extension2);
|
||||
|
||||
if (!shopStoreEmployeeRightsGroupService.saveOrUpdate(shopStoreEmployeeRightsGroups)) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
if (allowThrown) {
|
||||
throw new ApiException(ResultCode.FAILED);
|
||||
}
|
||||
log.error("初始化店铺员工默认权限失败");
|
||||
}
|
||||
|
||||
// 添加店铺到用户
|
||||
@ -3504,7 +3553,9 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
.set("employee_is_admin", CommonConstant.Enable)
|
||||
.set("employee_is_kefu", CommonConstant.Enable);
|
||||
if (!shopStoreEmployeeService.update(queryWrapper)) {
|
||||
// throw new ApiException(I18nUtil._("设置店铺管理员权限失败"));
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("设置店铺管理员权限失败"));
|
||||
}
|
||||
log.error("设置店铺管理员权限失败!");
|
||||
}
|
||||
|
||||
@ -3518,7 +3569,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
String storeIds = appendStoreIdToAccount(userId, storeId);
|
||||
accountUserBase.setStore_ids(storeIds); // 重要,给用户添加上这个店铺的归属权
|
||||
if (!accountService.saveOrUpdateUserBase(accountUserBase)) {
|
||||
throw new ApiException(I18nUtil._("店铺关联到用户失败"));
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("店铺关联到用户失败"));
|
||||
}
|
||||
log.error("店铺关联到用户失败!");
|
||||
}
|
||||
|
||||
// 添加默认运输模板
|
||||
@ -3530,7 +3584,10 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
shopStoreTransportType.setTransport_type_freight_free(BigDecimal.ZERO); // 免运费额度
|
||||
shopStoreTransportType.setTransport_type_free(1);
|
||||
if (!shopStoreTransportTypeService.saveOrUpdate(shopStoreTransportType)) {
|
||||
throw new ApiException(I18nUtil._("添加运输模板失败"));
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("添加运输模板失败"));
|
||||
}
|
||||
log.error("添加运输模板失败!");
|
||||
}
|
||||
|
||||
// 店铺配置
|
||||
@ -3569,18 +3626,24 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
shopStoreConfig.setSc_festival_amount_upper(BigDecimal.ZERO);
|
||||
shopStoreConfig.setSc_festival_float_proportion(BigDecimal.ZERO);
|
||||
if (!shopStoreConfigService.saveOrUpdate(shopStoreConfig)) {
|
||||
throw new ApiException(I18nUtil._("添加订单流转配置失败"));
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("添加订单流转配置失败"));
|
||||
}
|
||||
log.error("添加订单流转配置失败!");
|
||||
}
|
||||
|
||||
// 添加默认客户等级
|
||||
InvoicingCustomerLevel invoicingCustomerLevel = new InvoicingCustomerLevel();
|
||||
invoicingCustomerLevel.setCustomer_level_name(I18nUtil._("普通(系统默认,不可删除)"));
|
||||
invoicingCustomerLevel.setCustomer_level_discountrate(new BigDecimal("100"));
|
||||
invoicingCustomerLevel.setCustomer_level_discountrate(new BigDecimal(100));
|
||||
invoicingCustomerLevel.setCustomer_level_is_buildin(1);
|
||||
invoicingCustomerLevel.setCustomer_level_desc("");
|
||||
|
||||
if (!invoicingCustomerLevelService.saveOrUpdate(invoicingCustomerLevel)) {
|
||||
throw new ApiException(I18nUtil._("添加默认客户等级失败"));
|
||||
if (allowThrown) {
|
||||
throw new ApiException(I18nUtil._("添加默认客户等级失败"));
|
||||
}
|
||||
log.error("添加默认客户等级失败!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3642,50 +3705,54 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
* 获取店铺的分账比例
|
||||
* 根据店铺的大分、小分类、来计算分账比例
|
||||
*
|
||||
* @param storeId
|
||||
* @param storeId 店铺ID
|
||||
* @param reCalculate 是否根据店铺的分类来重新计算
|
||||
* @return 平台分账比例的数值
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal getStoreSplitRatio(Integer storeId, boolean reCalculate) {
|
||||
BigDecimal defaultSplitRatio = new BigDecimal("100");
|
||||
log.debug("开始获取店铺分账比例,storeId={}, reCalculate={}", storeId, reCalculate);
|
||||
|
||||
// 定义默认分账比例
|
||||
final BigDecimal defaultSplitRatio = BigDecimal.valueOf(94);
|
||||
if (storeId == null || storeId <= 0) {
|
||||
log.warn("店铺ID无效,使用默认分账比例: {}", defaultSplitRatio);
|
||||
return defaultSplitRatio;
|
||||
}
|
||||
|
||||
BigDecimal splitRatio = null;
|
||||
|
||||
// 获取店铺基础信息
|
||||
ShopStoreBase shopStoreBase = get(storeId);
|
||||
if (shopStoreBase == null) {
|
||||
return defaultSplitRatio;
|
||||
}
|
||||
log.debug("获取店铺基础信息完成,shopStoreBase={}",
|
||||
shopStoreBase != null ? shopStoreBase.getStore_id() : null);
|
||||
|
||||
// 获取店铺已配置的分账比例
|
||||
BigDecimal splitRatio = shopStoreBase.getSplit_ratio();
|
||||
if (shopStoreBase != null) {
|
||||
// 获取店铺已配置的分账比例
|
||||
splitRatio = CheckUtil.isEmpty(shopStoreBase.getSplit_ratio()) ? null : shopStoreBase.getSplit_ratio();
|
||||
log.debug("从店铺基础信息获取分账比例: {}", splitRatio);
|
||||
}
|
||||
|
||||
// 如果没有配置或需要重新计算,则基于店铺分类获取比例
|
||||
if (reCalculate || splitRatio == null) {
|
||||
Integer storeCategoryId = shopStoreBase.getStore_category_id();
|
||||
if (storeCategoryId == null) {
|
||||
return defaultSplitRatio; // 默认 100%
|
||||
}
|
||||
log.debug("需要重新计算分账比例或当前比例为空,基于店铺分类计算");
|
||||
|
||||
// 获取店铺分类信息
|
||||
ShopBaseStoreCategory category = shopBaseStoreCategoryService.get(storeCategoryId);
|
||||
if (category != null && category.getSplit_ratio() != null) {
|
||||
splitRatio = category.getSplit_ratio();
|
||||
} else {
|
||||
splitRatio = defaultSplitRatio; // 默认值
|
||||
}
|
||||
|
||||
// 确保比例不超过 100%
|
||||
if (splitRatio.compareTo(BigDecimal.ZERO) < 0 || splitRatio.compareTo(defaultSplitRatio) > 0) {
|
||||
splitRatio = defaultSplitRatio;
|
||||
}
|
||||
// 通过商户入驻服务计算分账比例,传入null作为一级分类,店铺分类ID作为二级分类
|
||||
splitRatio = shopMchEntryService.getMchEntryRatioOrDefault(shopStoreBase.getStore_category_id(), shopStoreBase.getStore_2nd_category_id(), splitRatio, defaultSplitRatio);
|
||||
log.debug("通过商户入驻服务计算分账比例: {}", splitRatio);
|
||||
}
|
||||
|
||||
// 确保比例在有效范围内 (0%, 100%)
|
||||
if (splitRatio != null && (splitRatio.compareTo(BigDecimal.ZERO) <= 0 || splitRatio.compareTo(BigDecimal.valueOf(100)) >= 0)) {
|
||||
log.warn("分账比例超出有效范围 (0, 100),使用默认分账比例: {},当前比例: {}", defaultSplitRatio, splitRatio);
|
||||
splitRatio = defaultSplitRatio;
|
||||
}
|
||||
|
||||
log.info("获取店铺分账比例完成,storeId={}, splitRatio={}", storeId, splitRatio);
|
||||
return splitRatio;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 修改店铺的营业状态
|
||||
*
|
||||
@ -4032,6 +4099,48 @@ public class ShopStoreBaseServiceImpl extends BaseServiceImpl<ShopStoreBaseMappe
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新店铺分账比例
|
||||
*
|
||||
* @param storeId 店铺ID
|
||||
* @param splitRatio 分账比例
|
||||
* @return 更新结果,成功返回true,失败返回false
|
||||
*/
|
||||
protected Boolean updateStoreBaseSplitRatio(Integer storeId, BigDecimal splitRatio) {
|
||||
log.debug("开始更新店铺分账比例,storeId={}, splitRatio={}", storeId, splitRatio);
|
||||
|
||||
// 参数校验
|
||||
if (storeId == null || storeId <= 0) {
|
||||
log.warn("更新店铺分账比例参数校验失败,storeId不能为空或小于等于0");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (splitRatio == null || splitRatio.compareTo(BigDecimal.ZERO) <= 0
|
||||
|| splitRatio.compareTo(BigDecimal.valueOf(100)) >= 0) {
|
||||
log.warn("更新店铺分账比例参数校验失败,splitRatio 值无效");
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
UpdateWrapper<ShopStoreBase> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.eq("store_id", storeId);
|
||||
updateWrapper.set("split_ratio", splitRatio);
|
||||
|
||||
boolean result = update(updateWrapper);
|
||||
if (result) {
|
||||
log.info("店铺分账比例更新成功,storeId={}, splitRatio={}", storeId, splitRatio);
|
||||
} else {
|
||||
log.warn("店铺分账比例更新失败,storeId={}, splitRatio={}", storeId, splitRatio);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("更新店铺分账比例时发生异常,storeId={}, splitRatio={}", storeId, splitRatio, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// @Override
|
||||
// public Page<ShopStoreBase> getMobileStoreList(Integer page, Integer rows) {
|
||||
// QueryWrapper<ShopStoreBase> queryWrapper=new QueryWrapper<>();
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.suisung.mall.shop.store.service.impl;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.suisung.mall.common.api.ResultCode;
|
||||
import com.suisung.mall.common.exception.ApiException;
|
||||
import com.suisung.mall.common.modules.store.ShopStoreCompany;
|
||||
@ -30,6 +31,19 @@ public class ShopStoreCompanyServiceImpl extends BaseServiceImpl<ShopStoreCompan
|
||||
@Autowired
|
||||
private ShopStoreEmployeeService shopStoreEmployeeService;
|
||||
|
||||
/**
|
||||
* 获取店铺公司信息
|
||||
*
|
||||
* @param storeId
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public ShopStoreCompany getCompany(Integer storeId) {
|
||||
QueryWrapper<ShopStoreCompany> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.eq("store_id", storeId).orderByAsc("company_id");
|
||||
return findOne(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveOrUpdateCompany(ShopStoreCompany shopStoreCompany) {
|
||||
|
||||
|
||||
@ -243,6 +243,33 @@ public class ShopStoreEmployeeServiceImpl extends BaseServiceImpl<ShopStoreEmplo
|
||||
return shopStoreEmployeeMapper.selectByStoreIdRightGroupId(storeId, rightsGroupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据店铺Id和员工Id获取员工信息
|
||||
*
|
||||
* @param storeId
|
||||
* @param userId
|
||||
* @param isAdmin
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<ShopStoreEmployee> selectEmployeeByCondition(Integer storeId, Integer userId, Boolean isAdmin) {
|
||||
if (CheckUtil.isEmpty(storeId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
QueryWrapper<ShopStoreEmployee> queryWrapper = new QueryWrapper();
|
||||
queryWrapper.eq("store_id", storeId);
|
||||
if (CheckUtil.isNotEmpty(userId)) {
|
||||
queryWrapper.eq("user_id", userId);
|
||||
}
|
||||
|
||||
if (isAdmin != null) {
|
||||
queryWrapper.eq("employee_is_admin", isAdmin);
|
||||
}
|
||||
|
||||
return list(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据店铺Id获取店铺的所有员工的个推 CID 列表
|
||||
*
|
||||
|
||||
@ -6,10 +6,11 @@ import java.math.RoundingMode;
|
||||
public class ProductPriceCalculator {
|
||||
/**
|
||||
* 计算切割后的商品单价和数量
|
||||
*
|
||||
* @param unitPricePerKg 千克单价(元/千克)
|
||||
* @param totalWeightKg 总重量(千克)
|
||||
* @param pieceWeight 切割单位重量(数值)
|
||||
* @param weightUnit 重量单位("g"=克,"kg"=千克)
|
||||
* @param totalWeightKg 总重量(千克)
|
||||
* @param pieceWeight 切割单位重量(数值)
|
||||
* @param weightUnit 重量单位("g"=克,"kg"=千克)
|
||||
* @return 包含两个元素的数组:[切割后单价(元/单位), 完整单位数量]
|
||||
*/
|
||||
public static BigDecimal[] calculatePriceAndQuantity(
|
||||
@ -70,7 +71,7 @@ public class ProductPriceCalculator {
|
||||
|
||||
// 千克单位示例
|
||||
BigDecimal[] result3 = calculatePriceAndQuantity(
|
||||
new BigDecimal("100"),
|
||||
new BigDecimal(100),
|
||||
new BigDecimal("2.5"),
|
||||
new BigDecimal("0.5"),
|
||||
"kg");
|
||||
|
||||
@ -57,7 +57,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
|
||||
this.syncThirdDataService = syncThirdDataService;
|
||||
this.syncStoreSpecsService = syncStoreSpecsService;
|
||||
// 创建线程池(根据CPU核心数优化)
|
||||
// int corePoolSize = Runtime.getRuntime().availableProcessors();
|
||||
// int corePoolSize = Runtime.getRuntime().availableProcessors();
|
||||
// log.info("核心线程数量{}", corePoolSize);
|
||||
this.executorService = Executors.newFixedThreadPool(6);
|
||||
this.futures = new ArrayList<>();
|
||||
@ -74,7 +74,7 @@ public class ShopBatchSubmitListener extends AnalysisEventListener<SxGoosModelEx
|
||||
sxGoosModelExcel.setProduct_barcode("1");
|
||||
sxGoosModelExcel.setProduct_name("产品1");
|
||||
sxGoosModelExcel.setShop_specs("颜色");
|
||||
sxGoosModelExcel.setRetail_price(new BigDecimal("100"));
|
||||
sxGoosModelExcel.setRetail_price(new BigDecimal(100));
|
||||
sxGoosModelExcel.setStock(new BigDecimal("10"));
|
||||
sxGoosModelExcel.setJsonSpecs("[颜色:红色][尺寸:10]");
|
||||
|
||||
|
||||
@ -121,4 +121,5 @@ job:
|
||||
- "UpdatePayCardStateJob"
|
||||
- "RetryMqMsgJob"
|
||||
- "ProductItemAutoFillJob"
|
||||
- "XcxSubSendMessageJob"
|
||||
- "XcxSubSendMessageJob"
|
||||
- "UpdateOrderSeparateJob"
|
||||
@ -168,7 +168,7 @@ lakala:
|
||||
client_id: lsycs
|
||||
client_secret: XPa1HB5d55Ig0qV8
|
||||
user_no: 29153396
|
||||
split_lowest_ratio: 94.00
|
||||
split_lowest_ratio: 20.00
|
||||
activity_id: 687
|
||||
wx_fee: 0.6 # 微信手续费 6/1000
|
||||
api_pub_key_path: payKey/lakala/dev/tk_api_public_key.txt
|
||||
|
||||
@ -147,34 +147,34 @@ sf-express:
|
||||
#拉卡拉进件配置
|
||||
lakala:
|
||||
#服务地址
|
||||
server_url: https://test.wsmsd.cn/sit
|
||||
server_url: https://s2.lakala.com
|
||||
#应用Id
|
||||
app_id: OP00000003
|
||||
app_id: OP10000439
|
||||
#商户证书序列号
|
||||
serial_no: 00dfba8194c41b84cf
|
||||
serial_no: 1737359895636
|
||||
#商户证书
|
||||
api_cert_path: payKey/lakala/dev/OP00000003_cert.cer
|
||||
api_cert_path: payKey/lakala/prod/api_cert.cer
|
||||
#商户私钥
|
||||
api_pri_key_path: payKey/lakala/dev/OP00000003_private_key.pem
|
||||
api_pri_key_path: payKey/lakala/prod/api_private_key.pem
|
||||
#拉卡拉平台证书
|
||||
lkl_platform_cer_path: payKey/lakala/dev/lkl_notify_cert_v2.cer
|
||||
lkl_platform_cer_path: payKey/lakala/prod/lkl_platform.cer
|
||||
#机构代码
|
||||
org_code: 1951582
|
||||
is_prod: false
|
||||
# 拉卡拉拓客进件配置
|
||||
org_code: 980688
|
||||
is_prod: true
|
||||
tk:
|
||||
#服务地址
|
||||
server_url: https://test.wsmsd.cn
|
||||
client_id: lsycs
|
||||
client_secret: XPa1HB5d55Ig0qV8
|
||||
user_no: 29153396
|
||||
split_lowest_ratio: 94.00
|
||||
activity_id: 687
|
||||
wx_fee: 0.6 # 微信手续费 6/1000
|
||||
api_pub_key_path: payKey/lakala/dev/tk_api_public_key.txt
|
||||
api_pri_key_path: payKey/lakala/dev/tk_api_private_key.txt
|
||||
notify_pub_key_path: payKey/lakala/dev/tk_notify_public_key.txt
|
||||
notify_pri_key_path: payKey/lakala/dev/tk_notify_private_key.txt
|
||||
server_url: https://tkapi.lakala.com
|
||||
client_id: gpff
|
||||
client_secret: uRjvaJkWS1A0VsAv
|
||||
user_no: 22874827
|
||||
split_lowest_ratio: 20.00
|
||||
activity_id: 208
|
||||
wx_fee: 0.25 # 微信手续费 千分之2.5
|
||||
api_pub_key_path: payKey/lakala/prod/tk_api_public_key.txt
|
||||
api_pri_key_path: payKey/lakala/prod/tk_api_private_key.txt
|
||||
notify_pub_key_path: payKey/lakala/prod/tk_notify_public_key.txt
|
||||
notify_pri_key_path: payKey/lakala/prod/tk_notify_private_key.txt
|
||||
|
||||
#e签宝配置
|
||||
esign:
|
||||
server_url: https://smlopenapi.esign.cn
|
||||
|
||||
@ -183,7 +183,7 @@ lakala:
|
||||
client_id: gpff
|
||||
client_secret: uRjvaJkWS1A0VsAv
|
||||
user_no: 22874827
|
||||
split_lowest_ratio: 94.00
|
||||
split_lowest_ratio: 20.00
|
||||
activity_id: 208
|
||||
wx_fee: 0.25 # 微信手续费 千分之2.5
|
||||
api_pub_key_path: payKey/lakala/prod/tk_api_public_key.txt
|
||||
|
||||
@ -172,7 +172,7 @@ lakala:
|
||||
client_id: lsycs
|
||||
client_secret: XPa1HB5d55Ig0qV8
|
||||
user_no: 29153396
|
||||
split_lowest_ratio: 94.00
|
||||
split_lowest_ratio: 20.00
|
||||
activity_id: 687
|
||||
wx_fee: 0.6 # 微信手续费 6/1000
|
||||
api_pub_key_path: payKey/lakala/dev/tk_api_public_key.txt
|
||||
|
||||
@ -172,7 +172,7 @@ lakala:
|
||||
client_id: lsycs
|
||||
client_secret: XPa1HB5d55Ig0qV8
|
||||
user_no: 29153396
|
||||
split_lowest_ratio: 94.00
|
||||
split_lowest_ratio: 20.00
|
||||
activity_id: 687
|
||||
wx_fee: 0.6 # 微信手续费 6/1000
|
||||
api_pub_key_path: payKey/lakala/dev/tk_api_public_key.txt
|
||||
|
||||
@ -17,15 +17,15 @@
|
||||
JOIN lkl_area c on c.area_code=b.parent_code and c.type=b.type
|
||||
<where>
|
||||
<if test="bankNo!=null and bankNo!=''">
|
||||
and bank_no = #{bankNo}
|
||||
and a.bank_no = #{bankNo}
|
||||
</if>
|
||||
|
||||
<if test="branchBankNo!=null and branchBankNo!=''">
|
||||
and branch_bank_no = #{branchBankNo}
|
||||
and a.branch_bank_no = #{branchBankNo}
|
||||
</if>
|
||||
|
||||
<if test="branchBankNo!=null and branchBankNo!=''">
|
||||
and branch_bank_no = #{branchBankNo}
|
||||
<if test="clearNo!=null and clearNo!=''">
|
||||
and a.clear_no = #{clearNo}
|
||||
</if>
|
||||
|
||||
<choose>
|
||||
@ -40,7 +40,7 @@
|
||||
<if test="keywords!=null and keywords.size>0">
|
||||
AND (
|
||||
<foreach collection="keywords" item="keyword" separator="AND">
|
||||
branch_bank_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
a.branch_bank_name LIKE CONCAT('%', #{keyword}, '%')
|
||||
</foreach>
|
||||
)
|
||||
</if>
|
||||
|
||||
@ -5,7 +5,8 @@
|
||||
<sql id="Base_Column_List">
|
||||
store_id
|
||||
, user_id, store_name, store_grade_id, store_logo, store_slogan, store_domain, store_area, store_district_id,
|
||||
store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open, store_biz_state, shop_parent_id,
|
||||
store_address, store_latitude, store_longitude, store_is_selfsupport, store_type, store_is_open, store_biz_state,
|
||||
ringtone_is_enable, shop_parent_id, store_2nd_category_id,
|
||||
store_category_id, store_state_id, store_time, store_end_time, product_category_ids, store_o2o_tags,
|
||||
store_o2o_flag, store_o2o_merchant_id, store_circle, subsite_id, lkl_merchant_no, lkl_term_no, wx_qrcode,
|
||||
split_ratio, packing_fee, created_at, updated_at
|
||||
|
||||
File diff suppressed because one or more lines are too long
19038
mall-shop/src/main/resources/static/diy/js/diy.js.bak9023
Normal file
19038
mall-shop/src/main/resources/static/diy/js/diy.js.bak9023
Normal file
File diff suppressed because one or more lines are too long
5498
mall-shop/src/main/resources/templates/diy.html.bak0923
Normal file
5498
mall-shop/src/main/resources/templates/diy.html.bak0923
Normal file
File diff suppressed because it is too large
Load Diff
368
mall-shop/src/main/resources/templates/sign_lkl_ec.html
Normal file
368
mall-shop/src/main/resources/templates/sign_lkl_ec.html
Normal file
@ -0,0 +1,368 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN" xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport">
|
||||
<title>小发同城电子合同签署</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background-color: #f5f5f5;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 100%;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #4CAF50, #2E7D32);
|
||||
color: white;
|
||||
padding: 30px 20px 20px;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 30px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
background-color: #e8f5e9;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.success-icon span {
|
||||
font-size: 40px;
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
.message {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.message p {
|
||||
margin-bottom: 20px;
|
||||
font-size: 16px;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.message p:first-child {
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.link-container {
|
||||
background-color: #f0f9ff;
|
||||
border: 1px dashed #2196F3;
|
||||
border-radius: 12px;
|
||||
padding: 20px 15px;
|
||||
margin: 25px 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.link-title {
|
||||
font-weight: 500;
|
||||
color: #1976D2;
|
||||
margin-bottom: 12px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.link {
|
||||
color: #1976D2;
|
||||
word-break: break-all;
|
||||
font-size: 15px;
|
||||
font-family: monospace;
|
||||
padding: 10px;
|
||||
background-color: #e3f2fd;
|
||||
border-radius: 8px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.app-option {
|
||||
background-color: #fff3e0;
|
||||
border: 1px solid #ffcc80;
|
||||
border-radius: 12px;
|
||||
padding: 20px 15px;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.app-option p {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.app-icon {
|
||||
font-size: 36px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: #fff8e1;
|
||||
border-left: 4px solid #ffc107;
|
||||
padding: 15px;
|
||||
margin: 25px 0;
|
||||
text-align: left;
|
||||
border-radius: 0 6px 6px 0;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.warning strong {
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: #fafafa;
|
||||
padding: 20px 15px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
|
||||
.contact {
|
||||
color: #1976D2;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.contact:hover, .contact:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: linear-gradient(135deg, #2196F3, #1976D2);
|
||||
color: white;
|
||||
text-align: center;
|
||||
border: none;
|
||||
border-radius: 10px;
|
||||
font-size: 17px;
|
||||
font-weight: 500;
|
||||
margin: 20px 0;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 6px rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(1px);
|
||||
box-shadow: 0 2px 3px rgba(33, 150, 243, 0.2);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
background: #e3f2fd;
|
||||
color: #1976D2;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 移动端优化 */
|
||||
@media (max-width: 480px) {
|
||||
.header {
|
||||
padding: 25px 15px 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
}
|
||||
|
||||
.success-icon span {
|
||||
font-size: 35px;
|
||||
}
|
||||
|
||||
.message p {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.message p:first-child {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.link-container, .app-option {
|
||||
padding: 15px 12px;
|
||||
}
|
||||
|
||||
.link {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 超小屏幕优化 */
|
||||
@media (max-width: 360px) {
|
||||
body {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 14px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>小发同城电子合同签署</h1>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<div class="success-icon">
|
||||
<span>✓</span>
|
||||
</div>
|
||||
|
||||
<div class="message">
|
||||
<p>恭喜!您的开店入驻申请已审核通过</p>
|
||||
<p>请在24小时内完成电子合同签署</p>
|
||||
|
||||
<div class="link-container">
|
||||
<div class="link-title">合作方签署链接(24小时内有效)</div>
|
||||
<div class="link" id="signLink" th:text="${resultUrl}">${resultUrl}</div>
|
||||
<button class="btn copy-btn" onclick="copyLink()">复制链接</button>
|
||||
</div>
|
||||
|
||||
<!-- <div class="app-option">-->
|
||||
<!-- <div class="app-icon">📱</div>-->
|
||||
<!-- <p>或前往<strong>小发商家版APP</strong>完成签署</p>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<button class="btn" onclick="openLink()">立即签署合同</button>
|
||||
|
||||
<div class="warning">
|
||||
<strong>注意:</strong>链接有效期为24小时,逾期需重新提交申请。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>如有疑问请联系客服:<a class="contact" href="tel:17777525395">17777525395</a></p>
|
||||
<p>感谢您对小发同城的支持!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script th:inline="javascript">
|
||||
// 获取后端传递的resultUrl变量
|
||||
var resultUrl = '${resultUrl}';
|
||||
|
||||
// 复制链接功能
|
||||
function copyLink() {
|
||||
var linkText = document.getElementById('signLink').textContent;
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(linkText).then(() => {
|
||||
alert('链接已复制到剪贴板');
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err);
|
||||
fallbackCopyTextToClipboard(linkText);
|
||||
});
|
||||
} else {
|
||||
fallbackCopyTextToClipboard(linkText);
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容性复制方法
|
||||
function fallbackCopyTextToClipboard(text) {
|
||||
var textArea = document.createElement("textarea");
|
||||
textArea.value = text;
|
||||
textArea.style.position = "fixed";
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if (successful) {
|
||||
alert('链接已复制到剪贴板');
|
||||
} else {
|
||||
prompt("请手动复制以下链接:", text);
|
||||
}
|
||||
} catch (err) {
|
||||
prompt("请手动复制以下链接:", text);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
}
|
||||
|
||||
// 打开链接功能
|
||||
function openLink() {
|
||||
if (resultUrl) {
|
||||
// 如果是完整URL则直接跳转
|
||||
if (resultUrl.startsWith('http') || resultUrl.startsWith('https')) {
|
||||
window.location.href = resultUrl;
|
||||
} else {
|
||||
// 否则添加协议前缀
|
||||
window.location.href = 'https://' + resultUrl;
|
||||
}
|
||||
} else {
|
||||
alert('链接无效,请联系客服');
|
||||
}
|
||||
}
|
||||
|
||||
// 检测是否在微信中打开
|
||||
var ua = navigator.userAgent.toLowerCase();
|
||||
if (ua.includes('micromessenger')) {
|
||||
// 在微信中显示提示
|
||||
var btn = document.querySelector('.btn');
|
||||
if (btn) {
|
||||
btn.textContent = '点击右上角菜单选择在浏览器中打开';
|
||||
}
|
||||
}
|
||||
|
||||
// 防止页面被嵌入到iframe中
|
||||
if (window.self !== window.top) {
|
||||
window.top.location = window.location;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user