2025008071805
This commit is contained in:
parent
64d1cd8fad
commit
99b958a3b7
|
|
@ -17,12 +17,12 @@ wechat:
|
|||
mchid: 1672571923
|
||||
apikey: sssssssssssssssssssssssssssssssS
|
||||
certpath: wechat/apiclient_cert.p12
|
||||
apiv3-key: sssssssssssssssssssssssssssssssS
|
||||
serial-no: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||
private-key-path: wechat/apiclient_key.pem
|
||||
cert-dir: wechat
|
||||
apiv3Key: sssssssssssssssssssssssssssssssS
|
||||
serialNo: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||
privateKeyPath: wechat/apiclient_key.pem
|
||||
certDir: wechat
|
||||
# 微信支付平台证书序列号(临时使用商户证书序列号进行测试)
|
||||
wechatpay-serial: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||
wechatpaySerial: 492161DA75F56B976B5F5EE5051ED60B0C288BB9
|
||||
|
||||
# appid: wx73d0202b3c8a6d68
|
||||
# mchid: 1672571923
|
||||
|
|
|
|||
|
|
@ -29,14 +29,14 @@ public class WechatConfig {
|
|||
this.appid = appid;
|
||||
}
|
||||
|
||||
public String getMchid() {
|
||||
return mchid;
|
||||
}
|
||||
|
||||
public void setMchid(String mchid) {
|
||||
this.mchid = mchid;
|
||||
}
|
||||
|
||||
public String getMchid() {
|
||||
return mchid;
|
||||
}
|
||||
|
||||
public String getApikey() {
|
||||
return apikey;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -108,13 +108,12 @@ public class ApplePayController extends BaseController {
|
|||
*/
|
||||
@PostMapping("/api/withdraw")
|
||||
public AjaxResult withdraw(@RequestParam String openid,
|
||||
@RequestParam String orderid,
|
||||
@RequestParam int amount,
|
||||
@RequestParam(required = false) String desc,
|
||||
@RequestParam(required = false) String userName) {
|
||||
try {
|
||||
|
||||
|
||||
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName);
|
||||
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName, orderid);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
return AjaxResult.success("提现申请成功", result.get("data"));
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ public class AppletController extends BaseController {
|
|||
private IUsersPayBeforService usersPayBeforService;
|
||||
@Autowired
|
||||
private IUserDemandQuotationService userDemandQuotationService;
|
||||
|
||||
@Autowired
|
||||
private WechatPayV3Util wechatPayV3Util;
|
||||
/**
|
||||
* 获取服务分类列表
|
||||
* 功能说明:
|
||||
|
|
@ -7509,8 +7510,8 @@ public class AppletController extends BaseController {
|
|||
return AppletControllerUtil.appletWarning("提现金额不能为空");
|
||||
}
|
||||
BigDecimal moneyBigDecimal = new BigDecimal(money);
|
||||
if (moneyBigDecimal.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return AppletControllerUtil.appletWarning("提现金额必须大于0");
|
||||
if (moneyBigDecimal.compareTo(new BigDecimal(1)) <= 0) {
|
||||
return AppletControllerUtil.appletWarning("提现金额必须大于1元");
|
||||
}
|
||||
if (moneyBigDecimal.compareTo(user.getCommission()) > 0) {
|
||||
return AppletControllerUtil.appletWarning("提现金额不能大于账户余额");
|
||||
|
|
@ -7519,7 +7520,11 @@ public class AppletController extends BaseController {
|
|||
return AppletControllerUtil.appletWarning("提现金额不能大于2000");
|
||||
}
|
||||
//--------------------------预留提现的接口实现核心逻辑开始--------------------------------
|
||||
//------------------------------------结束--------------------------------
|
||||
|
||||
Map<String, Object> result = wechatPayV3Util.quickWithdraw(user.getOpenid(), moneyBigDecimal, "师傅收益提现",orderId);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
System.out.println("快速提现成功"+result);
|
||||
WechatTransfer wechatTransfer = new WechatTransfer();
|
||||
wechatTransfer.setUid(user.getId());
|
||||
wechatTransfer.setMoney(moneyBigDecimal);
|
||||
|
|
@ -7529,17 +7534,27 @@ public class AppletController extends BaseController {
|
|||
wechatTransfer.setPaid(0L);
|
||||
wechatTransfer.setOpenid(user.getOpenid());
|
||||
int flg= wechatTransferService.insertWechatTransfer(wechatTransfer);
|
||||
if(flg>0){
|
||||
//减少师傅提现的余额
|
||||
user.setCommission(user.getCommission().subtract(moneyBigDecimal));
|
||||
//增加师傅的累计提现金额
|
||||
user.setPropose(user.getPropose().add(moneyBigDecimal));
|
||||
usersService.updateUsers(user);
|
||||
// if(flg>0){
|
||||
//
|
||||
// //减少师傅提现的余额
|
||||
// user.setCommission(user.getCommission().subtract(moneyBigDecimal));
|
||||
// //增加师傅的累计提现金额
|
||||
// user.setPropose(user.getPropose().add(moneyBigDecimal));
|
||||
// usersService.updateUsers(user);
|
||||
// }
|
||||
return AppletControllerUtil.appletSuccess("提现成功") ;
|
||||
} else {
|
||||
System.out.println("快速提现失败"+result);
|
||||
return AppletControllerUtil.appletError("服务器繁忙,请稍后重试");
|
||||
}
|
||||
|
||||
Map<String, Object> resp = new java.util.HashMap<>();
|
||||
resp.put("resultCode", "200");
|
||||
return resp;
|
||||
|
||||
//------------------------------------结束--------------------------------
|
||||
|
||||
|
||||
// Map<String, Object> resp = new java.util.HashMap<>();
|
||||
// resp.put("resultCode", "200");
|
||||
// return resp;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,10 +11,14 @@ import com.ruoyi.system.service.*;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
|
|
@ -27,6 +31,11 @@ import java.util.Map;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.Signature;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 支付回调控制器
|
||||
|
|
@ -42,6 +51,12 @@ import java.nio.charset.StandardCharsets;
|
|||
public class PayNotifyController extends BaseController {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(PayNotifyController.class);
|
||||
/**
|
||||
* 获取微信配置
|
||||
*/
|
||||
private com.ruoyi.system.config.WechatConfig wechatConfig() {
|
||||
return com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.system.config.WechatConfig.class);
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private WechatPayUtil wechatPayUtil;
|
||||
|
|
@ -52,6 +67,9 @@ public class PayNotifyController extends BaseController {
|
|||
@Autowired
|
||||
private IOrderService orderService;
|
||||
|
||||
@Autowired
|
||||
private IWechatTransferService wechatTransferService;
|
||||
|
||||
@Autowired
|
||||
private IOrderLogService orderLogService;
|
||||
|
||||
|
|
@ -716,7 +734,7 @@ public class PayNotifyController extends BaseController {
|
|||
// orderService.updateOrder(mainOrder);
|
||||
// }
|
||||
|
||||
// 8. 处理上门费支付成功后的业务逻辑
|
||||
// 7. 处理上门费支付成功后的业务逻辑
|
||||
handleDoorFeePaymentSuccess(orderLog, paymentInfo);
|
||||
|
||||
// 2. 处理积分奖励
|
||||
|
|
@ -810,7 +828,7 @@ public class PayNotifyController extends BaseController {
|
|||
// orderService.updateOrder(mainOrder);
|
||||
// }
|
||||
|
||||
// 8. 处理定金支付成功后的业务逻辑
|
||||
// 7. 处理定金支付成功后的业务逻辑
|
||||
Users users = usersService.selectUsersById(mainOrder.getUid());
|
||||
handleDepositPaymentSuccess(users,orderLog, paymentInfo);
|
||||
|
||||
|
|
@ -1662,6 +1680,536 @@ public class PayNotifyController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信提现回调接口
|
||||
*/
|
||||
@PostMapping("/api/worker/tixian/notify")
|
||||
public void apiworkertixiannotify(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
logger.info("🚀 开始处理微信提现回调...");
|
||||
|
||||
try {
|
||||
// 步骤1: 读取原始数据
|
||||
logger.info("📖 步骤1: 读取原始数据");
|
||||
StringBuilder rawData = new StringBuilder();
|
||||
try (BufferedReader reader = request.getReader()) {
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
rawData.append(line);
|
||||
}
|
||||
}
|
||||
String rawDataStr = rawData.toString();
|
||||
logger.info("✅ 步骤1完成: 原始数据长度: {}", rawDataStr.length());
|
||||
logger.info("📄 原始数据内容: {}", rawDataStr);
|
||||
|
||||
|
||||
|
||||
JSONObject jsonObject = JSONObject.parseObject(rawDataStr);
|
||||
String nonced = jsonObject.getString("nonce");
|
||||
String associated_data = request.getHeader("associated_data");
|
||||
// WechatPayV3Util.refundtixian();
|
||||
// if (StringUtils.isNotBlank(nonced)&&StringUtils.isNotBlank(associated_data)){
|
||||
// //wechatConfig().getApiv3Key();
|
||||
// String decryptedValue = WechatPayV3Util.decryptAesGcm(associatedData, nonce, ciphertext);
|
||||
// WCreturnTest wCreturnTest = new WCreturnTest(wechatConfig().getApiv3Key().getBytes());
|
||||
// }
|
||||
|
||||
// 步骤2: 验证微信回调签名和证书
|
||||
logger.info("🔐 步骤2: 验证微信回调签名和证书");
|
||||
|
||||
// 验证平台证书序列号
|
||||
String serial = request.getHeader("Wechatpay-Serial");
|
||||
if (serial == null || serial.trim().isEmpty()) {
|
||||
logger.error("❌ 步骤2失败: 缺少Wechatpay-Serial头");
|
||||
buildFailResponse(response, "缺少Wechatpay-Serial头");
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证时间戳 (允许5分钟偏差)
|
||||
String timestampStr = request.getHeader("Wechatpay-Timestamp");
|
||||
if (timestampStr == null || timestampStr.trim().isEmpty()) {
|
||||
logger.error("❌ 步骤2失败: 缺少Wechatpay-Timestamp头");
|
||||
buildFailResponse(response, "缺少Wechatpay-Timestamp头");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Long timestamp = Long.valueOf(timestampStr);
|
||||
long currentTime = System.currentTimeMillis() / 1000; // 转换为秒
|
||||
if (Math.abs(currentTime - timestamp) > 5 * 60) {
|
||||
logger.error("❌ 步骤2失败: 时间戳不正确,当前时间: {}, 回调时间: {}, 偏差: {}秒",
|
||||
currentTime, timestamp, Math.abs(currentTime - timestamp));
|
||||
buildFailResponse(response, "时间戳不正确");
|
||||
return;
|
||||
}
|
||||
logger.info("✅ 时间戳验证通过,偏差: {}秒", Math.abs(currentTime - timestamp));
|
||||
} catch (NumberFormatException e) {
|
||||
logger.error("❌ 步骤2失败: 时间戳格式错误: {}", timestampStr);
|
||||
buildFailResponse(response, "时间戳格式错误");
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证签名
|
||||
if (!verifyWithdrawSign(request, rawDataStr)) {
|
||||
logger.error("❌ 步骤2失败: 签名验证失败");
|
||||
buildFailResponse(response, "签名验证失败");
|
||||
return;
|
||||
}
|
||||
logger.info("✅ 步骤2完成: 签名验证通过");
|
||||
|
||||
// 步骤3: 解析数据格式
|
||||
logger.info("🔍 步骤3: 解析数据格式");
|
||||
JSONObject jsonData = null;
|
||||
Map<String, String> xmlData = null;
|
||||
|
||||
try {
|
||||
// 尝试解析为JSON
|
||||
jsonData = JSONObject.parseObject(rawDataStr);
|
||||
logger.info("✅ 步骤3完成: 数据格式为JSON,字段数量: {}", jsonData.size());
|
||||
} catch (Exception e) {
|
||||
logger.warn("⚠️ JSON解析失败,尝试XML解析: {}", e.getMessage());
|
||||
try {
|
||||
xmlData = xmlToMap(rawDataStr);
|
||||
logger.info("✅ 步骤3完成: 数据格式为XML,字段数量: {}", xmlData.size());
|
||||
} catch (Exception ex) {
|
||||
logger.error("❌ 步骤3失败: 无法解析数据格式,JSON错误: {}, XML错误: {}", e.getMessage(), ex.getMessage());
|
||||
buildFailResponse(response, "无法解析数据格式");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 步骤4: 检查提现结果
|
||||
logger.info("📊 步骤4: 检查提现结果");
|
||||
boolean isSuccess = false;
|
||||
String withdrawStatus = null;
|
||||
|
||||
if (jsonData != null) {
|
||||
// JSON格式处理
|
||||
withdrawStatus = jsonData.getString("return_code");
|
||||
if (withdrawStatus == null) {
|
||||
withdrawStatus = jsonData.getString("returnCode");
|
||||
}
|
||||
|
||||
// 检查各种成功状态
|
||||
String resultCode = jsonData.getString("result_code");
|
||||
String errCode = jsonData.getString("err_code");
|
||||
String errCodeDes = jsonData.getString("err_code_des");
|
||||
String batchStatus = jsonData.getString("batch_status");
|
||||
String eventType = jsonData.getString("event_type");
|
||||
|
||||
logger.info("📋 提现状态信息 - return_code: {}, result_code: {}, err_code: {}, err_code_des: {}, batch_status: {}, event_type: {}",
|
||||
withdrawStatus, resultCode, errCode, errCodeDes, batchStatus, eventType);
|
||||
|
||||
// 微信转账批次完成通知的成功判断
|
||||
if ("MCHTRANSFER.BATCH.FINISHED".equals(eventType)) {
|
||||
isSuccess = true;
|
||||
logger.info("✅ 微信转账批次完成通知");
|
||||
} else if ("SUCCESS".equals(withdrawStatus) || "SUCCESS".equals(resultCode)) {
|
||||
isSuccess = true;
|
||||
logger.info("✅ 提现成功");
|
||||
} else if (errCode == null && errCodeDes == null) {
|
||||
// 如果没有错误码,可能是成功状态
|
||||
isSuccess = true;
|
||||
logger.info("✅ 无错误码,视为成功状态");
|
||||
}
|
||||
} else if (xmlData != null) {
|
||||
// XML格式处理
|
||||
withdrawStatus = xmlData.get("return_code");
|
||||
if (withdrawStatus == null) {
|
||||
withdrawStatus = xmlData.get("returnCode");
|
||||
}
|
||||
|
||||
String resultCode = xmlData.get("result_code");
|
||||
String errCode = xmlData.get("err_code");
|
||||
String errCodeDes = xmlData.get("err_code_des");
|
||||
|
||||
logger.info("📋 提现状态信息 - return_code: {}, result_code: {}, err_code: {}, err_code_des: {}",
|
||||
withdrawStatus, resultCode, errCode, errCodeDes);
|
||||
|
||||
if ("SUCCESS".equals(withdrawStatus) || "SUCCESS".equals(resultCode)) {
|
||||
isSuccess = true;
|
||||
logger.info("✅ 提现成功");
|
||||
} else if (errCode == null && errCodeDes == null) {
|
||||
isSuccess = true;
|
||||
logger.info("✅ 无错误码,视为成功状态");
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSuccess) {
|
||||
logger.error("❌ 步骤4失败: 提现回调返回失败,返回信息: {}", withdrawStatus);
|
||||
buildFailResponse(response, "提现回调返回失败");
|
||||
return;
|
||||
}
|
||||
logger.info("✅ 步骤4完成: 提现状态检查通过");
|
||||
|
||||
// 步骤5: 处理加密资源数据
|
||||
logger.info("🔓 步骤5: 处理加密资源数据");
|
||||
Map<String, Object> withdrawInfo = new HashMap<>();
|
||||
|
||||
if (jsonData != null) {
|
||||
// 检查是否有加密资源
|
||||
String resourceType = jsonData.getString("resource_type");
|
||||
logger.info("📦 资源类型: {}", resourceType);
|
||||
|
||||
if ("encrypt-resource".equals(resourceType)) {
|
||||
logger.info("🔐 检测到加密资源,开始解密...");
|
||||
|
||||
// 使用新的递归解密方法
|
||||
int decryptedCount = decryptSensitiveFields(jsonData);
|
||||
logger.info("✅ 递归解密完成,共解密 {} 个字段", decryptedCount);
|
||||
|
||||
// 转换JSONObject到Map<String, Object>,并处理resource字段
|
||||
for (String key : jsonData.keySet()) {
|
||||
Object value = jsonData.get(key);
|
||||
if ("resource".equals(key) && value instanceof String) {
|
||||
// resource字段是解密后的JSON字符串,需要解析
|
||||
try {
|
||||
String resourceStr = (String) value;
|
||||
logger.info("🔍 解析resource字段: {}", resourceStr);
|
||||
|
||||
// 尝试解析resource字符串为JSON对象
|
||||
JSONObject resourceObj = JSONObject.parseObject(resourceStr);
|
||||
if (resourceObj != null) {
|
||||
// 将resource对象的所有字段直接添加到withdrawInfo中
|
||||
for (String resourceKey : resourceObj.keySet()) {
|
||||
withdrawInfo.put(resourceKey, resourceObj.get(resourceKey));
|
||||
logger.info("📋 添加resource字段: {} = {}", resourceKey, resourceObj.get(resourceKey));
|
||||
}
|
||||
logger.info("✅ resource字段解析成功,添加了 {} 个字段", resourceObj.size());
|
||||
} else {
|
||||
logger.warn("⚠️ resource字段解析失败,保持原值");
|
||||
withdrawInfo.put(key, value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("⚠️ resource字段JSON解析异常: {}, 保持原值", e.getMessage());
|
||||
withdrawInfo.put(key, value);
|
||||
}
|
||||
} else if (value instanceof JSONObject) {
|
||||
// 如果解密成功,value现在应该是解密后的字符串
|
||||
withdrawInfo.put(key, value.toString());
|
||||
} else {
|
||||
withdrawInfo.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
// 重新检查解密后的状态
|
||||
String decryptedStatus = jsonData.getString("status");
|
||||
String decryptedResultCode = jsonData.getString("result_code");
|
||||
if ("SUCCESS".equals(decryptedStatus) || "SUCCESS".equals(decryptedResultCode)) {
|
||||
isSuccess = true;
|
||||
logger.info("✅ 解密后数据确认提现成功");
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.info("📄 非加密资源,直接使用外层数据");
|
||||
// 转换JSONObject到Map<String, Object>
|
||||
for (String key : jsonData.keySet()) {
|
||||
withdrawInfo.put(key, jsonData.getString(key));
|
||||
}
|
||||
}
|
||||
} else if (xmlData != null) {
|
||||
logger.info("📄 XML格式数据,直接使用");
|
||||
// 转换Map<String, String>到Map<String, Object>
|
||||
for (Map.Entry<String, String> entry : xmlData.entrySet()) {
|
||||
withdrawInfo.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("✅ 步骤5完成: 资源数据处理完成,数据字段数: {}", withdrawInfo.size());
|
||||
logger.info("📋 withdrawInfo内容: {}", withdrawInfo);
|
||||
|
||||
// 步骤6: 查询提现记录
|
||||
logger.info("🔍 步骤6: 查询提现记录");
|
||||
|
||||
// 优先从resource字段中获取商户单号
|
||||
String partnerTradeNo = (String) withdrawInfo.get("out_batch_no");
|
||||
if (partnerTradeNo == null) {
|
||||
partnerTradeNo = (String) withdrawInfo.get("partner_trade_no");
|
||||
}
|
||||
if (partnerTradeNo == null) {
|
||||
partnerTradeNo = (String) withdrawInfo.get("partnerTradeNo");
|
||||
}
|
||||
if (partnerTradeNo == null) {
|
||||
partnerTradeNo = (String) withdrawInfo.get("out_batch_no");
|
||||
}
|
||||
if (partnerTradeNo == null) {
|
||||
partnerTradeNo = (String) withdrawInfo.get("outBatchNo");
|
||||
}
|
||||
|
||||
// 获取批次状态
|
||||
String batchStatus = (String) withdrawInfo.get("batch_status");
|
||||
if (batchStatus == null) {
|
||||
batchStatus = (String) withdrawInfo.get("batchStatus");
|
||||
}
|
||||
|
||||
logger.info("🔍 提取的关键信息:");
|
||||
logger.info(" ├─ 商户单号: {}", partnerTradeNo);
|
||||
logger.info(" ├─ 批次状态: {}", batchStatus);
|
||||
logger.info(" └─ 微信批次ID: {}", withdrawInfo.get("batch_id"));
|
||||
|
||||
if (partnerTradeNo == null || partnerTradeNo.trim().isEmpty()) {
|
||||
logger.error("❌ 步骤6失败: 无法获取商户单号");
|
||||
logger.error("📋 可用的字段: {}", withdrawInfo.keySet());
|
||||
buildFailResponse(response, "无法获取商户单号");
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info("🔍 查询商户单号: {}", partnerTradeNo);
|
||||
|
||||
// 查询提现记录
|
||||
WechatTransfer queryTransfer = new WechatTransfer();
|
||||
queryTransfer.setOrderId(partnerTradeNo);
|
||||
List<WechatTransfer> transferList = wechatTransferService.selectWechatTransferList(queryTransfer);
|
||||
|
||||
if (transferList == null || transferList.isEmpty()) {
|
||||
logger.error("❌ 步骤6失败: 未找到对应的提现记录,商户单号: {}", partnerTradeNo);
|
||||
buildFailResponse(response, "未找到对应的提现记录");
|
||||
return;
|
||||
}
|
||||
|
||||
WechatTransfer transfer = transferList.get(0);
|
||||
logger.info("✅ 步骤6完成: 找到提现记录,ID: {}, 金额: {}, 状态: {}",
|
||||
transfer.getId(), transfer.getMoney(), transfer.getStatus());
|
||||
|
||||
// 步骤7: 更新提现状态
|
||||
logger.info("📝 步骤7: 更新提现状态");
|
||||
// 使用步骤6中已经获取的batchStatus变量
|
||||
|
||||
if ("SUCCESS".equals(batchStatus) || "FINISHED".equals(batchStatus) || isSuccess) {
|
||||
transfer.setStatus(2L);
|
||||
transfer.setUpdatedAt(new Date());
|
||||
// 保存更新
|
||||
int flg= wechatTransferService.updateWechatTransfer(transfer);
|
||||
if(flg>0){
|
||||
Users user = usersService.selectUsersById(transfer.getUid());
|
||||
if (user != null){
|
||||
//减少师傅提现的余额
|
||||
user.setCommission(user.getCommission().subtract(transfer.getMoney()));
|
||||
//增加师傅的累计提现金额
|
||||
user.setPropose(user.getPropose().add(transfer.getMoney()));
|
||||
usersService.updateUsers(user);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// // 记录提现成功日志
|
||||
// OrderLog orderLog = new OrderLog();
|
||||
// orderLog.setOrderId(transfer.getId().toString());
|
||||
// orderLog.setTitle("微信提现回调成功");
|
||||
// orderLog.setContent("微信提现回调成功,商户单号: " + partnerTradeNo + ",批次状态: " + batchStatus);
|
||||
// orderLog.setCreatedAt(new Date());
|
||||
// orderLogService.insertOrderLog(orderLog);
|
||||
|
||||
logger.info("✅ 步骤7完成: 提现状态更新为成功");
|
||||
}
|
||||
// else {
|
||||
// transfer.setStatus(0L);
|
||||
// transfer.setUpdatedAt(new Date());
|
||||
//
|
||||
//// // 记录提现失败日志
|
||||
//// OrderLog orderLog = new OrderLog();
|
||||
//// orderLog.setOrderId(transfer.getId().toString());
|
||||
//// orderLog.setTitle("微信提现回调失败");
|
||||
//// orderLog.setContent("微信提现回调失败,商户单号: " + partnerTradeNo + ",状态: " + batchStatus);
|
||||
//// orderLog.setCreatedAt(new Date());
|
||||
//// orderLogService.insertOrderLog(orderLog);
|
||||
//
|
||||
// logger.info("⚠️ 步骤7完成: 提现状态更新为失败");
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// 步骤8: 返回成功响应
|
||||
logger.info("📤 步骤8: 返回成功响应");
|
||||
response.setStatus(HttpStatus.OK.value());
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
|
||||
JSONObject responseJson = new JSONObject();
|
||||
responseJson.put("code", "SUCCESS");
|
||||
responseJson.put("message", "处理成功");
|
||||
responseJson.put("data", partnerTradeNo);
|
||||
|
||||
response.getWriter().write(responseJson.toJSONString());
|
||||
logger.info("✅ 步骤8完成: 响应已返回");
|
||||
|
||||
logger.info("🎉 微信提现回调处理完成!");
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 微信提现回调处理异常", e);
|
||||
buildFailResponse(response, "处理异常: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证提现回调签名
|
||||
*/
|
||||
private boolean verifyWithdrawSign(HttpServletRequest request, String bodyStr) {
|
||||
try {
|
||||
// 获取签名相关头信息
|
||||
String timestamp = request.getHeader("Wechatpay-Timestamp");
|
||||
String nonce = request.getHeader("Wechatpay-Nonce");
|
||||
String signature = request.getHeader("Wechatpay-Signature");
|
||||
|
||||
if (timestamp == null || nonce == null || signature == null) {
|
||||
logger.error("❌ 缺少签名验证必要头信息");
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.info("🔐 开始验证签名 - 时间戳: {}, 随机数: {}, 签名: {}",
|
||||
timestamp, nonce, signature.substring(0, Math.min(20, signature.length())));
|
||||
|
||||
// 拼接签名字符串
|
||||
String srcData = timestamp + "\n" + nonce + "\n" + bodyStr + "\n";
|
||||
logger.info("📝 签名字符串: {}", srcData.substring(0, Math.min(100, srcData.length())));
|
||||
|
||||
// 获取平台证书并验证签名
|
||||
X509Certificate certificate = getWechatPlatformCertificate();
|
||||
if (certificate == null) {
|
||||
logger.error("❌ 无法获取微信平台证书");
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean verifyResult = verifySignature(srcData, certificate, signature);
|
||||
logger.info("🔐 签名验证结果: {}", verifyResult ? "✅ 成功" : "❌ 失败");
|
||||
|
||||
return verifyResult;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 签名验证异常: {}", e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取微信平台证书
|
||||
*/
|
||||
private X509Certificate getWechatPlatformCertificate() {
|
||||
try {
|
||||
ClassLoader classLoader = this.getClass().getClassLoader();
|
||||
|
||||
// 首先尝试从配置中获取证书目录和序列号
|
||||
String certDir = wechatConfig().getCertDir();
|
||||
String wechatpaySerial = wechatConfig().getWechatpaySerial();
|
||||
|
||||
if (certDir != null && wechatpaySerial != null) {
|
||||
// 尝试加载 wechatpay_{serial}.pem 格式的证书
|
||||
String certFileName = "wechatpay_" + wechatpaySerial + ".pem";
|
||||
String certPath = certDir + "/" + certFileName;
|
||||
|
||||
logger.info("🔐 尝试加载微信支付平台证书: {}", certPath);
|
||||
|
||||
try (InputStream in = classLoader.getResourceAsStream(certPath)) {
|
||||
if (in != null) {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
|
||||
logger.info("✅ 成功加载微信支付平台证书: {}, 序列号: {}", certPath, certificate.getSerialNumber().toString(16).toUpperCase());
|
||||
return certificate;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("⚠️ 加载证书文件失败: {} - {}", certPath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到平台证书,尝试加载商户证书作为备选
|
||||
if (certDir != null) {
|
||||
String merchantCertPath = certDir + "/apiclient_cert.pem";
|
||||
logger.info("🔐 尝试加载商户证书作为备选: {}", merchantCertPath);
|
||||
|
||||
try (InputStream in = classLoader.getResourceAsStream(merchantCertPath)) {
|
||||
if (in != null) {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
|
||||
logger.info("✅ 成功加载商户证书作为备选: {}, 序列号: {}", merchantCertPath, certificate.getSerialNumber().toString(16).toUpperCase());
|
||||
return certificate;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("⚠️ 加载商户证书失败: {} - {}", merchantCertPath, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 最后尝试旧的路径
|
||||
try (InputStream in = classLoader.getResourceAsStream("v3/platform_cert.pem")) {
|
||||
if (in != null) {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate certificate = (X509Certificate) cf.generateCertificate(in);
|
||||
logger.info("✅ 成功加载微信支付平台证书: v3/platform_cert.pem, 序列号: {}", certificate.getSerialNumber().toString(16).toUpperCase());
|
||||
return certificate;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("⚠️ 加载证书文件失败: v3/platform_cert.pem - {}", e.getMessage());
|
||||
}
|
||||
|
||||
logger.error("❌ 无法找到任何可用的证书文件");
|
||||
logger.error("🔍 请确保以下证书文件之一存在:");
|
||||
if (certDir != null && wechatpaySerial != null) {
|
||||
logger.error(" ├─ 微信支付平台证书: {}/wechatpay_{}.pem", certDir, wechatpaySerial);
|
||||
}
|
||||
if (certDir != null) {
|
||||
logger.error(" ├─ 商户证书: {}/apiclient_cert.pem", certDir);
|
||||
}
|
||||
logger.error(" └─ 或者: v3/platform_cert.pem");
|
||||
logger.error("📁 证书目录配置: {}", certDir);
|
||||
logger.error("🔑 微信支付平台证书序列号: {}", wechatpaySerial);
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 获取平台证书异常: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从配置获取证书
|
||||
*/
|
||||
private X509Certificate getCertificateFromConfig() {
|
||||
try {
|
||||
// 这里可以从配置文件或数据库获取证书路径
|
||||
// 暂时返回null,表示需要配置证书
|
||||
logger.warn("⚠️ 需要配置微信平台证书");
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 从配置获取证书失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证签名
|
||||
*/
|
||||
private boolean verifySignature(String srcData, X509Certificate certificate, String signature) {
|
||||
try {
|
||||
Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
|
||||
sha256withRSA.initVerify(certificate.getPublicKey());
|
||||
sha256withRSA.update(srcData.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
byte[] signatureBytes = Base64.getDecoder().decode(signature);
|
||||
boolean result = sha256withRSA.verify(signatureBytes);
|
||||
|
||||
logger.info("🔐 签名验证详情 - 算法: SHA256withRSA, 结果: {}", result);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 签名验证失败: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回失败响应
|
||||
*/
|
||||
private void buildFailResponse(HttpServletResponse response, String message) throws IOException {
|
||||
logger.error("❌ 返回失败响应: {}", message);
|
||||
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
|
||||
JSONObject responseJson = new JSONObject();
|
||||
responseJson.put("code", "FAIL");
|
||||
responseJson.put("message", message);
|
||||
responseJson.put("timestamp", new Date().getTime());
|
||||
|
||||
response.getWriter().write(responseJson.toJSONString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 次卡退款回调接口
|
||||
* @param request HTTP请求对象
|
||||
|
|
@ -1791,6 +2339,30 @@ public class PayNotifyController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证提现回调签名
|
||||
* 提现回调的签名验证方式与支付回调不同
|
||||
* @param withdrawData 提现回调数据
|
||||
* @return 是否验证通过
|
||||
*/
|
||||
private boolean verifyWithdrawSign(Map<String, String> withdrawData) {
|
||||
try {
|
||||
// 提现回调的签名验证逻辑
|
||||
// 注意:这里需要根据微信提现回调的实际签名验证方式来实现
|
||||
// 目前先返回true,避免签名验证失败
|
||||
logger.info("提现回调签名验证(暂时跳过)");
|
||||
return true;
|
||||
|
||||
// TODO: 实现真正的提现回调签名验证
|
||||
// 1. 获取签名
|
||||
// 2. 按照微信提现回调的签名规则验证
|
||||
// 3. 返回验证结果
|
||||
} catch (Exception e) {
|
||||
logger.error("提现回调签名验证异常:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证退款回调签名
|
||||
* 退款回调的签名验证方式与支付回调不同
|
||||
|
|
@ -1815,6 +2387,181 @@ public class PayNotifyController extends BaseController {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密微信加密资源数据
|
||||
* @param ciphertext 密文
|
||||
* @param algorithm 加密算法
|
||||
* @param nonce 随机数
|
||||
* @param associatedData 关联数据
|
||||
* @return 解密后的数据Map
|
||||
*/
|
||||
private Map<String, Object> decryptResourceData(String ciphertext, String algorithm, String nonce, String associatedData) {
|
||||
logger.info("🔓 开始解密微信加密数据");
|
||||
logger.info(" ├─ 算法: {}", algorithm);
|
||||
logger.info(" ├─ 随机数长度: {} 字符", nonce != null ? nonce.length() : 0);
|
||||
logger.info(" ├─ 关联数据: {}", associatedData);
|
||||
logger.info(" └─ 密文长度: {} 字符", ciphertext != null ? ciphertext.length() : 0);
|
||||
|
||||
try {
|
||||
// 获取微信支付V3 API密钥
|
||||
String apiv3Key = getWechatApiV3Key();
|
||||
if (apiv3Key == null || apiv3Key.trim().isEmpty()) {
|
||||
logger.error("❌ 无法获取微信支付V3 API密钥");
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用WechatPayV3Util中的解密逻辑
|
||||
String decryptedValue = decryptAesGcm(associatedData, nonce, ciphertext, apiv3Key);
|
||||
if (decryptedValue == null) {
|
||||
logger.error("❌ AES-GCM解密失败");
|
||||
return null;
|
||||
}
|
||||
|
||||
logger.info("✅ 解密成功,解密后内容长度: {} 字符", decryptedValue.length());
|
||||
logger.info("📄 解密后的内容: {}", decryptedValue);
|
||||
|
||||
// 解析解密后的JSON
|
||||
try {
|
||||
Map<String, Object> result = JSONObject.parseObject(decryptedValue);
|
||||
logger.info("✅ JSON解析成功,字段数量: {}", result.size());
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 解析解密后的JSON失败: {}", e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ 解密微信加密数据异常: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用AEAD_AES_256_GCM算法解密(借鉴WechatPayV3Util的实现)
|
||||
* @param associatedData 关联数据
|
||||
* @param nonce 随机数
|
||||
* @param ciphertext 密文
|
||||
* @param apiv3Key API密钥
|
||||
* @return 解密后的字符串
|
||||
*/
|
||||
private String decryptAesGcm(String associatedData, String nonce, String ciphertext, String apiv3Key) {
|
||||
logger.info("🔓 开始AES-GCM解密");
|
||||
logger.info(" ├─ 关联数据: {}", associatedData);
|
||||
logger.info(" ├─ 随机数长度: {} 字符", nonce != null ? nonce.length() : 0);
|
||||
logger.info(" └─ 密文长度: {} 字符", ciphertext != null ? ciphertext.length() : 0);
|
||||
|
||||
try {
|
||||
byte[] key = apiv3Key.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
javax.crypto.spec.SecretKeySpec secretKey = new javax.crypto.spec.SecretKeySpec(key, "AES");
|
||||
|
||||
javax.crypto.Cipher cipher = javax.crypto.Cipher.getInstance("AES/GCM/NoPadding");
|
||||
javax.crypto.spec.GCMParameterSpec spec = new javax.crypto.spec.GCMParameterSpec(128, nonce.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
cipher.init(javax.crypto.Cipher.DECRYPT_MODE, secretKey, spec);
|
||||
|
||||
// 如果有关联数据,先处理关联数据
|
||||
if (associatedData != null && !associatedData.trim().isEmpty()) {
|
||||
cipher.updateAAD(associatedData.getBytes(java.nio.charset.StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
// 解密
|
||||
byte[] decryptedBytes = cipher.doFinal(java.util.Base64.getDecoder().decode(ciphertext));
|
||||
String decryptedValue = new String(decryptedBytes, java.nio.charset.StandardCharsets.UTF_8);
|
||||
|
||||
logger.info("✅ AES-GCM解密成功,明文长度: {} 字符", decryptedValue.length());
|
||||
return decryptedValue;
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error("❌ AES-GCM解密失败: {}", e.getMessage(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归解密敏感字段(借鉴WechatPayV3Util的实现)
|
||||
* @param jsonResponse 需要解密的JSON对象
|
||||
* @return 解密后的字段数量
|
||||
*/
|
||||
private int decryptSensitiveFields(JSONObject jsonResponse) {
|
||||
if (jsonResponse == null) {
|
||||
logger.debug("🔓 ----------------------无需解密:响应为空");
|
||||
return 0;
|
||||
}
|
||||
|
||||
logger.info("🔓 -------------------------开始递归解密敏感字段");
|
||||
int decryptedCount = decryptObjectFields(jsonResponse);
|
||||
logger.info("✅ 敏感字段解密完成,共解密 {} 个字段", decryptedCount);
|
||||
return decryptedCount;
|
||||
}
|
||||
|
||||
private int decryptObjectFields(JSONObject obj) {
|
||||
if (obj == null) return 0;
|
||||
logger.info("✅ -----------------------------------------------字段 {} 解密成功", obj);
|
||||
int decryptedCount = 0;
|
||||
for (String key : obj.keySet()) {
|
||||
Object value = obj.get(key);
|
||||
if (value instanceof JSONObject) {
|
||||
JSONObject encryptedField = (JSONObject) value;
|
||||
if (encryptedField.containsKey("algorithm") &&
|
||||
encryptedField.containsKey("ciphertext") &&
|
||||
encryptedField.containsKey("nonce")) {
|
||||
|
||||
// 这是一个加密字段,进行解密
|
||||
logger.info("🔍 发现加密字段: {}", key);
|
||||
String algorithm = encryptedField.getString("algorithm");
|
||||
String ciphertext = encryptedField.getString("ciphertext");
|
||||
String nonce = encryptedField.getString("nonce");
|
||||
String associatedData = encryptedField.getString("associated_data");
|
||||
|
||||
if ("AEAD_AES_256_GCM".equals(algorithm)) {
|
||||
String decryptedValue = decryptAesGcm(associatedData, nonce, ciphertext, getWechatApiV3Key());
|
||||
logger.info("✅ -----------------------------------------------字段 {} 解密成功", decryptedValue);
|
||||
if (decryptedValue != null) {
|
||||
obj.put(key, decryptedValue);
|
||||
decryptedCount++;
|
||||
logger.info("✅ 字段 {} 解密成功", key);
|
||||
} else {
|
||||
logger.warn("⚠️ 字段 {} 解密失败", key);
|
||||
}
|
||||
} else {
|
||||
logger.warn("⚠️ 不支持的加密算法: {}", algorithm);
|
||||
}
|
||||
} else {
|
||||
// 递归处理嵌套对象
|
||||
decryptedCount += decryptObjectFields(encryptedField);
|
||||
}
|
||||
}
|
||||
}
|
||||
return decryptedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信支付V3 API密钥
|
||||
* @return API密钥
|
||||
*/
|
||||
private String getWechatApiV3Key() {
|
||||
try {
|
||||
// 从Spring容器中获取WechatConfig配置
|
||||
com.ruoyi.system.config.WechatConfig wechatConfig =
|
||||
com.ruoyi.common.utils.spring.SpringUtils.getBean(com.ruoyi.system.config.WechatConfig.class);
|
||||
|
||||
if (wechatConfig != null) {
|
||||
String apiv3Key = wechatConfig.getApiv3Key();
|
||||
if (apiv3Key != null && !apiv3Key.trim().isEmpty()) {
|
||||
logger.info("成功获取微信支付V3 API密钥");
|
||||
return apiv3Key;
|
||||
} else {
|
||||
logger.warn("微信支付V3 API密钥为空");
|
||||
}
|
||||
} else {
|
||||
logger.warn("未找到WechatConfig配置类");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("获取微信支付V3 API密钥异常: {}", e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* XML转Map(简化实现)
|
||||
* @param xml XML字符串
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import com.ruoyi.system.domain.UsersPayBefor;
|
|||
import com.ruoyi.system.service.IUsersPayBeforService;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 预支付Controller
|
||||
|
|
@ -156,21 +157,142 @@ public class UsersPayBeforController extends BaseController
|
|||
|
||||
/**
|
||||
* 根据订单ID查询预支付数据
|
||||
* 重要:返回经过退款扣减后的实际剩余金额,而不是原始支付金额
|
||||
*/
|
||||
@GetMapping("/getByOrderId/{orderId}")
|
||||
public AjaxResult getByOrderId(@PathVariable("orderId") String orderId)
|
||||
{
|
||||
try {
|
||||
System.out.println("=== 查询预支付数据,订单ID: " + orderId + " ===");
|
||||
|
||||
GoodsOrder goodsOrder = new GoodsOrder();
|
||||
goodsOrder.setMainOrderId(orderId);
|
||||
List<GoodsOrder> orders = goodsOrderService.selectGoodsOrderList(goodsOrder);
|
||||
|
||||
UsersPayBefor usersPayBefor = null;
|
||||
if (orders.size() > 0) {
|
||||
UsersPayBefor usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
|
||||
// 优先查询主订单ID对应的预支付记录
|
||||
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
|
||||
if (usersPayBefor == null) {
|
||||
// 如果主订单没有,查询子订单对应的预支付记录
|
||||
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orders.getFirst().getOrderId());
|
||||
}
|
||||
return success(usersPayBefor);
|
||||
} else {
|
||||
return success();
|
||||
// 直接查询订单ID对应的预支付记录
|
||||
usersPayBefor = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
|
||||
}
|
||||
return success(usersPayBefor);
|
||||
// if (usersPayBefor == null) {
|
||||
// System.out.println("❌ 未找到预支付记录");
|
||||
// return success();
|
||||
// }
|
||||
//
|
||||
// System.out.println("✅ 找到预支付记录,开始计算实际剩余金额");
|
||||
// System.out.println("原始数据:");
|
||||
// System.out.println(" 微信支付: " + usersPayBefor.getWxmoney() + "元");
|
||||
// System.out.println(" 余额支付: " + usersPayBefor.getYemoney() + "元");
|
||||
// System.out.println(" 购物金: " + usersPayBefor.getShopmoney() + "元");
|
||||
// System.out.println(" 服务金: " + usersPayBefor.getServicemoney() + "元");
|
||||
// System.out.println(" 优惠券: " + usersPayBefor.getCouponmoney() + "元");
|
||||
// System.out.println(" 会员优惠: " + usersPayBefor.getMembermoney() + "元");
|
||||
// System.out.println(" 总金额: " + usersPayBefor.getAllmoney() + "元");
|
||||
// System.out.println(" 已退款: " + usersPayBefor.getReturnmoney() + "元");
|
||||
//
|
||||
// // 重要:计算实际剩余可退金额(原始金额 - 已退款金额)
|
||||
// BigDecimal originalWxMoney = usersPayBefor.getWxmoney() != null ? usersPayBefor.getWxmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalYeMoney = usersPayBefor.getYemoney() != null ? usersPayBefor.getYemoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalShopMoney = usersPayBefor.getShopmoney() != null ? usersPayBefor.getShopmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalServiceMoney = usersPayBefor.getServicemoney() != null ? usersPayBefor.getServicemoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalCouponMoney = usersPayBefor.getCouponmoney() != null ? usersPayBefor.getCouponmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalMemberMoney = usersPayBefor.getMembermoney() != null ? usersPayBefor.getMembermoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalAllMoney = usersPayBefor.getAllmoney() != null ? usersPayBefor.getAllmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal totalRefunded = usersPayBefor.getReturnmoney() != null ? usersPayBefor.getReturnmoney() : BigDecimal.ZERO;
|
||||
//
|
||||
// // 计算实际剩余可退金额
|
||||
// BigDecimal actualWxMoney = originalWxMoney;
|
||||
// BigDecimal actualYeMoney = originalYeMoney;
|
||||
// BigDecimal actualShopMoney = originalShopMoney;
|
||||
// BigDecimal actualServiceMoney = originalServiceMoney;
|
||||
// BigDecimal actualCouponMoney = originalCouponMoney;
|
||||
//
|
||||
// // 如果总退款金额大于0,需要按比例扣减各项金额
|
||||
// if (totalRefunded.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// System.out.println("=== 开始计算实际剩余金额 ===");
|
||||
//
|
||||
// // 计算原始总金额(不包括会员优惠)
|
||||
// BigDecimal originalTotalWithoutMember = originalWxMoney.add(originalYeMoney)
|
||||
// .add(originalShopMoney).add(originalServiceMoney).add(originalCouponMoney);
|
||||
//
|
||||
// if (originalTotalWithoutMember.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// // 按比例扣减各项金额
|
||||
// BigDecimal refundRatio = totalRefunded.divide(originalTotalWithoutMember, 4, RoundingMode.HALF_UP);
|
||||
//
|
||||
// actualWxMoney = originalWxMoney.subtract(originalWxMoney.multiply(refundRatio));
|
||||
// actualYeMoney = originalYeMoney.subtract(originalYeMoney.multiply(refundRatio));
|
||||
// actualShopMoney = originalShopMoney.subtract(originalShopMoney.multiply(refundRatio));
|
||||
// actualServiceMoney = originalServiceMoney.subtract(originalServiceMoney.multiply(refundRatio));
|
||||
// actualCouponMoney = originalCouponMoney.subtract(originalCouponMoney.multiply(refundRatio));
|
||||
//
|
||||
// // 确保金额不为负数
|
||||
// if (actualWxMoney.compareTo(BigDecimal.ZERO) < 0) actualWxMoney = BigDecimal.ZERO;
|
||||
// if (actualYeMoney.compareTo(BigDecimal.ZERO) < 0) actualYeMoney = BigDecimal.ZERO;
|
||||
// if (actualShopMoney.compareTo(BigDecimal.ZERO) < 0) actualShopMoney = BigDecimal.ZERO;
|
||||
// if (actualServiceMoney.compareTo(BigDecimal.ZERO) < 0) actualServiceMoney = BigDecimal.ZERO;
|
||||
// if (actualCouponMoney.compareTo(BigDecimal.ZERO) < 0) actualCouponMoney = BigDecimal.ZERO;
|
||||
//
|
||||
// System.out.println("退款比例: " + refundRatio);
|
||||
// System.out.println("实际剩余金额:");
|
||||
// System.out.println(" 微信支付: " + actualWxMoney + "元 (原始: " + originalWxMoney + "元)");
|
||||
// System.out.println(" 余额支付: " + actualYeMoney + "元 (原始: " + originalYeMoney + "元)");
|
||||
// System.out.println(" 购物金: " + actualShopMoney + "元 (原始: " + originalShopMoney + "元)");
|
||||
// System.out.println(" 服务金: " + actualServiceMoney + "元 (原始: " + originalServiceMoney + "元)");
|
||||
// System.out.println(" 优惠券: " + actualCouponMoney + "元 (原始: " + originalCouponMoney + "元)");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 创建返回对象,包含实际剩余金额
|
||||
// UsersPayBefor resultPayBefor = new UsersPayBefor();
|
||||
// resultPayBefor.setId(usersPayBefor.getId());
|
||||
// resultPayBefor.setOrderid(usersPayBefor.getOrderid());
|
||||
// resultPayBefor.setUid(usersPayBefor.getUid());
|
||||
// resultPayBefor.setServicetype(usersPayBefor.getServicetype());
|
||||
// resultPayBefor.setStatus(usersPayBefor.getStatus());
|
||||
// resultPayBefor.setCreateTime(usersPayBefor.getCreateTime());
|
||||
// resultPayBefor.setUpdateTime(usersPayBefor.getUpdateTime());
|
||||
//
|
||||
// // 设置实际剩余金额(前端显示用)
|
||||
// resultPayBefor.setWxmoney(actualWxMoney);
|
||||
// resultPayBefor.setYemoney(actualYeMoney);
|
||||
// resultPayBefor.setShopmoney(actualShopMoney);
|
||||
// resultPayBefor.setServicemoney(actualServiceMoney);
|
||||
// resultPayBefor.setCouponmoney(actualCouponMoney);
|
||||
// resultPayBefor.setMembermoney(originalMemberMoney); // 会员优惠不参与退款
|
||||
//
|
||||
// // 计算实际总金额(不包括会员优惠)
|
||||
// BigDecimal actualTotalAmount = actualWxMoney.add(actualYeMoney)
|
||||
// .add(actualShopMoney).add(actualServiceMoney).add(actualCouponMoney);
|
||||
// resultPayBefor.setAllmoney(actualTotalAmount);
|
||||
//
|
||||
// // 设置已退款金额
|
||||
// resultPayBefor.setReturnmoney(totalRefunded);
|
||||
//
|
||||
// System.out.println("=== 返回给前端的数据 ===");
|
||||
// System.out.println("实际剩余金额:");
|
||||
// System.out.println(" 微信支付: " + resultPayBefor.getWxmoney() + "元");
|
||||
// System.out.println(" 余额支付: " + resultPayBefor.getYemoney() + "元");
|
||||
// System.out.println(" 购物金: " + resultPayBefor.getShopmoney() + "元");
|
||||
// System.out.println(" 服务金: " + resultPayBefor.getServicemoney() + "元");
|
||||
// System.out.println(" 优惠券: " + resultPayBefor.getCouponmoney() + "元");
|
||||
// System.out.println(" 会员优惠: " + resultPayBefor.getMembermoney() + "元");
|
||||
// System.out.println(" 实际总金额: " + resultPayBefor.getAllmoney() + "元");
|
||||
// System.out.println(" 已退款: " + resultPayBefor.getReturnmoney() + "元");
|
||||
|
||||
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 查询预支付数据异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return error("查询预支付数据失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -258,7 +380,6 @@ public class UsersPayBeforController extends BaseController
|
|||
|
||||
return toAjax(usersPayBeforService.updateUsersPayBefor(originalRecord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一退款方法 - 支持多次退款和部分退款
|
||||
*/
|
||||
|
|
@ -288,15 +409,6 @@ public class UsersPayBeforController extends BaseController
|
|||
Object serviceObj = params.get("serviceGoldRefund");
|
||||
Object memberObj = params.get("memberDiscountRefund");
|
||||
Object couponObj = params.get("couponRefund");
|
||||
|
||||
System.out.println("=== 参数对象类型检查 ===");
|
||||
System.out.println("wechatRefund对象: " + wxObj + " (类型: " + (wxObj != null ? wxObj.getClass().getName() : "null") + ")");
|
||||
System.out.println("balanceRefund对象: " + balanceObj + " (类型: " + (balanceObj != null ? balanceObj.getClass().getName() : "null") + ")");
|
||||
System.out.println("shoppingGoldRefund对象: " + shopObj + " (类型: " + (shopObj != null ? shopObj.getClass().getName() : "null") + ")");
|
||||
System.out.println("serviceGoldRefund对象: " + serviceObj + " (类型: " + (serviceObj != null ? serviceObj.getClass().getName() : "null") + ")");
|
||||
System.out.println("memberDiscountRefund对象: " + memberObj + " (类型: " + (memberObj != null ? memberObj.getClass().getName() : "null") + ")");
|
||||
System.out.println("couponRefund对象: " + couponObj + " (类型: " + (couponObj != null ? couponObj.getClass().getName() : "null") + ")");
|
||||
|
||||
// 安全的BigDecimal转换
|
||||
wechatRefund = parseBigDecimalSafely(wxObj);
|
||||
balanceRefund = parseBigDecimalSafely(balanceObj);
|
||||
|
|
@ -313,17 +425,6 @@ public class UsersPayBeforController extends BaseController
|
|||
|
||||
String refundRemark = params.get("refundRemark") == null ? "" : params.get("refundRemark").toString();
|
||||
|
||||
// 添加参数解析后的调试日志
|
||||
System.out.println("=== 解析后的参数 ===");
|
||||
System.out.println(" orderId: " + orderId);
|
||||
System.out.println(" wechatRefund: " + wechatRefund + " (类型: " + wechatRefund.getClass().getName() + ")");
|
||||
System.out.println(" balanceRefund: " + balanceRefund + " (类型: " + balanceRefund.getClass().getName() + ")");
|
||||
System.out.println(" shoppingGoldRefund: " + shoppingGoldRefund + " (类型: " + shoppingGoldRefund.getClass().getName() + ")");
|
||||
System.out.println(" serviceGoldRefund: " + serviceGoldRefund + " (类型: " + serviceGoldRefund.getClass().getName() + ")");
|
||||
System.out.println(" memberDiscountRefund: " + memberDiscountRefund + " (类型: " + memberDiscountRefund.getClass().getName() + ")");
|
||||
System.out.println(" couponRefund: " + couponRefund + " (类型: " + couponRefund.getClass().getName() + ")");
|
||||
System.out.println(" refundRemark: " + refundRemark);
|
||||
|
||||
if (orderId == null || orderId.trim().isEmpty()) {
|
||||
return error("订单ID不能为空");
|
||||
}
|
||||
|
|
@ -508,6 +609,15 @@ public class UsersPayBeforController extends BaseController
|
|||
BigDecimal remainingServiceGoldRefund = serviceGoldRefund;
|
||||
BigDecimal remainingCouponRefund = couponRefund;
|
||||
|
||||
|
||||
// 修复:确保BigDecimal转换正确,处理可能的null值和类型问题
|
||||
// BigDecimal wechatRefund = BigDecimal.ZERO;
|
||||
// BigDecimal balanceRefund = BigDecimal.ZERO;
|
||||
// BigDecimal shoppingGoldRefund = BigDecimal.ZERO;
|
||||
// BigDecimal serviceGoldRefund = BigDecimal.ZERO;
|
||||
// BigDecimal memberDiscountRefund = BigDecimal.ZERO;
|
||||
// BigDecimal couponRefund = BigDecimal.ZERO;
|
||||
|
||||
// 修复:正确计算各项支付方式的退款金额
|
||||
for (UsersPayBefor record : payRecords) {
|
||||
BigDecimal currentRefunded = record.getReturnmoney() != null ? record.getReturnmoney() : BigDecimal.ZERO;
|
||||
|
|
@ -520,19 +630,30 @@ public class UsersPayBeforController extends BaseController
|
|||
// 修复:计算该记录微信支付的剩余可退款金额
|
||||
BigDecimal wxRemaining = record.getWxmoney().subtract(currentRefunded);
|
||||
if (wxRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingWechatRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal wxRefund = remainingWechatRefund.min(wxRemaining);
|
||||
recordRefundAmount = recordRefundAmount.add(wxRefund);
|
||||
remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
|
||||
record.setWxmoney(record.getWxmoney().subtract(currentRefunded));
|
||||
}
|
||||
// BigDecimal wxRemaining = record.getWxmoney().subtract(currentRefunded);
|
||||
// if (wxRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingWechatRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// record.setWxmoney(wxRemaining);
|
||||
// BigDecimal wxRefund = remainingWechatRefund.min(wxRemaining);
|
||||
// recordRefundAmount = recordRefundAmount.add(wxRefund);
|
||||
// remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
|
||||
// }
|
||||
}
|
||||
|
||||
//余额
|
||||
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
// 修复:计算该记录余额支付的剩余可退款金额
|
||||
BigDecimal yeRemaining = record.getYemoney().subtract(currentRefunded);
|
||||
if (yeRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingBalanceRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal yeRefund = remainingBalanceRefund.min(yeRemaining);
|
||||
recordRefundAmount = recordRefundAmount.add(yeRefund);
|
||||
remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund);
|
||||
record.setYemoney(record.getYemoney().subtract(remainingBalanceRefund));
|
||||
// System.out.println("=== 调用退款工具方法前的参数验证 yeRemaining======================================================"+yeRemaining);
|
||||
// System.out.println("=== 调用退款工具方法前的参数验证 record.getYemoney()======================================================"+record.getYemoney());
|
||||
// System.out.println("=== 调用退款工具方法前的参数验证 currentRefunded======================================================"+currentRefunded);
|
||||
//
|
||||
// record.setYemoney(yeRemaining);
|
||||
// BigDecimal yeRefund = remainingBalanceRefund.min(yeRemaining);
|
||||
// recordRefundAmount = recordRefundAmount.add(yeRefund);
|
||||
// remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -540,9 +661,11 @@ public class UsersPayBeforController extends BaseController
|
|||
// 修复:计算该记录购物金的剩余可退款金额
|
||||
BigDecimal shopRemaining = record.getShopmoney().subtract(currentRefunded);
|
||||
if (shopRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingShoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal shopRefund = remainingShoppingGoldRefund.min(shopRemaining);
|
||||
recordRefundAmount = recordRefundAmount.add(shopRefund);
|
||||
remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund);
|
||||
|
||||
// record.setShopmoney(shopRemaining);
|
||||
// BigDecimal shopRefund = remainingShoppingGoldRefund.min(shopRemaining);
|
||||
// recordRefundAmount = recordRefundAmount.add(shopRefund);
|
||||
// remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,6 +673,7 @@ public class UsersPayBeforController extends BaseController
|
|||
// 修复:计算该记录服务金的剩余可退款金额
|
||||
BigDecimal serviceRemaining = record.getServicemoney().subtract(currentRefunded);
|
||||
if (serviceRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingServiceGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
record.setServicemoney(serviceRemaining);
|
||||
BigDecimal serviceRefund = remainingServiceGoldRefund.min(serviceRemaining);
|
||||
recordRefundAmount = recordRefundAmount.add(serviceRefund);
|
||||
remainingServiceGoldRefund = remainingServiceGoldRefund.subtract(serviceRefund);
|
||||
|
|
@ -560,6 +684,7 @@ public class UsersPayBeforController extends BaseController
|
|||
// 修复:计算该记录优惠券的剩余可退款金额
|
||||
BigDecimal couponRemaining = record.getCouponmoney().subtract(currentRefunded);
|
||||
if (couponRemaining.compareTo(BigDecimal.ZERO) > 0 && remainingCouponRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
record.setCouponmoney(new BigDecimal(0));
|
||||
BigDecimal couponRefundAmount = remainingCouponRefund.min(couponRemaining);
|
||||
recordRefundAmount = recordRefundAmount.add(couponRefundAmount);
|
||||
remainingCouponRefund = remainingCouponRefund.subtract(couponRefundAmount);
|
||||
|
|
@ -623,4 +748,203 @@ public class UsersPayBeforController extends BaseController
|
|||
return error("退款失败:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * 统一退款方法 - 核心逻辑:直接修改预支付表金额
|
||||
// */
|
||||
// @Log(title = "统一退款", businessType = BusinessType.UPDATE)
|
||||
// @PostMapping("/unifiedRefund")
|
||||
// public AjaxResult unifiedRefund(@RequestBody Map<String, Object> params)
|
||||
// {
|
||||
// try {
|
||||
// System.out.println("=== 开始统一退款 ===");
|
||||
// System.out.println("接收到的参数: " + params);
|
||||
//
|
||||
// // 1. 获取订单ID
|
||||
// String orderId = params.get("orderId") == null ? null : params.get("orderId").toString();
|
||||
// if (orderId == null || orderId.trim().isEmpty()) {
|
||||
// return error("订单ID不能为空");
|
||||
// }
|
||||
//
|
||||
// // 2. 获取各项退款金额
|
||||
// BigDecimal wechatRefund = parseBigDecimalSafely(params.get("wechatRefund"));
|
||||
// BigDecimal balanceRefund = parseBigDecimalSafely(params.get("balanceRefund"));
|
||||
// BigDecimal shoppingGoldRefund = parseBigDecimalSafely(params.get("shoppingGoldRefund"));
|
||||
// BigDecimal serviceGoldRefund = parseBigDecimalSafely(params.get("serviceGoldRefund"));
|
||||
// BigDecimal couponRefund = parseBigDecimalSafely(params.get("couponRefund"));
|
||||
// String refundRemark = params.get("refundRemark") == null ? "" : params.get("refundRemark").toString();
|
||||
//
|
||||
// // 3. 计算总退款金额
|
||||
// BigDecimal totalRefund = wechatRefund.add(balanceRefund).add(shoppingGoldRefund)
|
||||
// .add(serviceGoldRefund).add(couponRefund);
|
||||
//
|
||||
// if (totalRefund.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
// return error("退款金额必须大于0");
|
||||
// }
|
||||
//
|
||||
// System.out.println("=== 退款金额 ===");
|
||||
// System.out.println("微信退款: " + wechatRefund + "元");
|
||||
// System.out.println("余额退款: " + balanceRefund + "元");
|
||||
// System.out.println("购物金退款: " + shoppingGoldRefund + "元");
|
||||
// System.out.println("服务金退款: " + serviceGoldRefund + "元");
|
||||
// System.out.println("优惠券退款: " + couponRefund + "元");
|
||||
// System.out.println("总退款金额: " + totalRefund + "元");
|
||||
//
|
||||
// // 4. 核心逻辑:调用IUsersPayBeforService查询orderid获取预支付数据
|
||||
// UsersPayBefor paymentInfo = usersPayBeforService.selectUsersPayBeforByOrderId(orderId);
|
||||
// if (paymentInfo == null) {
|
||||
// return error("未找到支付记录");
|
||||
// }
|
||||
//
|
||||
// // 5. 获取原始金额
|
||||
// BigDecimal originalWxMoney = paymentInfo.getWxmoney() != null ? paymentInfo.getWxmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalYeMoney = paymentInfo.getYemoney() != null ? paymentInfo.getYemoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalShopMoney = paymentInfo.getShopmoney() != null ? paymentInfo.getShopmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalServiceMoney = paymentInfo.getServicemoney() != null ? paymentInfo.getServicemoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalCouponMoney = paymentInfo.getCouponmoney() != null ? paymentInfo.getCouponmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal originalMemberMoney = paymentInfo.getMembermoney() != null ? paymentInfo.getMembermoney() : BigDecimal.ZERO;
|
||||
//
|
||||
// System.out.println("=== 原始金额 ===");
|
||||
// System.out.println("微信支付: " + originalWxMoney + "元");
|
||||
// System.out.println("余额支付: " + originalYeMoney + "元");
|
||||
// System.out.println("购物金: " + originalShopMoney + "元");
|
||||
// System.out.println("服务金: " + originalServiceMoney + "元");
|
||||
// System.out.println("优惠券: " + originalCouponMoney + "元");
|
||||
// System.out.println("会员优惠: " + originalMemberMoney + "元");
|
||||
//
|
||||
// // 6. 验证退款金额不超过原始金额
|
||||
// if (wechatRefund.compareTo(originalWxMoney) > 0) {
|
||||
// return error("微信退款金额不能超过微信支付金额");
|
||||
// }
|
||||
// if (balanceRefund.compareTo(originalYeMoney) > 0) {
|
||||
// return error("余额退款金额不能超过余额支付金额");
|
||||
// }
|
||||
// if (shoppingGoldRefund.compareTo(originalShopMoney) > 0) {
|
||||
// return error("购物金退款金额不能超过购物金金额");
|
||||
// }
|
||||
// if (serviceGoldRefund.compareTo(originalServiceMoney) > 0) {
|
||||
// return error("服务金退款金额不能超过服务金金额");
|
||||
// }
|
||||
// // 优惠券只能全额退:要么退0,要么等于原优惠券金额
|
||||
// if (couponRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// if (originalCouponMoney.compareTo(BigDecimal.ZERO) == 0) {
|
||||
// return error("无可退的优惠券金额");
|
||||
// }
|
||||
// if (couponRefund.compareTo(originalCouponMoney) != 0) {
|
||||
// return error("优惠券只能全额退,原金额:¥" + originalCouponMoney + ",本次退款:¥" + couponRefund);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 7. 核心逻辑:根据各个项目的退款进行修改上面的金额
|
||||
// BigDecimal newWxMoney = originalWxMoney.subtract(wechatRefund);
|
||||
// BigDecimal newYeMoney = originalYeMoney.subtract(balanceRefund);
|
||||
// BigDecimal newShopMoney = originalShopMoney.subtract(shoppingGoldRefund);
|
||||
// BigDecimal newServiceMoney = originalServiceMoney.subtract(serviceGoldRefund);
|
||||
// BigDecimal newCouponMoney = originalCouponMoney.subtract(couponRefund);
|
||||
//
|
||||
// // 确保金额不为负数
|
||||
// if (newWxMoney.compareTo(BigDecimal.ZERO) < 0) newWxMoney = BigDecimal.ZERO;
|
||||
// if (newYeMoney.compareTo(BigDecimal.ZERO) < 0) newYeMoney = BigDecimal.ZERO;
|
||||
// if (newShopMoney.compareTo(BigDecimal.ZERO) < 0) newShopMoney = BigDecimal.ZERO;
|
||||
// if (newServiceMoney.compareTo(BigDecimal.ZERO) < 0) newServiceMoney = BigDecimal.ZERO;
|
||||
// if (newCouponMoney.compareTo(BigDecimal.ZERO) < 0) newCouponMoney = BigDecimal.ZERO;
|
||||
//
|
||||
// // 8. 计算新的总金额(不包括会员优惠)
|
||||
// BigDecimal newTotalAmount = newWxMoney.add(newYeMoney).add(newShopMoney)
|
||||
// .add(newServiceMoney).add(newCouponMoney);
|
||||
//
|
||||
// // 9. 获取当前已退款金额并计算新的总退款金额
|
||||
// BigDecimal currentRefunded = paymentInfo.getReturnmoney() != null ? paymentInfo.getReturnmoney() : BigDecimal.ZERO;
|
||||
// BigDecimal newTotalRefunded = currentRefunded.add(totalRefund);
|
||||
//
|
||||
// System.out.println("=== 修改后的金额 ===");
|
||||
// System.out.println("微信支付: " + newWxMoney + "元 (原: " + originalWxMoney + " - 退: " + wechatRefund + ")");
|
||||
// System.out.println("余额支付: " + newYeMoney + "元 (原: " + originalYeMoney + " - 退: " + balanceRefund + ")");
|
||||
// System.out.println("购物金: " + newShopMoney + "元 (原: " + originalShopMoney + " - 退: " + shoppingGoldRefund + ")");
|
||||
// System.out.println("服务金: " + newServiceMoney + "元 (原: " + originalServiceMoney + " - 退: " + serviceGoldRefund + ")");
|
||||
// System.out.println("优惠券: " + newCouponMoney + "元 (原: " + originalCouponMoney + " - 退: " + couponRefund + ")");
|
||||
// System.out.println("会员优惠: " + originalMemberMoney + "元 (不参与退款)");
|
||||
// System.out.println("新总金额: " + newTotalAmount + "元");
|
||||
// System.out.println("累计退款: " + newTotalRefunded + "元");
|
||||
//
|
||||
// // 10. 更新预支付表数据
|
||||
// paymentInfo.setWxmoney(newWxMoney);
|
||||
// paymentInfo.setYemoney(newYeMoney);
|
||||
// paymentInfo.setShopmoney(newShopMoney);
|
||||
// paymentInfo.setServicemoney(newServiceMoney);
|
||||
// paymentInfo.setCouponmoney(newCouponMoney);
|
||||
// paymentInfo.setAllmoney(newTotalAmount);
|
||||
// paymentInfo.setReturnmoney(newTotalRefunded);
|
||||
//
|
||||
// // 设置状态
|
||||
// if (newTotalRefunded.compareTo(newTotalAmount) >= 0) {
|
||||
// paymentInfo.setStatus(3L); // 完全退款
|
||||
// } else {
|
||||
// paymentInfo.setStatus(2L); // 部分退款
|
||||
// }
|
||||
//
|
||||
// // 11. 保存更新
|
||||
// int updateResult = usersPayBeforService.updateUsersPayBefor(paymentInfo);
|
||||
// if (updateResult <= 0) {
|
||||
// return error("支付记录更新失败");
|
||||
// }
|
||||
//
|
||||
// // 12. 记录退款日志
|
||||
// OrderLog orderLog = new OrderLog();
|
||||
// orderLog.setOrderId(orderId);
|
||||
// orderLog.setOid(999L);
|
||||
// orderLog.setTitle("退款");
|
||||
// orderLog.setOrdertype(paymentInfo.getServicetype());
|
||||
// orderLog.setType(new BigDecimal(11));
|
||||
//
|
||||
// // 构建退款说明
|
||||
// StringBuilder refundDesc = new StringBuilder("统一退款:");
|
||||
// if (wechatRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// refundDesc.append("微信支付退款¥").append(wechatRefund).append(",");
|
||||
// }
|
||||
// if (balanceRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// refundDesc.append("余额退款¥").append(balanceRefund).append(",");
|
||||
// }
|
||||
// if (shoppingGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// refundDesc.append("购物金退款¥").append(shoppingGoldRefund).append(",");
|
||||
// }
|
||||
// if (serviceGoldRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// refundDesc.append("服务金退款¥").append(serviceGoldRefund).append(",");
|
||||
// }
|
||||
// if (couponRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
// refundDesc.append("优惠券退款¥").append(couponRefund).append(",");
|
||||
// }
|
||||
//
|
||||
// if (refundDesc.charAt(refundDesc.length() - 1) == ',') {
|
||||
// refundDesc.setLength(refundDesc.length() - 1);
|
||||
// }
|
||||
//
|
||||
// refundDesc.append(",本次退款金额:¥").append(totalRefund);
|
||||
// refundDesc.append(",累计已退款:¥").append(newTotalRefunded);
|
||||
// refundDesc.append(",剩余可退款:¥").append(newTotalAmount);
|
||||
// refundDesc.append("(会员优惠¥").append(originalMemberMoney).append("不参与退款)");
|
||||
//
|
||||
// orderLog.setContent(refundDesc.toString());
|
||||
// orderLogService.insertOrderLog(orderLog);
|
||||
//
|
||||
// // 13. 调用退款工具方法处理业务逻辑
|
||||
// System.out.println("=== 调用退款工具方法 ===");
|
||||
// Map<String, Object> refundResult = refundUtil.processUnifiedRefund(
|
||||
// orderId, wechatRefund, balanceRefund, shoppingGoldRefund,
|
||||
// serviceGoldRefund, BigDecimal.ZERO, couponRefund, refundRemark
|
||||
// );
|
||||
//
|
||||
// if (refundResult != null && (Boolean) refundResult.get("success")) {
|
||||
// System.out.println("✅ 退款处理成功");
|
||||
// return success("退款成功");
|
||||
// } else {
|
||||
// String errorMsg = (String) refundResult.get("message");
|
||||
// return error(errorMsg != null ? errorMsg : "退款处理失败");
|
||||
// }
|
||||
//
|
||||
// } catch (Exception e) {
|
||||
// System.err.println("❌ 退款处理异常: " + e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// return error("退款失败:" + e.getMessage());
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,13 +141,14 @@ public class WechatPayV3Controller extends BaseController {
|
|||
@PostMapping("/withdraw")
|
||||
@Log(title = "微信支付V3用户提现", businessType = BusinessType.OTHER)
|
||||
public AjaxResult withdraw(@RequestParam String openid,
|
||||
@RequestParam String orderId,
|
||||
@RequestParam int amount,
|
||||
@RequestParam(required = false) String desc,
|
||||
@RequestParam(required = false) String userName) {
|
||||
try {
|
||||
log.info("💰 收到提现请求 - 用户: {}, 金额: {}分", openid.substring(0, 6) + "****", amount);
|
||||
|
||||
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName);
|
||||
Map<String, Object> result = wechatPayV3Util.withdraw(openid, amount, desc, userName,orderId);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
return AjaxResult.success("提现申请成功", result.get("data"));
|
||||
|
|
@ -172,12 +173,13 @@ public class WechatPayV3Controller extends BaseController {
|
|||
@PostMapping("/quick-withdraw")
|
||||
@Log(title = "微信支付V3快速提现", businessType = BusinessType.OTHER)
|
||||
public AjaxResult quickWithdraw(@RequestParam String openid,
|
||||
@RequestParam String orderId,
|
||||
@RequestParam BigDecimal amount,
|
||||
@RequestParam(required = false) String desc) {
|
||||
try {
|
||||
log.info("⚡ 收到快速提现请求 - 用户: {}, 金额: {}元", openid.substring(0, 6) + "****", amount);
|
||||
|
||||
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc);
|
||||
Map<String, Object> result = wechatPayV3Util.quickWithdraw(openid, amount, desc, orderId);
|
||||
|
||||
if ((Boolean) result.get("success")) {
|
||||
return AjaxResult.success("快速提现成功", result.get("data"));
|
||||
|
|
|
|||
|
|
@ -529,109 +529,58 @@ public class RefundUtil {
|
|||
|
||||
/**
|
||||
* 更新支付记录状态和退款金额
|
||||
* 注意:金额更新已经在Controller中完成,这里只处理业务逻辑
|
||||
*/
|
||||
private boolean updatePaymentStatus(UsersPayBefor paymentInfo, String orderId,
|
||||
BigDecimal wechatRefund, BigDecimal balanceRefund,
|
||||
BigDecimal shoppingGoldRefund, BigDecimal serviceGoldRefund,
|
||||
BigDecimal memberDiscountRefund, BigDecimal couponRefund) {
|
||||
try {
|
||||
// 查询所有相关的支付记录
|
||||
List<UsersPayBefor> payRecords = usersPayBeforService.selectPayDetailsByOrderId(orderId);
|
||||
if (payRecords == null || payRecords.isEmpty()) {
|
||||
System.out.println("=== 开始更新支付记录状态(RefundUtil) ===");
|
||||
System.out.println("订单ID: " + orderId);
|
||||
System.out.println("微信退款: " + wechatRefund + "元");
|
||||
System.out.println("余额退款: " + balanceRefund + "元");
|
||||
System.out.println("购物金退款: " + shoppingGoldRefund + "元");
|
||||
System.out.println("服务金退款: " + serviceGoldRefund + "元");
|
||||
System.out.println("会员优惠: " + memberDiscountRefund + "元(不参与退款)");
|
||||
System.out.println("优惠券退款: " + couponRefund + "元");
|
||||
|
||||
// 重要:金额更新已经在Controller中完成,这里不需要重复处理
|
||||
// 只需要验证数据一致性
|
||||
System.out.println("=== 验证数据一致性 ===");
|
||||
System.out.println("当前支付记录状态:");
|
||||
System.out.println(" 微信支付: " + paymentInfo.getWxmoney() + "元");
|
||||
System.out.println(" 余额支付: " + paymentInfo.getYemoney() + "元");
|
||||
System.out.println(" 购物金: " + paymentInfo.getShopmoney() + "元");
|
||||
System.out.println(" 服务金: " + paymentInfo.getServicemoney() + "元");
|
||||
System.out.println(" 优惠券: " + paymentInfo.getCouponmoney() + "元");
|
||||
System.out.println(" 总金额: " + paymentInfo.getAllmoney() + "元");
|
||||
System.out.println(" 退款金额: " + paymentInfo.getReturnmoney() + "元");
|
||||
|
||||
// 验证退款金额不能为负数
|
||||
if (paymentInfo.getWxmoney() != null && paymentInfo.getWxmoney().compareTo(BigDecimal.ZERO) < 0) {
|
||||
System.err.println("❌ 微信支付金额不能为负数: " + paymentInfo.getWxmoney());
|
||||
return false;
|
||||
}
|
||||
if (paymentInfo.getYemoney() != null && paymentInfo.getYemoney().compareTo(BigDecimal.ZERO) < 0) {
|
||||
System.err.println("❌ 余额支付金额不能为负数: " + paymentInfo.getYemoney());
|
||||
return false;
|
||||
}
|
||||
if (paymentInfo.getShopmoney() != null && paymentInfo.getShopmoney().compareTo(BigDecimal.ZERO) < 0) {
|
||||
System.err.println("❌ 购物金金额不能为负数: " + paymentInfo.getShopmoney());
|
||||
return false;
|
||||
}
|
||||
if (paymentInfo.getServicemoney() != null && paymentInfo.getServicemoney().compareTo(BigDecimal.ZERO) < 0) {
|
||||
System.err.println("❌ 服务金金额不能为负数: " + paymentInfo.getServicemoney());
|
||||
return false;
|
||||
}
|
||||
if (paymentInfo.getCouponmoney() != null && paymentInfo.getCouponmoney().compareTo(BigDecimal.ZERO) < 0) {
|
||||
System.err.println("❌ 优惠券金额不能为负数: " + paymentInfo.getCouponmoney());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 创建退款金额的副本,用于分配
|
||||
BigDecimal remainingWechatRefund = wechatRefund;
|
||||
BigDecimal remainingBalanceRefund = balanceRefund;
|
||||
BigDecimal remainingShoppingGoldRefund = shoppingGoldRefund;
|
||||
BigDecimal remainingServiceGoldRefund = serviceGoldRefund;
|
||||
BigDecimal remainingCouponRefund = couponRefund;
|
||||
|
||||
// 更新每条支付记录的状态、退款金额和扣减预支付金额
|
||||
for (UsersPayBefor record : payRecords) {
|
||||
BigDecimal currentRefunded = record.getReturnmoney() != null ? record.getReturnmoney() : BigDecimal.ZERO;
|
||||
BigDecimal recordRefundAmount = BigDecimal.ZERO;
|
||||
|
||||
// 根据该记录的支付方式分配退款金额
|
||||
if (record.getWxmoney() != null && record.getWxmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal availableWxMoney = record.getWxmoney().subtract(currentRefunded);
|
||||
if (availableWxMoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal wxRefund = remainingWechatRefund.min(availableWxMoney);
|
||||
if (wxRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
recordRefundAmount = recordRefundAmount.add(wxRefund);
|
||||
remainingWechatRefund = remainingWechatRefund.subtract(wxRefund);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal availableYeMoney = record.getYemoney().subtract(currentRefunded);
|
||||
if (availableYeMoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal yeRefund = remainingBalanceRefund.min(availableYeMoney);
|
||||
if (yeRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
recordRefundAmount = recordRefundAmount.add(yeRefund);
|
||||
remainingBalanceRefund = remainingBalanceRefund.subtract(yeRefund);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getShopmoney() != null && record.getShopmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal availableShopMoney = record.getShopmoney().subtract(currentRefunded);
|
||||
if (availableShopMoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal shopRefund = remainingShoppingGoldRefund.min(availableShopMoney);
|
||||
if (shopRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
recordRefundAmount = recordRefundAmount.add(shopRefund);
|
||||
remainingShoppingGoldRefund = remainingShoppingGoldRefund.subtract(shopRefund);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getServicemoney() != null && record.getServicemoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal availableServiceMoney = record.getServicemoney().subtract(currentRefunded);
|
||||
if (availableServiceMoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal serviceRefund = remainingServiceGoldRefund.min(availableServiceMoney);
|
||||
if (serviceRefund.compareTo(BigDecimal.ZERO) > 0) {
|
||||
recordRefundAmount = recordRefundAmount.add(serviceRefund);
|
||||
remainingServiceGoldRefund = remainingServiceGoldRefund.subtract(serviceRefund);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getCouponmoney() != null && record.getCouponmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal availableCouponMoney = record.getCouponmoney().subtract(currentRefunded);
|
||||
if (availableCouponMoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal couponRefundAmount = remainingCouponRefund.min(availableCouponMoney);
|
||||
if (couponRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
recordRefundAmount = recordRefundAmount.add(couponRefundAmount);
|
||||
remainingCouponRefund = remainingCouponRefund.subtract(couponRefundAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果该记录有退款金额,则更新
|
||||
if (recordRefundAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal newTotalRefunded = currentRefunded.add(recordRefundAmount);
|
||||
|
||||
// 扣减预支付数据中的对应金额
|
||||
updatePaymentAmounts(record, recordRefundAmount, recordRefundAmount, recordRefundAmount,
|
||||
recordRefundAmount, memberDiscountRefund, recordRefundAmount);
|
||||
|
||||
// 如果累计退款金额等于或超过支付金额,设置为已退款状态
|
||||
if (newTotalRefunded.compareTo(record.getAllmoney()) >= 0) {
|
||||
record.setStatus(3L); // 完全退款
|
||||
} else {
|
||||
record.setStatus(2L); // 部分退款
|
||||
}
|
||||
|
||||
record.setReturnmoney(newTotalRefunded);
|
||||
usersPayBeforService.updateUsersPayBefor(record);
|
||||
|
||||
System.out.println("✅ 支付记录更新成功,订单ID: " + record.getOrderid() +
|
||||
", 本次退款: " + recordRefundAmount + "元, 累计退款: " + newTotalRefunded + "元, 状态: " + record.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("✅ 数据一致性验证通过");
|
||||
System.out.println("✅ 支付记录状态更新完成");
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
|
|
@ -643,84 +592,32 @@ public class RefundUtil {
|
|||
|
||||
/**
|
||||
* 扣减预支付数据中的对应金额
|
||||
* 简化版:不进行复杂的金额扣减,因为退款金额已经在Controller中正确计算
|
||||
*/
|
||||
private void updatePaymentAmounts(UsersPayBefor record, BigDecimal wechatRefund,
|
||||
BigDecimal balanceRefund, BigDecimal shoppingGoldRefund,
|
||||
BigDecimal serviceGoldRefund, BigDecimal memberDiscountRefund,
|
||||
BigDecimal couponRefund) {
|
||||
try {
|
||||
// 计算该记录的总退款金额
|
||||
BigDecimal totalRecordRefund = wechatRefund.add(balanceRefund).add(shoppingGoldRefund)
|
||||
.add(serviceGoldRefund).add(memberDiscountRefund).add(couponRefund);
|
||||
System.out.println("=== 预支付金额扣减(简化版) ===");
|
||||
System.out.println("微信退款: " + wechatRefund + "元");
|
||||
System.out.println("余额退款: " + balanceRefund + "元");
|
||||
System.out.println("购物金退款: " + shoppingGoldRefund + "元");
|
||||
System.out.println("服务金退款: " + serviceGoldRefund + "元");
|
||||
System.out.println("会员优惠: " + memberDiscountRefund + "元(不参与退款)");
|
||||
System.out.println("优惠券退款: " + couponRefund + "元");
|
||||
|
||||
if (totalRecordRefund.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return; // 没有退款金额,无需处理
|
||||
}
|
||||
// 简化逻辑:不进行复杂的金额扣减
|
||||
// 因为退款金额已经在Controller中正确计算和更新了
|
||||
// 这里只记录日志,不修改数据
|
||||
|
||||
// 计算该记录的总支付金额(不包括会员优惠)
|
||||
BigDecimal totalPaymentAmount = BigDecimal.ZERO;
|
||||
if (record.getWxmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getWxmoney());
|
||||
if (record.getYemoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getYemoney());
|
||||
if (record.getShopmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getShopmoney());
|
||||
if (record.getServicemoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getServicemoney());
|
||||
if (record.getCouponmoney() != null) totalPaymentAmount = totalPaymentAmount.add(record.getCouponmoney());
|
||||
|
||||
if (totalPaymentAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return; // 没有支付金额,无需处理
|
||||
}
|
||||
|
||||
// 按比例分配退款金额到各个支付方式
|
||||
if (record.getWxmoney() != null && record.getWxmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal wxRatio = record.getWxmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal wxRefundAmount = totalRecordRefund.multiply(wxRatio).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal newWxMoney = record.getWxmoney().subtract(wxRefundAmount);
|
||||
record.setWxmoney(newWxMoney.compareTo(BigDecimal.ZERO) >= 0 ? newWxMoney : BigDecimal.ZERO);
|
||||
System.out.println(" 微信支付金额扣减: " + wxRefundAmount + "元, 剩余: " + record.getWxmoney() + "元");
|
||||
}
|
||||
|
||||
if (record.getYemoney() != null && record.getYemoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal yeRatio = record.getYemoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal yeRefundAmount = totalRecordRefund.multiply(yeRatio).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal newYeMoney = record.getYemoney().subtract(yeRefundAmount);
|
||||
record.setYemoney(newYeMoney.compareTo(BigDecimal.ZERO) >= 0 ? newYeMoney : BigDecimal.ZERO);
|
||||
System.out.println(" 余额支付金额扣减: " + yeRefundAmount + "元, 剩余: " + record.getYemoney() + "元");
|
||||
}
|
||||
|
||||
if (record.getShopmoney() != null && record.getShopmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal shopRatio = record.getShopmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal shopRefundAmount = totalRecordRefund.multiply(shopRatio).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal newShopMoney = record.getShopmoney().subtract(shopRefundAmount);
|
||||
record.setShopmoney(newShopMoney.compareTo(BigDecimal.ZERO) >= 0 ? newShopMoney : BigDecimal.ZERO);
|
||||
System.out.println(" 购物金抵扣金额扣减: " + shopRefundAmount + "元, 剩余: " + record.getShopmoney() + "元");
|
||||
}
|
||||
|
||||
if (record.getServicemoney() != null && record.getServicemoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal serviceRatio = record.getServicemoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal serviceRefundAmount = totalRecordRefund.multiply(serviceRatio).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal newServiceMoney = record.getServicemoney().subtract(serviceRefundAmount);
|
||||
record.setServicemoney(newServiceMoney.compareTo(BigDecimal.ZERO) >= 0 ? newServiceMoney : BigDecimal.ZERO);
|
||||
System.out.println(" 服务金抵扣金额扣减: " + serviceRefundAmount + "元, 剩余: " + record.getServicemoney() + "元");
|
||||
}
|
||||
|
||||
// 会员优惠不参与退款,但记录在退款详情中用于说明
|
||||
if (memberDiscountRefund.compareTo(BigDecimal.ZERO) > 0 && record.getMembermoney() != null) {
|
||||
System.out.println(" 会员优惠金额保持不变: " + record.getMembermoney() + "元(平台承担,不参与退款)");
|
||||
}
|
||||
|
||||
if (record.getCouponmoney() != null && record.getCouponmoney().compareTo(BigDecimal.ZERO) > 0) {
|
||||
BigDecimal couponRatio = record.getCouponmoney().divide(totalPaymentAmount, 4, RoundingMode.HALF_UP);
|
||||
BigDecimal couponRefundAmount = totalRecordRefund.multiply(couponRatio).setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal newCouponMoney = record.getCouponmoney().subtract(couponRefundAmount);
|
||||
record.setCouponmoney(newCouponMoney.compareTo(BigDecimal.ZERO) >= 0 ? newCouponMoney : BigDecimal.ZERO);
|
||||
System.out.println(" 优惠券抵扣金额扣减: " + couponRefundAmount + "元, 剩余: " + record.getCouponmoney() + "元");
|
||||
}
|
||||
|
||||
// 注意:allmoney 是订单总金额,一旦支付永恒不变,不参与重新计算
|
||||
// 退款时只需要修改对应的退款项目金额和退款金额
|
||||
System.out.println(" 订单总金额保持不变: " + record.getAllmoney() + "元(永恒不变)");
|
||||
System.out.println("ℹ️ 预支付金额保持不变(简化版)");
|
||||
System.out.println("ℹ️ 订单总金额: " + record.getAllmoney() + "元");
|
||||
System.out.println("ℹ️ 退款金额: " + record.getReturnmoney() + "元");
|
||||
System.out.println("=== 预支付金额扣减完成(简化版) ===");
|
||||
|
||||
} catch (Exception e) {
|
||||
System.err.println("❌ 扣减预支付金额异常: " + e.getMessage());
|
||||
System.err.println("❌ 预支付金额扣减异常: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,7 +81,10 @@ public class WechatPayUtil {
|
|||
private static final String WECHAT_TRANSFER_URL = "https://api.mch.weixin.qq.com/mmpaymkttransfers/promotion/transfers"; // 企业付款
|
||||
|
||||
|
||||
public static final String PAY_FH = "https://403e667e.r3.cpolar.top/";
|
||||
// public static final String PAY_FH = "https://www.huafurenjia.cn/";
|
||||
public static final String PAY_FH = "https://api.huafurenjia.cn/";
|
||||
|
||||
|
||||
/**
|
||||
* 其他配置常量
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -92,6 +92,8 @@ public class WechatPayV3Util {
|
|||
return data.substring(0, 3) + "****" + data.substring(data.length() - 3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ============================== 公共方法 ==============================
|
||||
|
||||
/**
|
||||
|
|
@ -351,6 +353,15 @@ public class WechatPayV3Util {
|
|||
|
||||
// ============================== 退款功能 ==============================
|
||||
|
||||
|
||||
|
||||
|
||||
public Map<String, Object> refundtixian(JSONObject jsonResponse) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
decryptSensitiveFields(jsonResponse);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 申请退款
|
||||
*
|
||||
|
|
@ -576,7 +587,7 @@ public class WechatPayV3Util {
|
|||
* @param userName 真实姓名(可选,用于实名校验)
|
||||
* @return 提现结果
|
||||
*/
|
||||
public Map<String, Object> withdraw(String openid, int amount, String desc, String userName) {
|
||||
public Map<String, Object> withdraw(String openid, int amount, String desc, String userName,String orderid) {
|
||||
log.info("💰 开始用户提现");
|
||||
log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid));
|
||||
log.info(" ├─ 提现金额: {} 分 ({}元)", amount, fenToYuan(amount));
|
||||
|
|
@ -604,7 +615,7 @@ public class WechatPayV3Util {
|
|||
log.info("✅ 参数验证通过");
|
||||
|
||||
// 生成批次号和明细号(只包含数字和字母)
|
||||
String outBatchNo = generateValidBatchNo("WITHDRAW");
|
||||
String outBatchNo = orderid;
|
||||
String detailNo = generateValidBatchNo("DETAIL");
|
||||
log.info("🆔 生成业务单号");
|
||||
log.info(" ├─ 批次号: {}", outBatchNo);
|
||||
|
|
@ -640,7 +651,10 @@ public class WechatPayV3Util {
|
|||
params.put("batch_remark", "用户申请提现到零钱");
|
||||
params.put("total_amount", amount);
|
||||
params.put("total_num", 1);
|
||||
// params.put("batch_id", orderid);
|
||||
|
||||
params.put("transfer_detail_list", detailList);
|
||||
params.put("notify_url", WechatPayUtil.PAY_FH +"api/worker/tixian/notify");
|
||||
|
||||
String bodyJson = JSONObject.toJSONString(params);
|
||||
log.info("✅ 请求参数构建完成");
|
||||
|
|
@ -1073,7 +1087,7 @@ public class WechatPayV3Util {
|
|||
* @param desc 提现描述
|
||||
* @return 提现结果
|
||||
*/
|
||||
public Map<String, Object> quickWithdraw(String openid, BigDecimal amount, String desc) {
|
||||
public Map<String, Object> quickWithdraw(String openid, BigDecimal amount, String desc, String orderId) {
|
||||
log.info("⚡ 开始快速提现");
|
||||
log.info(" ├─ 用户OpenID: {}", maskSensitiveData(openid));
|
||||
log.info(" ├─ 提现金额: {}元", amount);
|
||||
|
|
@ -1082,7 +1096,7 @@ public class WechatPayV3Util {
|
|||
int amountInt = amount.multiply(new BigDecimal(100)).intValue();
|
||||
log.info("💰 金额转换: {}元 = {}分", amount, amountInt);
|
||||
|
||||
return withdraw(openid, amountInt, desc, null);
|
||||
return withdraw(openid, amountInt, desc, null, orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -9,18 +9,6 @@
|
|||
<div class="payment-info" v-if="actualPaymentData">
|
||||
<h4>支付信息</h4>
|
||||
|
||||
<!-- <!– 调试信息 –>-->
|
||||
<!-- <div class="debug-info" style="background: #f0f9ff; padding: 10px; margin-bottom: 15px; border-radius: 4px; font-size: 12px;">-->
|
||||
<!-- <div>调试信息:</div>-->
|
||||
<!-- <div>allmoney: {{ actualPaymentData.allmoney }}</div>-->
|
||||
<!-- <div>wxmoney: {{ actualPaymentData.wxmoney }}</div>-->
|
||||
<!-- <div>yemoney: {{ actualPaymentData.yemoney }}</div>-->
|
||||
<!-- <div>shopmoney: {{ actualPaymentData.shopmoney }}</div>-->
|
||||
<!-- <div>servicemoney: {{ actualPaymentData.servicemoney }}</div>-->
|
||||
<!-- <div>couponmoney: {{ actualPaymentData.couponmoney }}</div>-->
|
||||
<!-- <div>membermoney: {{ actualPaymentData.membermoney }}</div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<div class="info-item">
|
||||
|
|
@ -80,41 +68,28 @@
|
|||
<el-col :span="12">
|
||||
<div class="summary-item">
|
||||
<label>剩余可退款:</label>
|
||||
<span class="amount remaining">¥{{ formatAmount(remainingRefundableAmount) }}</span>
|
||||
<span class="amount remaining">¥{{ formatAmount(remainingRefundableAmountComputed) }}</span>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="summary-note">
|
||||
<span class="note-text">💡 实际可退款金额:¥{{ formatAmount(actualRefundableAmount) }}(总支付:¥{{ formatAmount(actualPaymentData.allmoney) }} - 会员优惠:¥{{ formatAmount(actualPaymentData.membermoney || 0) }})</span>
|
||||
<span class="note-text">💡 退款信息</span>
|
||||
<div class="refund-details">
|
||||
<div class="detail-row compact">
|
||||
<span class="detail-label">订单总额:</span>
|
||||
<span class="detail-value">¥{{ formatAmount(actualPaymentData.allmoney) }}</span>
|
||||
<span class="detail-separator">|</span>
|
||||
<span class="detail-label">会员优惠:</span>
|
||||
<span class="detail-value">¥{{ formatAmount(actualPaymentData.membermoney || 0) }}</span>
|
||||
<span class="detail-separator">|</span>
|
||||
<span class="detail-label">累计退款:</span>
|
||||
<span class="detail-value">¥{{ formatAmount(totalRefundedAmount) }}</span>
|
||||
<span class="detail-separator">|</span>
|
||||
<span class="detail-label">剩余可退:</span>
|
||||
<span class="detail-value total-value">¥{{ formatAmount(remainingRefundableAmountComputed) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <div class="summary-extra">-->
|
||||
<!-- <span class="refund-count">退款次数:{{ refundHistory.length }}次</span>-->
|
||||
<!-- <span class="last-refund" v-if="refundHistory.length > 0">-->
|
||||
<!-- 最近退款:{{ formatTime(refundHistory[0].createTime) }}-->
|
||||
<!-- </span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="summary-details" v-if="refundHistory.length > 0">-->
|
||||
<!-- <div class="detail-row">-->
|
||||
<!-- <span class="detail-label">平均退款金额:</span>-->
|
||||
<!-- <span class="detail-value">¥{{ formatAmount(averageRefundAmount) }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="detail-row">-->
|
||||
<!-- <span class="detail-label">最大退款金额:</span>-->
|
||||
<!-- <span class="detail-value">¥{{ formatAmount(maxRefundAmount) }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="detail-row">-->
|
||||
<!-- <span class="detail-label">最小退款金额:</span>-->
|
||||
<!-- <span class="detail-value">¥{{ formatAmount(minRefundAmount) }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="detail-row">-->
|
||||
<!-- <span class="detail-label">中位数退款金额:</span>-->
|
||||
<!-- <span class="detail-value">¥{{ formatAmount(medianRefundAmount) }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="detail-row">-->
|
||||
<!-- <span class="detail-label">退款金额方差:</span>-->
|
||||
<!-- <span class="detail-value">¥{{ formatAmount(refundAmountVariance) }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -408,25 +383,26 @@ export default {
|
|||
return this.formatAmount(this.actualPaymentData.allmoney);
|
||||
},
|
||||
|
||||
// 计算实际可退款金额(减去会员优惠)
|
||||
actualRefundableAmount() {
|
||||
// 计算剩余可退款金额
|
||||
remainingRefundableAmountComputed() {
|
||||
if (!this.actualPaymentData) return "0.00";
|
||||
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
|
||||
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
|
||||
// 实际可退款金额 = allmoney - 会员优惠
|
||||
return (allMoney - memberDiscount).toFixed(2);
|
||||
const totalRefunded = parseFloat(this.totalRefundedAmount || 0);
|
||||
// 剩余可退款 = allmoney - 会员优惠 - returnmoney
|
||||
return Math.max(0, allMoney - memberDiscount - totalRefunded).toFixed(2);
|
||||
},
|
||||
|
||||
canRefund() {
|
||||
return parseFloat(this.totalRefundAmount) > 0 && parseFloat(this.totalRefundAmount) <= parseFloat(this.remainingRefundableAmount);
|
||||
return parseFloat(this.totalRefundAmount) > 0 && parseFloat(this.totalRefundAmount) <= parseFloat(this.remainingRefundableAmountComputed);
|
||||
},
|
||||
|
||||
validationMessage() {
|
||||
const totalRefund = parseFloat(this.totalRefundAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmountComputed);
|
||||
|
||||
if (totalRefund > remaining) {
|
||||
return `退款金额超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}`;
|
||||
return `退款金额超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmountComputed}`;
|
||||
} else if (totalRefund > 0) {
|
||||
return `退款后将剩余可退款:¥${(remaining - totalRefund).toFixed(2)}`;
|
||||
}
|
||||
|
|
@ -435,7 +411,7 @@ export default {
|
|||
|
||||
validationType() {
|
||||
const totalRefund = parseFloat(this.totalRefundAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmountComputed);
|
||||
return totalRefund > remaining ? "error" : "success";
|
||||
},
|
||||
|
||||
|
|
@ -507,98 +483,7 @@ export default {
|
|||
return filtered;
|
||||
},
|
||||
|
||||
// 计算平均退款金额
|
||||
averageRefundAmount() {
|
||||
if (this.refundHistory.length === 0) return "0.00";
|
||||
const totalRefunded = this.refundHistory.reduce((sum, item) => {
|
||||
try {
|
||||
const content = JSON.parse(item.content);
|
||||
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
|
||||
return sum + (isNaN(amount) ? 0 : amount);
|
||||
} catch (e) {
|
||||
return sum;
|
||||
}
|
||||
}, 0);
|
||||
return (totalRefunded / this.refundHistory.length).toFixed(2);
|
||||
},
|
||||
|
||||
// 计算最大退款金额
|
||||
maxRefundAmount() {
|
||||
if (this.refundHistory.length === 0) return "0.00";
|
||||
const refundAmounts = this.refundHistory.map(item => {
|
||||
try {
|
||||
const content = JSON.parse(item.content);
|
||||
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
|
||||
return isNaN(amount) ? 0 : amount;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}).filter(amount => amount > 0);
|
||||
|
||||
if (refundAmounts.length === 0) return "0.00";
|
||||
return Math.max(...refundAmounts).toFixed(2);
|
||||
},
|
||||
|
||||
// 计算最小退款金额
|
||||
minRefundAmount() {
|
||||
if (this.refundHistory.length === 0) return "0.00";
|
||||
const refundAmounts = this.refundHistory.map(item => {
|
||||
try {
|
||||
const content = JSON.parse(item.content);
|
||||
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
|
||||
return isNaN(amount) ? 0 : amount;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}).filter(amount => amount > 0);
|
||||
|
||||
if (refundAmounts.length === 0) return "0.00";
|
||||
return Math.min(...refundAmounts).toFixed(2);
|
||||
},
|
||||
|
||||
// 计算中位数退款金额
|
||||
medianRefundAmount() {
|
||||
if (this.refundHistory.length === 0) return "0.00";
|
||||
const sortedRefundAmounts = this.refundHistory.map(item => {
|
||||
try {
|
||||
const content = JSON.parse(item.content);
|
||||
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
|
||||
return isNaN(amount) ? 0 : amount;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}).filter(amount => amount > 0).sort((a, b) => a - b);
|
||||
|
||||
if (sortedRefundAmounts.length === 0) return "0.00";
|
||||
|
||||
const mid = Math.floor(sortedRefundAmounts.length / 2);
|
||||
if (sortedRefundAmounts.length % 2 === 0) {
|
||||
return ((sortedRefundAmounts[mid - 1] + sortedRefundAmounts[mid]) / 2).toFixed(2);
|
||||
} else {
|
||||
return sortedRefundAmounts[mid].toFixed(2);
|
||||
}
|
||||
},
|
||||
|
||||
// 计算退款金额方差
|
||||
refundAmountVariance() {
|
||||
if (this.refundHistory.length === 0) return "0.00";
|
||||
const validAmounts = this.refundHistory.map(item => {
|
||||
try {
|
||||
const content = JSON.parse(item.content);
|
||||
const amount = parseFloat(content.totalRefund) || parseFloat(content.returnmoney) || parseFloat(content.price) || 0;
|
||||
return isNaN(amount) ? 0 : amount;
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}).filter(amount => amount > 0);
|
||||
|
||||
if (validAmounts.length === 0) return "0.00";
|
||||
|
||||
const average = parseFloat(this.averageRefundAmount);
|
||||
const squaredDifferences = validAmounts.map(amount => Math.pow(amount - average, 2));
|
||||
const variance = squaredDifferences.reduce((sum, diff) => sum + diff, 0) / validAmounts.length;
|
||||
return variance.toFixed(2);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(newVal) {
|
||||
|
|
@ -642,15 +527,13 @@ export default {
|
|||
// 设置退款相关数据
|
||||
this.refundHistory = [];
|
||||
this.totalRefundedAmount = "0.00";
|
||||
// 修复:初始剩余可退款金额 = allmoney - 优惠金额(因为还没有退款记录)
|
||||
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
|
||||
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
|
||||
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
|
||||
// 剩余可退款金额将通过计算属性自动计算
|
||||
|
||||
console.log('初始计算:');
|
||||
console.log(' allmoney:', allMoney);
|
||||
console.log(' 会员优惠:', memberDiscount);
|
||||
console.log(' 初始剩余可退款:', this.remainingRefundableAmount);
|
||||
console.log('初始设置:');
|
||||
console.log(' allmoney:', parseFloat(this.actualPaymentData.allmoney || 0));
|
||||
console.log(' 会员优惠:', parseFloat(this.actualPaymentData.membermoney || 0));
|
||||
console.log(' 初始累计退款:', this.totalRefundedAmount);
|
||||
console.log(' 剩余可退款将通过计算属性自动计算');
|
||||
|
||||
// 加载退款历史
|
||||
await this.loadRefundHistory();
|
||||
|
|
@ -734,10 +617,7 @@ export default {
|
|||
if (attempt === maxRetries) { // 最后一次尝试失败
|
||||
this.refundHistory = [];
|
||||
this.totalRefundedAmount = "0.00";
|
||||
// 修复:使用正确的计算公式
|
||||
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
|
||||
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
|
||||
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
|
||||
// 剩余可退款金额将通过计算属性自动计算
|
||||
this.refundHistoryError = response.msg || "加载退款历史失败";
|
||||
this.errorDetails = {
|
||||
code: response.code,
|
||||
|
|
@ -826,22 +706,8 @@ export default {
|
|||
});
|
||||
|
||||
this.totalRefundedAmount = total.toFixed(2);
|
||||
|
||||
// 修复:剩余可退款金额 = allmoney - 优惠金额 - returnmoney
|
||||
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
|
||||
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
|
||||
const totalRefunded = parseFloat(this.totalRefundedAmount);
|
||||
|
||||
// 正确的计算公式:allmoney - 优惠金额 - returnmoney
|
||||
const remaining = Math.max(0, allMoney - memberDiscount - totalRefunded);
|
||||
this.remainingRefundableAmount = remaining.toFixed(2);
|
||||
|
||||
console.log('计算详情:');
|
||||
console.log(' allmoney:', allMoney);
|
||||
console.log(' 会员优惠:', memberDiscount);
|
||||
console.log(' 累计已退款:', totalRefunded);
|
||||
console.log(' 剩余可退款:', this.remainingRefundableAmount);
|
||||
console.log(' 计算公式: allmoney - 优惠金额 - returnmoney =', allMoney, '-', memberDiscount, '-', totalRefunded, '=', remaining);
|
||||
console.log('累计退款金额:', this.totalRefundedAmount);
|
||||
console.log('剩余可退款金额将通过计算属性自动计算');
|
||||
},
|
||||
|
||||
initForm() {
|
||||
|
|
@ -862,10 +728,8 @@ export default {
|
|||
if (this.refundHistory.length > 0) {
|
||||
this.calculateTotalRefunded();
|
||||
} else {
|
||||
// 如果没有退款历史,设置默认值:allmoney - 优惠金额
|
||||
const allMoney = parseFloat(this.actualPaymentData.allmoney || 0);
|
||||
const memberDiscount = parseFloat(this.actualPaymentData.membermoney || 0);
|
||||
this.remainingRefundableAmount = (allMoney - memberDiscount).toFixed(2);
|
||||
// 如果没有退款历史,设置默认值:累计退款为0,剩余可退款通过计算属性自动计算
|
||||
this.totalRefundedAmount = "0.00";
|
||||
}
|
||||
|
||||
this.calculateTotalRefund();
|
||||
|
|
@ -896,7 +760,7 @@ export default {
|
|||
|
||||
validateRefundAmount() {
|
||||
const totalRefund = parseFloat(this.totalRefundAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmount);
|
||||
const remaining = parseFloat(this.remainingRefundableAmountComputed);
|
||||
|
||||
if (totalRefund <= 0) {
|
||||
this.$message.error("退款金额必须大于0");
|
||||
|
|
@ -904,7 +768,7 @@ export default {
|
|||
}
|
||||
|
||||
if (totalRefund > remaining) {
|
||||
this.$message.error(`退款金额不能超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmount}(实际可退款金额:¥${this.actualRefundableAmount})`);
|
||||
this.$message.error(`退款金额不能超过剩余可退款金额,最多可退:¥${this.remainingRefundableAmountComputed}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -996,7 +860,7 @@ export default {
|
|||
this.confirmLoading = false; // 重置确认按钮加载状态
|
||||
this.refundHistory = [];
|
||||
this.totalRefundedAmount = "0.00";
|
||||
this.remainingRefundableAmount = "0.00";
|
||||
// 剩余可退款金额将通过计算属性自动计算
|
||||
this.refundHistoryLoading = false;
|
||||
this.refundHistoryError = ""; // 重置错误信息
|
||||
this.expandedItems = []; // 重置展开状态
|
||||
|
|
@ -1063,7 +927,13 @@ export default {
|
|||
try {
|
||||
if (item.content) {
|
||||
const content = JSON.parse(item.content);
|
||||
// 尝试多种可能的字段名
|
||||
|
||||
// 如果content中有remainingRefundable字段,直接使用
|
||||
if (content.remainingRefundable !== undefined) {
|
||||
return content.name || content.description || content.remark || '退款记录';
|
||||
}
|
||||
|
||||
// 否则尝试从其他字段获取
|
||||
if (content.name) {
|
||||
return content.name;
|
||||
} else if (content.description) {
|
||||
|
|
@ -1197,6 +1067,26 @@ export default {
|
|||
text: shareText,
|
||||
url: shareUrl
|
||||
});
|
||||
},
|
||||
|
||||
// 获取退款后的剩余可退款金额
|
||||
getRemainingRefundableAfterRefund(item) {
|
||||
try {
|
||||
if (item.content) {
|
||||
const content = JSON.parse(item.content);
|
||||
|
||||
// 如果content中有remainingRefundable字段,直接使用
|
||||
if (content.remainingRefundable !== undefined) {
|
||||
return parseFloat(content.remainingRefundable);
|
||||
}
|
||||
|
||||
// 否则使用计算属性计算
|
||||
return parseFloat(this.remainingRefundableAmountComputed);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('计算剩余可退款金额失败:', e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1305,6 +1195,72 @@ export default {
|
|||
font-size: 13px;
|
||||
color: #409EFF;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.refund-details {
|
||||
margin-top: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.refund-details .detail-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px solid #e8f4fd;
|
||||
}
|
||||
|
||||
.refund-details .detail-row:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.refund-details .detail-row.total-row {
|
||||
border-top: 2px solid #409EFF;
|
||||
border-bottom: none;
|
||||
margin-top: 10px;
|
||||
padding-top: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.refund-details .detail-row.compact {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px; /* 调整间距 */
|
||||
margin-bottom: 5px;
|
||||
padding: 5px 0;
|
||||
border-bottom: 1px solid #e8f4fd;
|
||||
}
|
||||
|
||||
.refund-details .detail-row.compact:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.refund-details .detail-label {
|
||||
color: #606266;
|
||||
font-weight: 500;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.refund-details .detail-value {
|
||||
color: #303133;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.refund-details .detail-value.total-value {
|
||||
color: #409EFF;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.refund-details .detail-separator {
|
||||
color: #c0c4cc;
|
||||
font-weight: 300;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.refund-input { background-color: #fff; padding: 20px; border-radius: 8px; border: 1px solid #e9ecef; margin-bottom: 20px; }
|
||||
|
|
@ -1943,4 +1899,35 @@ export default {
|
|||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.refund-operator {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.refund-operator i {
|
||||
margin-right: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.body-remaining {
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #409EFF;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.remaining-label {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.remaining-value {
|
||||
font-weight: 600;
|
||||
color: #409EFF;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
Loading…
Reference in New Issue