202507251746
This commit is contained in:
parent
b0653ae8ec
commit
cf3eb835b4
|
|
@ -0,0 +1,680 @@
|
||||||
|
package com.ruoyi.system.ControllerUtil;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
import com.ruoyi.system.domain.IntegralLog;
|
||||||
|
import com.ruoyi.system.domain.SiteConfig;
|
||||||
|
import com.ruoyi.system.domain.UserBenefitPoints;
|
||||||
|
import com.ruoyi.system.domain.Users;
|
||||||
|
import com.ruoyi.system.domain.UsersPayBefor;
|
||||||
|
import com.ruoyi.system.service.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分及购物消费金处理工具类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2025-01-27
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class IntegralAndBenefitUtil {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(IntegralAndBenefitUtil.class);
|
||||||
|
|
||||||
|
// 注入相关服务
|
||||||
|
private static final ISiteConfigService siteConfigService = SpringUtils.getBean(ISiteConfigService.class);
|
||||||
|
private static final IUsersService usersService = SpringUtils.getBean(IUsersService.class);
|
||||||
|
private static final IIntegralLogService integralLogService = SpringUtils.getBean(IIntegralLogService.class);
|
||||||
|
private static final IUserBenefitPointsService userBenefitPointsService = SpringUtils.getBean(IUserBenefitPointsService.class);
|
||||||
|
private static final IUsersPayBeforService usersPayBeforService = SpringUtils.getBean(IUsersPayBeforService.class);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 积分及购物消费金处理
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static JSONObject processIntegralAndBenefit(BigDecimal money, String orderid, Long uid,Long befoid) {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
UsersPayBefor usersPayBefor = usersPayBeforService.selectUsersPayBeforById(befoid);
|
||||||
|
Users users = usersService.selectUsersById(uid);
|
||||||
|
// 1. 查询配置信息
|
||||||
|
SiteConfig config = siteConfigService.selectSiteConfigByName("config_one");
|
||||||
|
if (config == null || config.getValue() == null) {
|
||||||
|
logger.error("未找到config_one配置信息");
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息缺失");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析配置信息
|
||||||
|
JSONObject configJson = JSONObject.parseObject(config.getValue());
|
||||||
|
BigDecimal orderScore = configJson.getBigDecimal("orderScore");
|
||||||
|
BigDecimal servicefee = configJson.getBigDecimal("servicefee");
|
||||||
|
BigDecimal consumption = configJson.getBigDecimal("consumption");
|
||||||
|
|
||||||
|
if (orderScore == null || servicefee == null || consumption == null) {
|
||||||
|
logger.error("配置信息不完整,orderScore: {}, servicefee: {}, consumption: {}",
|
||||||
|
orderScore, servicefee, consumption);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息不完整");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 处理用户积分
|
||||||
|
Long integralPoints = processUserIntegral(money, orderScore, orderid, users);
|
||||||
|
|
||||||
|
// 3. 处理消费金和服务金
|
||||||
|
boolean benefitResult = processUserBenefit(money, Math.toIntExact(usersPayBefor.getServicetype()), servicefee, consumption, orderid, users);
|
||||||
|
|
||||||
|
if (integralPoints > 0 && benefitResult) {
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("message", "积分和消费金处理成功");
|
||||||
|
result.put("integralPoints", integralPoints);
|
||||||
|
} else {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "积分或消费金处理失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("积分及购物消费金处理异常", e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "处理过程中发生异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理用户积分
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param orderScore 积分兑换比例
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @param users 用户对象
|
||||||
|
* @return 获得的积分数量
|
||||||
|
*/
|
||||||
|
private static Long processUserIntegral(BigDecimal money, BigDecimal orderScore, String orderid, Users users) {
|
||||||
|
try {
|
||||||
|
// 计算积分:money / orderScore,向下取整
|
||||||
|
BigDecimal integralDecimal = money.divide(orderScore, 0, RoundingMode.DOWN);
|
||||||
|
Long integralPoints = integralDecimal.longValue();
|
||||||
|
if (integralPoints > 0) {
|
||||||
|
// 更新用户积分
|
||||||
|
Long currentIntegral = users.getIntegral() != null ? users.getIntegral() : 0L;
|
||||||
|
Long currentTotalIntegral = users.getTotalIntegral() != null ? users.getTotalIntegral() : 0L;
|
||||||
|
|
||||||
|
users.setIntegral(currentIntegral + integralPoints);
|
||||||
|
users.setTotalIntegral(currentTotalIntegral + integralPoints);
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加积分日志
|
||||||
|
IntegralLog integralLog = new IntegralLog();
|
||||||
|
integralLog.setOrderId(orderid);
|
||||||
|
integralLog.setTitle("订单消费获得积分");
|
||||||
|
integralLog.setMark("订单消费" + money + "元,获得积分" + integralPoints + "点");
|
||||||
|
integralLog.setUid(users.getId());
|
||||||
|
integralLog.setUname(users.getName());
|
||||||
|
integralLog.setType(1L); // 1:增加
|
||||||
|
integralLog.setNum(integralPoints);
|
||||||
|
integralLog.setCreatedAt(new Date());
|
||||||
|
integralLog.setUpdatedAt(new Date());
|
||||||
|
|
||||||
|
integralLogService.insertIntegralLog(integralLog);
|
||||||
|
|
||||||
|
logger.info("用户{}积分处理成功,获得积分{}点", users.getId(), integralPoints);
|
||||||
|
return integralPoints;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户积分失败,用户ID: {}", users.getId());
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("订单金额{}元,积分兑换比例{},计算积分{}点,不进行积分处理",
|
||||||
|
money, orderScore, integralPoints);
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理用户积分异常", e);
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理用户消费金和服务金
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param type 类型:1-服务,2-商城
|
||||||
|
* @param servicefee 服务金比例
|
||||||
|
* @param consumption 消费金比例
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @param users 用户对象
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
private static boolean processUserBenefit(BigDecimal money, Integer type, BigDecimal servicefee,
|
||||||
|
BigDecimal consumption, String orderid, Users users) {
|
||||||
|
try {
|
||||||
|
BigDecimal benefitAmount = BigDecimal.ZERO;
|
||||||
|
Long benefitType = 0L;
|
||||||
|
String benefitName = "";
|
||||||
|
Integer intype;
|
||||||
|
if (type == 1) {
|
||||||
|
intype=2;
|
||||||
|
// 服务类型:增加消费金
|
||||||
|
benefitAmount = money.multiply(consumption).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
||||||
|
benefitType = 1L; // 消费金
|
||||||
|
benefitName = "服务消费金";
|
||||||
|
|
||||||
|
// 更新用户消费金
|
||||||
|
BigDecimal currentConsumption = users.getConsumption() != null ? users.getConsumption() : BigDecimal.ZERO;
|
||||||
|
users.setConsumption(currentConsumption.add(benefitAmount));
|
||||||
|
|
||||||
|
} else if (type == 2) {
|
||||||
|
intype=1;
|
||||||
|
// 商城类型:增加服务金
|
||||||
|
benefitAmount = money.multiply(servicefee).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
||||||
|
benefitType = 2L; // 服务金
|
||||||
|
benefitName = "商城服务金";
|
||||||
|
|
||||||
|
// 更新用户服务金
|
||||||
|
BigDecimal currentServicefee = users.getServicefee() != null ? users.getServicefee() : BigDecimal.ZERO;
|
||||||
|
users.setServicefee(currentServicefee.add(benefitAmount));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.error("无效的类型参数: {}", type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benefitAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加福利金日志
|
||||||
|
UserBenefitPoints benefitPoints = new UserBenefitPoints();
|
||||||
|
benefitPoints.setType(Long.valueOf(intype));
|
||||||
|
benefitPoints.setDotime(new Date());
|
||||||
|
benefitPoints.setOrdermoney(money);
|
||||||
|
benefitPoints.setMoney(benefitAmount);
|
||||||
|
benefitPoints.setOrdertype(1L); // 1:收入
|
||||||
|
benefitPoints.setUid(users.getId());
|
||||||
|
benefitPoints.setBeformoney(benefitType == 1L ?
|
||||||
|
(users.getConsumption() != null ? users.getConsumption().subtract(benefitAmount) : BigDecimal.ZERO) :
|
||||||
|
(users.getServicefee() != null ? users.getServicefee().subtract(benefitAmount) : BigDecimal.ZERO));
|
||||||
|
benefitPoints.setAftremoney(benefitType == 1L ? users.getConsumption() : users.getServicefee());
|
||||||
|
benefitPoints.setCreatedAt(new Date());
|
||||||
|
benefitPoints.setUpdatedAt(new Date());
|
||||||
|
//benefitPoints.setOrderid(Long.parseLong(orderid));
|
||||||
|
benefitPoints.setReamk("订单"+orderid+"消费获得" + benefitName);
|
||||||
|
|
||||||
|
userBenefitPointsService.insertUserBenefitPoints(benefitPoints);
|
||||||
|
|
||||||
|
logger.info("用户{}{}处理成功,金额{}元", users.getId(), benefitName, benefitAmount);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户{}失败,用户ID: {}", benefitName, users.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("订单金额{}元,类型{},计算{}金额{}元,不进行处理",
|
||||||
|
money, type, benefitName, benefitAmount);
|
||||||
|
return true; // 金额为0也算成功
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理用户{}异常", type == 1 ? "消费金" : "服务金", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减少积分及购物消费金处理
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @param users 用户对象
|
||||||
|
* @param type 类型:1-服务,2-商城
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static JSONObject reduceIntegralAndBenefit(BigDecimal money, String orderid, Users users, Integer type) {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 查询配置信息
|
||||||
|
SiteConfig config = siteConfigService.selectSiteConfigByName("config_one");
|
||||||
|
if (config == null || config.getValue() == null) {
|
||||||
|
logger.error("未找到config_one配置信息");
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息缺失");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析配置信息
|
||||||
|
JSONObject configJson = JSONObject.parseObject(config.getValue());
|
||||||
|
BigDecimal orderScore = configJson.getBigDecimal("orderScore");
|
||||||
|
BigDecimal servicefee = configJson.getBigDecimal("servicefee");
|
||||||
|
BigDecimal consumption = configJson.getBigDecimal("consumption");
|
||||||
|
|
||||||
|
if (orderScore == null || servicefee == null || consumption == null) {
|
||||||
|
logger.error("配置信息不完整,orderScore: {}, servicefee: {}, consumption: {}",
|
||||||
|
orderScore, servicefee, consumption);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息不完整");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 处理用户积分减少
|
||||||
|
Long integralPoints = reduceUserIntegral(money, orderScore, orderid, users);
|
||||||
|
|
||||||
|
// 3. 处理消费金和服务金减少
|
||||||
|
boolean benefitResult = reduceUserBenefit(money, type, servicefee, consumption, orderid, users);
|
||||||
|
|
||||||
|
if (integralPoints > 0 && benefitResult) {
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("message", "积分和消费金减少处理成功");
|
||||||
|
result.put("integralPoints", integralPoints);
|
||||||
|
} else {
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "积分或消费金减少处理失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("减少积分及购物消费金处理异常", e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "处理过程中发生异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减少用户积分
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param orderScore 积分兑换比例
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @param users 用户对象
|
||||||
|
* @return 减少的积分数量
|
||||||
|
*/
|
||||||
|
private static Long reduceUserIntegral(BigDecimal money, BigDecimal orderScore, String orderid, Users users) {
|
||||||
|
try {
|
||||||
|
// 计算积分:money / orderScore,向下取整
|
||||||
|
BigDecimal integralDecimal = money.divide(orderScore, 0, RoundingMode.DOWN);
|
||||||
|
Long integralPoints = integralDecimal.longValue();
|
||||||
|
|
||||||
|
if (integralPoints > 0) {
|
||||||
|
// 检查用户积分是否足够
|
||||||
|
Long currentIntegral = users.getIntegral() != null ? users.getIntegral() : 0L;
|
||||||
|
if (currentIntegral < integralPoints) {
|
||||||
|
logger.warn("用户{}积分不足,当前积分{},需要扣除{}", users.getId(), currentIntegral, integralPoints);
|
||||||
|
integralPoints = currentIntegral; // 只能扣除现有积分
|
||||||
|
}
|
||||||
|
|
||||||
|
if (integralPoints > 0) {
|
||||||
|
// 更新用户积分
|
||||||
|
users.setIntegral(currentIntegral - integralPoints);
|
||||||
|
// 注意:total_integral不减少,因为这是累计积分
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加积分减少日志
|
||||||
|
IntegralLog integralLog = new IntegralLog();
|
||||||
|
integralLog.setOrderId(orderid);
|
||||||
|
integralLog.setTitle("订单消费扣除积分");
|
||||||
|
integralLog.setMark("订单消费" + money + "元,扣除积分" + integralPoints + "点");
|
||||||
|
integralLog.setUid(users.getId());
|
||||||
|
integralLog.setUname(users.getName());
|
||||||
|
integralLog.setType(2L); // 2:减少
|
||||||
|
integralLog.setNum(integralPoints);
|
||||||
|
integralLog.setCreatedAt(new Date());
|
||||||
|
integralLog.setUpdatedAt(new Date());
|
||||||
|
|
||||||
|
integralLogService.insertIntegralLog(integralLog);
|
||||||
|
|
||||||
|
logger.info("用户{}积分扣除成功,扣除积分{}点", users.getId(), integralPoints);
|
||||||
|
return integralPoints;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户积分失败,用户ID: {}", users.getId());
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("用户{}积分不足,无法扣除", users.getId());
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("订单金额{}元,积分兑换比例{},计算积分{}点,不进行积分扣除",
|
||||||
|
money, orderScore, integralPoints);
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("扣除用户积分异常", e);
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 减少用户消费金和服务金
|
||||||
|
*
|
||||||
|
* @param money 订单金额
|
||||||
|
* @param type 类型:1-服务,2-商城
|
||||||
|
* @param servicefee 服务金比例
|
||||||
|
* @param consumption 消费金比例
|
||||||
|
* @param orderid 订单ID
|
||||||
|
* @param users 用户对象
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
private static boolean reduceUserBenefit(BigDecimal money, Integer type, BigDecimal servicefee,
|
||||||
|
BigDecimal consumption, String orderid, Users users) {
|
||||||
|
try {
|
||||||
|
BigDecimal benefitAmount = BigDecimal.ZERO;
|
||||||
|
Long benefitType = 0L;
|
||||||
|
String benefitName = "";
|
||||||
|
|
||||||
|
if (type == 1) {
|
||||||
|
// 服务类型:减少消费金
|
||||||
|
benefitAmount = money.multiply(consumption).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
||||||
|
benefitType = 1L; // 消费金
|
||||||
|
benefitName = "服务消费金";
|
||||||
|
|
||||||
|
// 检查用户消费金是否足够
|
||||||
|
BigDecimal currentConsumption = users.getConsumption() != null ? users.getConsumption() : BigDecimal.ZERO;
|
||||||
|
if (currentConsumption.compareTo(benefitAmount) < 0) {
|
||||||
|
logger.warn("用户{}消费金不足,当前{}元,需要扣除{}元", users.getId(), currentConsumption, benefitAmount);
|
||||||
|
benefitAmount = currentConsumption; // 只能扣除现有金额
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benefitAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
users.setConsumption(currentConsumption.subtract(benefitAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (type == 2) {
|
||||||
|
// 商城类型:减少服务金
|
||||||
|
benefitAmount = money.multiply(servicefee).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP);
|
||||||
|
benefitType = 2L; // 服务金
|
||||||
|
benefitName = "商城服务金";
|
||||||
|
|
||||||
|
// 检查用户服务金是否足够
|
||||||
|
BigDecimal currentServicefee = users.getServicefee() != null ? users.getServicefee() : BigDecimal.ZERO;
|
||||||
|
if (currentServicefee.compareTo(benefitAmount) < 0) {
|
||||||
|
logger.warn("用户{}服务金不足,当前{}元,需要扣除{}元", users.getId(), currentServicefee, benefitAmount);
|
||||||
|
benefitAmount = currentServicefee; // 只能扣除现有金额
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benefitAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
users.setServicefee(currentServicefee.subtract(benefitAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
logger.error("无效的类型参数: {}", type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (benefitAmount.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加福利金减少日志
|
||||||
|
UserBenefitPoints benefitPoints = new UserBenefitPoints();
|
||||||
|
benefitPoints.setType(benefitType);
|
||||||
|
benefitPoints.setDotime(new Date());
|
||||||
|
benefitPoints.setOrdermoney(money);
|
||||||
|
benefitPoints.setMoney(benefitAmount);
|
||||||
|
benefitPoints.setOrdertype(2L); // 2:支出
|
||||||
|
benefitPoints.setUid(users.getId());
|
||||||
|
benefitPoints.setBeformoney(benefitType == 1L ?
|
||||||
|
(users.getConsumption() != null ? users.getConsumption().add(benefitAmount) : benefitAmount) :
|
||||||
|
(users.getServicefee() != null ? users.getServicefee().add(benefitAmount) : benefitAmount));
|
||||||
|
benefitPoints.setAftremoney(benefitType == 1L ? users.getConsumption() : users.getServicefee());
|
||||||
|
benefitPoints.setCreatedAt(new Date());
|
||||||
|
benefitPoints.setUpdatedAt(new Date());
|
||||||
|
//benefitPoints.setOrderid(Long.parseLong(orderid));
|
||||||
|
benefitPoints.setReamk("订单"+orderid+"消费扣除" + benefitName);
|
||||||
|
|
||||||
|
userBenefitPointsService.insertUserBenefitPoints(benefitPoints);
|
||||||
|
|
||||||
|
logger.info("用户{}{}扣除成功,金额{}元", users.getId(), benefitName, benefitAmount);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户{}失败,用户ID: {}", benefitName, users.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("订单金额{}元,类型{},计算{}金额{}元,不进行扣除",
|
||||||
|
money, type, benefitName, benefitAmount);
|
||||||
|
return true; // 金额为0也算成功
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("扣除用户{}异常", type == 1 ? "消费金" : "服务金", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付后期处理
|
||||||
|
*
|
||||||
|
* @param usersPayBefor 支付前对象
|
||||||
|
* @param uid 用户ID
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
public static JSONObject paymentPostProcess(UsersPayBefor usersPayBefor, Long uid) {
|
||||||
|
JSONObject result = new JSONObject();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Users users = usersService.selectUsersById(uid);
|
||||||
|
if (users == null) {
|
||||||
|
logger.error("未找到用户信息,用户ID: {}", uid);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "用户信息不存在");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 查询配置信息
|
||||||
|
SiteConfig config = siteConfigService.selectSiteConfigByName("config_one");
|
||||||
|
if (config == null || config.getValue() == null) {
|
||||||
|
logger.error("未找到config_one配置信息");
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息缺失");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析配置信息
|
||||||
|
JSONObject configJson = JSONObject.parseObject(config.getValue());
|
||||||
|
BigDecimal servicefee = configJson.getBigDecimal("servicefee");
|
||||||
|
BigDecimal consumption = configJson.getBigDecimal("consumption");
|
||||||
|
|
||||||
|
if (servicefee == null || consumption == null) {
|
||||||
|
logger.error("配置信息不完整,servicefee: {}, consumption: {}", servicefee, consumption);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "系统配置信息不完整");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shopResult = false;
|
||||||
|
boolean serviceResult = false;
|
||||||
|
|
||||||
|
// 2. 处理购物金扣除(如果shopmoney有值)
|
||||||
|
BigDecimal shopmoney = usersPayBefor.getShopmoney();
|
||||||
|
if (shopmoney != null && shopmoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
try {
|
||||||
|
shopResult = processShopMoneyDeduction(shopmoney, users, consumption);
|
||||||
|
logger.info("购物金扣除处理完成,结果:{}", shopResult);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("购物金扣除处理异常", e);
|
||||||
|
shopResult = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("用户{}购物金为空或为0,不进行处理", users.getId());
|
||||||
|
shopResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 处理服务金扣除(如果servicemoney有值)
|
||||||
|
BigDecimal servicemoney = usersPayBefor.getServicemoney();
|
||||||
|
if (servicemoney != null && servicemoney.compareTo(BigDecimal.ZERO) > 0) {
|
||||||
|
try {
|
||||||
|
serviceResult = processServiceMoneyDeduction(servicemoney, users, servicefee);
|
||||||
|
logger.info("服务金扣除处理完成,结果:{}", serviceResult);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("服务金扣除处理异常", e);
|
||||||
|
serviceResult = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("用户{}服务金为空或为0,不进行处理", users.getId());
|
||||||
|
serviceResult = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 返回处理结果
|
||||||
|
result.put("success", true);
|
||||||
|
result.put("message", "支付后期处理完成");
|
||||||
|
result.put("shopResult", shopResult);
|
||||||
|
result.put("serviceResult", serviceResult);
|
||||||
|
|
||||||
|
logger.info("支付后期处理全部完成,购物金处理:{},服务金处理:{}", shopResult, serviceResult);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("支付后期处理异常", e);
|
||||||
|
result.put("success", false);
|
||||||
|
result.put("message", "处理过程中发生异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理服务金扣除
|
||||||
|
*
|
||||||
|
* @param servicemoney 服务金金额
|
||||||
|
* @param users 用户对象
|
||||||
|
* @param servicefee 服务金比例
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
private static boolean processServiceMoneyDeduction(BigDecimal servicemoney, Users users, BigDecimal servicefee) {
|
||||||
|
try {
|
||||||
|
// 计算扣除金额:servicemoney / servicefee%
|
||||||
|
// 例如:servicefee=5,表示5%,计算方式:servicemoney / (5/100)
|
||||||
|
BigDecimal servicefeeDecimal = servicefee.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||||
|
BigDecimal deductionAmount = servicemoney.divide(servicefeeDecimal, 2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
// 获取当前消费金余额
|
||||||
|
BigDecimal currentConsumption = users.getServicefee() != null ? users.getServicefee() : BigDecimal.ZERO;
|
||||||
|
BigDecimal actualDeductionAmount = deductionAmount;
|
||||||
|
|
||||||
|
// 如果余额不足,设置为0,记录实际扣除金额
|
||||||
|
if (currentConsumption.compareTo(deductionAmount) < 0) {
|
||||||
|
logger.warn("用户{}消费金不足,当前{}元,需要扣除{}元,将设置为0",
|
||||||
|
users.getId(), currentConsumption, deductionAmount);
|
||||||
|
actualDeductionAmount = currentConsumption;
|
||||||
|
users.setServicefee(BigDecimal.ZERO);
|
||||||
|
} else {
|
||||||
|
// 余额充足,正常扣除
|
||||||
|
users.setServicefee(currentConsumption.subtract(deductionAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加福利金扣除日志
|
||||||
|
UserBenefitPoints benefitPoints = new UserBenefitPoints();
|
||||||
|
benefitPoints.setType(1L); // 消费金
|
||||||
|
benefitPoints.setDotime(new Date());
|
||||||
|
benefitPoints.setOrdermoney(servicemoney);
|
||||||
|
benefitPoints.setMoney(actualDeductionAmount.negate()); // 负数
|
||||||
|
benefitPoints.setOrdertype(2L); // 支出
|
||||||
|
benefitPoints.setUid(users.getId());
|
||||||
|
benefitPoints.setBeformoney(currentConsumption);
|
||||||
|
benefitPoints.setAftremoney(users.getServicefee());
|
||||||
|
benefitPoints.setCreatedAt(new Date());
|
||||||
|
benefitPoints.setUpdatedAt(new Date());
|
||||||
|
benefitPoints.setReamk("支付后期处理扣除消费金,服务金金额:" + servicemoney + "元,服务金比例:" + servicefee + "%,实际扣除:" + actualDeductionAmount + "积分");
|
||||||
|
|
||||||
|
userBenefitPointsService.insertUserBenefitPoints(benefitPoints);
|
||||||
|
|
||||||
|
logger.info("用户{}服务金扣除完成,服务金{}元,服务金比例{}%,扣除消费金{}元,实际扣除{}元",
|
||||||
|
users.getId(), servicemoney, servicefee, deductionAmount, actualDeductionAmount);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户消费金失败,用户ID: {}", users.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理服务金扣除异常", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理购物金扣除
|
||||||
|
*
|
||||||
|
* @param shopmoney 购物金金额
|
||||||
|
* @param users 用户对象
|
||||||
|
* @param consumption 消费金比例
|
||||||
|
* @return 处理结果
|
||||||
|
*/
|
||||||
|
private static boolean processShopMoneyDeduction(BigDecimal shopmoney, Users users, BigDecimal consumption) {
|
||||||
|
try {
|
||||||
|
// 计算扣除金额:shopmoney / consumption%
|
||||||
|
// 例如:consumption=10,表示10%,计算方式:shopmoney / (10/100)
|
||||||
|
BigDecimal consumptionDecimal = consumption.divide(new BigDecimal("100"), 4, RoundingMode.HALF_UP);
|
||||||
|
BigDecimal deductionAmount = shopmoney.divide(consumptionDecimal, 2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
// 获取当前服务金余额
|
||||||
|
BigDecimal currentServicefee = users.getConsumption() != null ? users.getConsumption() : BigDecimal.ZERO;
|
||||||
|
BigDecimal actualDeductionAmount = deductionAmount;
|
||||||
|
|
||||||
|
// 如果余额不足,设置为0,记录实际扣除金额
|
||||||
|
if (currentServicefee.compareTo(deductionAmount) < 0) {
|
||||||
|
logger.warn("用户{}服务金不足,当前{}元,需要扣除{}元,将设置为0",
|
||||||
|
users.getId(), currentServicefee, deductionAmount);
|
||||||
|
actualDeductionAmount = currentServicefee;
|
||||||
|
users.setConsumption(BigDecimal.ZERO);
|
||||||
|
} else {
|
||||||
|
// 余额充足,正常扣除
|
||||||
|
users.setConsumption(currentServicefee.subtract(deductionAmount));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
int updateResult = usersService.updateUsers(users);
|
||||||
|
if (updateResult > 0) {
|
||||||
|
// 添加福利金扣除日志
|
||||||
|
UserBenefitPoints benefitPoints = new UserBenefitPoints();
|
||||||
|
benefitPoints.setType(2L); // 服务金
|
||||||
|
benefitPoints.setDotime(new Date());
|
||||||
|
benefitPoints.setOrdermoney(shopmoney);
|
||||||
|
benefitPoints.setMoney(actualDeductionAmount.negate()); // 负数
|
||||||
|
benefitPoints.setOrdertype(2L); // 支出
|
||||||
|
benefitPoints.setUid(users.getId());
|
||||||
|
benefitPoints.setBeformoney(currentServicefee);
|
||||||
|
benefitPoints.setAftremoney(users.getConsumption());
|
||||||
|
benefitPoints.setReamk("支付后期处理扣除服务金,购物金金额:" + shopmoney + "元,消费金比例:" + consumption + "%,实际扣除:" + actualDeductionAmount + "积分");
|
||||||
|
|
||||||
|
userBenefitPointsService.insertUserBenefitPoints(benefitPoints);
|
||||||
|
|
||||||
|
logger.info("用户{}购物金扣除完成,购物金{}元,消费金比例{}%,扣除服务金{}元,实际扣除{}元",
|
||||||
|
users.getId(), shopmoney, consumption, deductionAmount, actualDeductionAmount);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("更新用户服务金失败,用户ID: {}", users.getId());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("处理购物金扣除异常", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,175 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFmpeg诊断工具
|
||||||
|
* 用于分析FFmpeg合并失败的原因
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegDiagnostic {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== FFmpeg诊断工具 ===");
|
||||||
|
|
||||||
|
// 1. 检查FFmpeg可用性
|
||||||
|
System.out.println("1. 检查FFmpeg可用性");
|
||||||
|
boolean isAvailable = FFmpegUtils.isFFmpegAvailable();
|
||||||
|
System.out.println(" FFmpeg可用: " + isAvailable);
|
||||||
|
|
||||||
|
if (isAvailable) {
|
||||||
|
String version = FFmpegUtils.getFFmpegVersion();
|
||||||
|
System.out.println(" FFmpeg版本: " + version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 测试简单的FFmpeg命令
|
||||||
|
System.out.println("\n2. 测试简单FFmpeg命令");
|
||||||
|
testSimpleFFmpegCommand();
|
||||||
|
|
||||||
|
// 3. 检查文件路径问题
|
||||||
|
System.out.println("\n3. 检查文件路径");
|
||||||
|
checkFilePathIssues();
|
||||||
|
|
||||||
|
// 4. 测试文件格式支持
|
||||||
|
System.out.println("\n4. 测试文件格式支持");
|
||||||
|
testFileFormatSupport();
|
||||||
|
|
||||||
|
System.out.println("\n=== 诊断完成 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试简单的FFmpeg命令
|
||||||
|
*/
|
||||||
|
private static void testSimpleFFmpegCommand() {
|
||||||
|
try {
|
||||||
|
String ffmpegPath = getFFmpegPath();
|
||||||
|
if (ffmpegPath == null) {
|
||||||
|
System.out.println(" 无法获取FFmpeg路径");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(" FFmpeg路径: " + ffmpegPath);
|
||||||
|
|
||||||
|
// 测试-help命令
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(ffmpegPath, "-help");
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
// 读取输出
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
System.out.println(" 简单命令测试 - 退出码: " + exitCode);
|
||||||
|
|
||||||
|
if (exitCode == 0) {
|
||||||
|
System.out.println(" ✓ FFmpeg基本功能正常");
|
||||||
|
} else {
|
||||||
|
System.out.println(" ✗ FFmpeg基本功能异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" 测试简单命令失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件路径问题
|
||||||
|
*/
|
||||||
|
private static void checkFilePathIssues() {
|
||||||
|
String[] testPaths = {
|
||||||
|
"C:\\test\\audio.mp3",
|
||||||
|
"/tmp/test/audio.mp3",
|
||||||
|
"test_audio.mp3",
|
||||||
|
"D:\\javacode\\RuoYi-Vue-master\\test.mp3"
|
||||||
|
};
|
||||||
|
|
||||||
|
System.out.println(" 检查文件路径格式:");
|
||||||
|
for (String path : testPaths) {
|
||||||
|
File file = new File(path);
|
||||||
|
System.out.println(" " + path);
|
||||||
|
System.out.println(" 存在: " + file.exists());
|
||||||
|
System.out.println(" 可读: " + file.canRead());
|
||||||
|
System.out.println(" 绝对路径: " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试文件格式支持
|
||||||
|
*/
|
||||||
|
private static void testFileFormatSupport() {
|
||||||
|
try {
|
||||||
|
String ffmpegPath = getFFmpegPath();
|
||||||
|
if (ffmpegPath == null) {
|
||||||
|
System.out.println(" 无法获取FFmpeg路径");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试支持的格式
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(ffmpegPath, "-formats");
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean foundMp3 = false;
|
||||||
|
boolean foundWav = false;
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
if (line.contains("mp3")) foundMp3 = true;
|
||||||
|
if (line.contains("wav")) foundWav = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
process.waitFor();
|
||||||
|
|
||||||
|
System.out.println(" 支持的音频格式:");
|
||||||
|
System.out.println(" MP3: " + (foundMp3 ? "✓" : "✗"));
|
||||||
|
System.out.println(" WAV: " + (foundWav ? "✓" : "✗"));
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.out.println(" 测试格式支持失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取FFmpeg路径(简化版)
|
||||||
|
*/
|
||||||
|
private static String getFFmpegPath() {
|
||||||
|
String[] paths = {
|
||||||
|
"ffmpeg",
|
||||||
|
"C:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"D:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
System.getProperty("user.dir") + "/ruoyi-system/src/main/resources/ffmpeg/ffmpeg.exe"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String path : paths) {
|
||||||
|
try {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(path, "-version");
|
||||||
|
Process process = pb.start();
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
if (exitCode == 0) {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 忽略错误,继续尝试下一个路径
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFmpeg合并功能测试类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegMergeTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== FFmpeg合并功能测试 ===");
|
||||||
|
|
||||||
|
// 测试FFmpeg是否可用
|
||||||
|
boolean isAvailable = FFmpegUtils.isFFmpegAvailable();
|
||||||
|
System.out.println("FFmpeg是否可用: " + isAvailable);
|
||||||
|
|
||||||
|
if (!isAvailable) {
|
||||||
|
System.out.println("FFmpeg不可用,测试终止");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取FFmpeg版本
|
||||||
|
String version = FFmpegUtils.getFFmpegVersion();
|
||||||
|
System.out.println("FFmpeg版本: " + version);
|
||||||
|
|
||||||
|
// 创建测试文件路径
|
||||||
|
String testDir = System.getProperty("user.dir") + "/test_audio";
|
||||||
|
File testDirFile = new File(testDir);
|
||||||
|
if (!testDirFile.exists()) {
|
||||||
|
testDirFile.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟输入文件列表(这里需要实际的音频文件)
|
||||||
|
List<String> inputFiles = Arrays.asList(
|
||||||
|
testDir + "/test1.mp3",
|
||||||
|
testDir + "/test2.mp3"
|
||||||
|
);
|
||||||
|
|
||||||
|
String outputFile = testDir + "/merged_output.mp3";
|
||||||
|
|
||||||
|
System.out.println("输入文件:");
|
||||||
|
for (String file : inputFiles) {
|
||||||
|
File f = new File(file);
|
||||||
|
System.out.println(" " + file + " (存在: " + f.exists() + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("输出文件: " + outputFile);
|
||||||
|
|
||||||
|
// 测试合并功能
|
||||||
|
System.out.println("开始测试合并功能...");
|
||||||
|
boolean success = FFmpegUtils.mergeAudioFiles(inputFiles, outputFile);
|
||||||
|
|
||||||
|
System.out.println("合并结果: " + (success ? "成功" : "失败"));
|
||||||
|
|
||||||
|
// 检查输出文件
|
||||||
|
File output = new File(outputFile);
|
||||||
|
if (output.exists()) {
|
||||||
|
System.out.println("输出文件存在,大小: " + output.length() + " bytes");
|
||||||
|
} else {
|
||||||
|
System.out.println("输出文件不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== 测试完成 ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,104 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFmpeg Opus转MP3测试工具
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegOpusTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== FFmpeg Opus转MP3测试 ===");
|
||||||
|
|
||||||
|
// 测试FFmpeg是否可用
|
||||||
|
boolean isAvailable = FFmpegUtilsSimple.isFFmpegAvailable();
|
||||||
|
System.out.println("FFmpeg是否可用: " + isAvailable);
|
||||||
|
|
||||||
|
if (!isAvailable) {
|
||||||
|
System.out.println("FFmpeg不可用,测试终止");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取FFmpeg版本
|
||||||
|
String version = FFmpegUtilsSimple.getFFmpegVersion();
|
||||||
|
System.out.println("FFmpeg版本: " + version);
|
||||||
|
|
||||||
|
// 测试音频格式检测
|
||||||
|
System.out.println("\n=== 音频格式检测测试 ===");
|
||||||
|
testAudioFormatDetection();
|
||||||
|
|
||||||
|
// 测试合并功能
|
||||||
|
System.out.println("\n=== 合并功能测试 ===");
|
||||||
|
testMergeFunction();
|
||||||
|
|
||||||
|
System.out.println("\n=== 测试完成 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试音频格式检测
|
||||||
|
*/
|
||||||
|
private static void testAudioFormatDetection() {
|
||||||
|
// 这里可以添加一些测试文件路径
|
||||||
|
String[] testFiles = {
|
||||||
|
"D:/ruoyi/uploadPath/upload/sound/2329/2025-08-02/wDoBh4bMpIXZcb9cc77fe118c54e5cba9ff87ec3ba12.durationTime=9472_20250802155052A017.mp3"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String filePath : testFiles) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (file.exists()) {
|
||||||
|
String format = FFmpegUtilsSimple.detectAudioFormat(filePath);
|
||||||
|
System.out.println("文件: " + filePath);
|
||||||
|
System.out.println(" 存在: " + file.exists());
|
||||||
|
System.out.println(" 大小: " + file.length() + " bytes");
|
||||||
|
System.out.println(" 格式: " + format);
|
||||||
|
System.out.println();
|
||||||
|
} else {
|
||||||
|
System.out.println("文件不存在: " + filePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 测试合并功能
|
||||||
|
*/
|
||||||
|
private static void testMergeFunction() {
|
||||||
|
// 使用实际的音频文件路径
|
||||||
|
List<String> inputFiles = Arrays.asList(
|
||||||
|
"D:/ruoyi/uploadPath/upload/sound/2329/2025-08-02/wDoBh4bMpIXZcb9cc77fe118c54e5cba9ff87ec3ba12.durationTime=9472_20250802155052A017.mp3"
|
||||||
|
);
|
||||||
|
|
||||||
|
String outputFile = "C:/Users/user/AppData/Local/Temp/test_merged_output.mp3";
|
||||||
|
|
||||||
|
System.out.println("输入文件:");
|
||||||
|
for (String file : inputFiles) {
|
||||||
|
File f = new File(file);
|
||||||
|
String format = FFmpegUtilsSimple.detectAudioFormat(file);
|
||||||
|
System.out.println(" " + file);
|
||||||
|
System.out.println(" 存在: " + f.exists());
|
||||||
|
System.out.println(" 大小: " + f.length() + " bytes");
|
||||||
|
System.out.println(" 格式: " + format);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("输出文件: " + outputFile);
|
||||||
|
|
||||||
|
// 测试合并功能
|
||||||
|
System.out.println("开始测试合并功能...");
|
||||||
|
boolean success = FFmpegUtilsSimple.mergeAudioFiles(inputFiles, outputFile);
|
||||||
|
|
||||||
|
System.out.println("合并结果: " + (success ? "成功" : "失败"));
|
||||||
|
|
||||||
|
// 检查输出文件
|
||||||
|
File output = new File(outputFile);
|
||||||
|
if (output.exists()) {
|
||||||
|
System.out.println("输出文件存在,大小: " + output.length() + " bytes");
|
||||||
|
String outputFormat = FFmpegUtilsSimple.detectAudioFormat(outputFile);
|
||||||
|
System.out.println("输出文件格式: " + outputFormat);
|
||||||
|
} else {
|
||||||
|
System.out.println("输出文件不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFmpeg集成测试类
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegTest {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println("=== FFmpeg集成测试 ===");
|
||||||
|
|
||||||
|
// 测试FFmpeg是否可用
|
||||||
|
boolean isAvailable = FFmpegUtils.isFFmpegAvailable();
|
||||||
|
System.out.println("FFmpeg是否可用: " + isAvailable);
|
||||||
|
|
||||||
|
if (isAvailable) {
|
||||||
|
// 获取FFmpeg版本
|
||||||
|
String version = FFmpegUtils.getFFmpegVersion();
|
||||||
|
System.out.println("FFmpeg版本: " + version);
|
||||||
|
} else {
|
||||||
|
System.out.println("FFmpeg不可用,请检查安装");
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("=== 测试完成 ===");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,349 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.io.FileUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FFmpeg工具类
|
||||||
|
* 用于音频文件合并等操作
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegUtils {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FFmpegUtils.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找FFmpeg路径
|
||||||
|
*/
|
||||||
|
private static String findFFmpegPath() {
|
||||||
|
// 首先尝试项目内部的FFmpeg
|
||||||
|
String projectFFmpegPath = getProjectFFmpegPath();
|
||||||
|
if (projectFFmpegPath != null && isExecutable(projectFFmpegPath)) {
|
||||||
|
logger.info("使用项目内部FFmpeg: {}", projectFFmpegPath);
|
||||||
|
return projectFFmpegPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果项目内部没有,尝试系统安装的FFmpeg
|
||||||
|
String[] systemPaths = {
|
||||||
|
"ffmpeg",
|
||||||
|
"C:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"D:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"/usr/bin/ffmpeg",
|
||||||
|
"/usr/local/bin/ffmpeg"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String path : systemPaths) {
|
||||||
|
if (isExecutable(path)) {
|
||||||
|
logger.info("使用系统FFmpeg: {}", path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error("未找到可用的FFmpeg");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目内部的FFmpeg路径
|
||||||
|
*/
|
||||||
|
private static String getProjectFFmpegPath() {
|
||||||
|
try {
|
||||||
|
// 获取项目资源目录中的FFmpeg路径
|
||||||
|
String resourcePath = "/ffmpeg/ffmpeg.exe";
|
||||||
|
java.net.URL resourceUrl = FFmpegUtils.class.getResource(resourcePath);
|
||||||
|
|
||||||
|
if (resourceUrl != null) {
|
||||||
|
// 如果是jar包中的资源,需要提取到临时目录
|
||||||
|
if (resourceUrl.getProtocol().equals("jar")) {
|
||||||
|
return extractFFmpegFromJar();
|
||||||
|
} else {
|
||||||
|
// 如果是文件系统中的资源,直接返回路径
|
||||||
|
return new java.io.File(resourceUrl.toURI()).getAbsolutePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从classpath中查找
|
||||||
|
String classpathPath = System.getProperty("user.dir") + "/ruoyi-system/src/main/resources/ffmpeg/ffmpeg.exe";
|
||||||
|
java.io.File classpathFile = new java.io.File(classpathPath);
|
||||||
|
if (classpathFile.exists()) {
|
||||||
|
return classpathFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取项目内部FFmpeg路径失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从jar包中提取FFmpeg到临时目录
|
||||||
|
*/
|
||||||
|
private static String extractFFmpegFromJar() {
|
||||||
|
try {
|
||||||
|
// 创建临时目录
|
||||||
|
java.io.File tempDir = java.io.File.createTempFile("ffmpeg", "");
|
||||||
|
tempDir.delete();
|
||||||
|
tempDir.mkdirs();
|
||||||
|
|
||||||
|
// 提取FFmpeg
|
||||||
|
String ffmpegPath = tempDir.getAbsolutePath() + "/ffmpeg.exe";
|
||||||
|
java.io.File ffmpegFile = new java.io.File(ffmpegPath);
|
||||||
|
|
||||||
|
// 从jar包中复制文件
|
||||||
|
java.io.InputStream inputStream = FFmpegUtils.class.getResourceAsStream("/ffmpeg/ffmpeg.exe");
|
||||||
|
if (inputStream != null) {
|
||||||
|
java.io.FileOutputStream outputStream = new java.io.FileOutputStream(ffmpegFile);
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int length;
|
||||||
|
while ((length = inputStream.read(buffer)) > 0) {
|
||||||
|
outputStream.write(buffer, 0, length);
|
||||||
|
}
|
||||||
|
inputStream.close();
|
||||||
|
outputStream.close();
|
||||||
|
|
||||||
|
// 设置可执行权限
|
||||||
|
ffmpegFile.setExecutable(true);
|
||||||
|
|
||||||
|
logger.info("从jar包提取FFmpeg到: {}", ffmpegPath);
|
||||||
|
return ffmpegPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("从jar包提取FFmpeg失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否可执行
|
||||||
|
*/
|
||||||
|
private static boolean isExecutable(String path) {
|
||||||
|
try {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(path, "-version");
|
||||||
|
Process process = pb.start();
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
return exitCode == 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并音频文件
|
||||||
|
*
|
||||||
|
* @param inputFiles 输入文件列表
|
||||||
|
* @param outputFile 输出文件路径
|
||||||
|
* @return 是否成功
|
||||||
|
*/
|
||||||
|
public static boolean mergeAudioFiles(List<String> inputFiles, String outputFile) {
|
||||||
|
try {
|
||||||
|
logger.info("开始合并音频文件,输入文件数量: {}, 输出文件: {}", inputFiles.size(), outputFile);
|
||||||
|
|
||||||
|
// 检查输入文件
|
||||||
|
for (String inputFile : inputFiles) {
|
||||||
|
if (!validateAudioFile(inputFile)) {
|
||||||
|
logger.error("输入文件验证失败: {}", inputFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建输出目录
|
||||||
|
File outputDir = new File(outputFile).getParentFile();
|
||||||
|
if (!outputDir.exists()) {
|
||||||
|
outputDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用系统命令方式合并
|
||||||
|
return mergeWithSystemCommand(inputFiles, outputFile);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("合并音频文件失败", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用系统命令方式合并
|
||||||
|
*/
|
||||||
|
private static boolean mergeWithSystemCommand(List<String> inputFiles, String outputFile) throws IOException {
|
||||||
|
// 创建输入文件列表
|
||||||
|
Path inputListFile = Files.createTempFile("input_list", ".txt");
|
||||||
|
try {
|
||||||
|
// 写入文件列表
|
||||||
|
StringBuilder content = new StringBuilder();
|
||||||
|
for (String inputFile : inputFiles) {
|
||||||
|
content.append("file '").append(inputFile).append("'\n");
|
||||||
|
logger.info("添加输入文件: {}", inputFile);
|
||||||
|
}
|
||||||
|
Files.write(inputListFile, content.toString().getBytes());
|
||||||
|
|
||||||
|
// 记录输入文件列表内容
|
||||||
|
logger.info("输入文件列表内容:\n{}", content.toString());
|
||||||
|
|
||||||
|
// 构建系统命令
|
||||||
|
String ffmpegPath = findFFmpegPath();
|
||||||
|
if (ffmpegPath == null) {
|
||||||
|
logger.error("未找到可用的FFmpeg");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建FFmpeg命令
|
||||||
|
List<String> command = new ArrayList<>();
|
||||||
|
command.add(ffmpegPath);
|
||||||
|
command.add("-f");
|
||||||
|
command.add("concat");
|
||||||
|
command.add("-safe");
|
||||||
|
command.add("0");
|
||||||
|
command.add("-i");
|
||||||
|
command.add(inputListFile.toString());
|
||||||
|
command.add("-c");
|
||||||
|
command.add("copy");
|
||||||
|
command.add(outputFile);
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
|
|
||||||
|
// 记录完整的FFmpeg命令
|
||||||
|
logger.info("执行FFmpeg命令: {}", String.join(" ", command));
|
||||||
|
|
||||||
|
// 设置工作目录
|
||||||
|
pb.directory(new File(System.getProperty("user.dir")));
|
||||||
|
|
||||||
|
// 合并错误和标准输出
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
|
||||||
|
// 执行命令
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
// 读取输出
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
try {
|
||||||
|
exitCode = process.waitFor();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.error("FFmpeg进程被中断", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitCode != 0) {
|
||||||
|
logger.error("FFmpeg合并失败,退出码: {}, 输出: {}", exitCode, output.toString());
|
||||||
|
|
||||||
|
// 分析错误原因
|
||||||
|
String errorOutput = output.toString();
|
||||||
|
if (errorOutput.contains("No such file or directory")) {
|
||||||
|
logger.error("错误原因: 输入文件不存在或路径错误");
|
||||||
|
} else if (errorOutput.contains("Permission denied")) {
|
||||||
|
logger.error("错误原因: 权限不足,无法读取输入文件或写入输出文件");
|
||||||
|
} else if (errorOutput.contains("Invalid data found")) {
|
||||||
|
logger.error("错误原因: 输入文件格式无效或损坏");
|
||||||
|
} else if (errorOutput.contains("No such file or directory")) {
|
||||||
|
logger.error("错误原因: 输出目录不存在或无法创建");
|
||||||
|
} else {
|
||||||
|
logger.error("错误原因: 未知错误,请检查FFmpeg输出信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证输出文件
|
||||||
|
File outputFileObj = new File(outputFile);
|
||||||
|
if (outputFileObj.exists() && outputFileObj.length() > 0) {
|
||||||
|
logger.info("音频文件合并成功: {}", outputFile);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("合并后的文件不存在或为空: {}", outputFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 清理临时文件
|
||||||
|
Files.deleteIfExists(inputListFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查FFmpeg是否可用
|
||||||
|
*/
|
||||||
|
public static boolean isFFmpegAvailable() {
|
||||||
|
return findFFmpegPath() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证音频文件格式
|
||||||
|
*/
|
||||||
|
private static boolean validateAudioFile(String filePath) {
|
||||||
|
try {
|
||||||
|
File file = new File(filePath);
|
||||||
|
if (!file.exists()) {
|
||||||
|
logger.error("文件不存在: {}", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.canRead()) {
|
||||||
|
logger.error("文件无法读取: {}", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件扩展名
|
||||||
|
String fileName = file.getName().toLowerCase();
|
||||||
|
if (!fileName.endsWith(".mp3") && !fileName.endsWith(".wav") &&
|
||||||
|
!fileName.endsWith(".m4a") && !fileName.endsWith(".aac")) {
|
||||||
|
logger.warn("文件格式可能不支持: {}", filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查文件大小
|
||||||
|
long fileSize = file.length();
|
||||||
|
if (fileSize == 0) {
|
||||||
|
logger.error("文件为空: {}", filePath);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("文件验证通过: {} (大小: {} bytes)", filePath, fileSize);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("验证文件失败: {}", filePath, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取FFmpeg版本信息
|
||||||
|
*/
|
||||||
|
public static String getFFmpegVersion() {
|
||||||
|
try {
|
||||||
|
String ffmpegPath = findFFmpegPath();
|
||||||
|
if (ffmpegPath != null) {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(ffmpegPath, "-version");
|
||||||
|
Process process = pb.start();
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
return reader.readLine();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取FFmpeg版本失败", e);
|
||||||
|
}
|
||||||
|
return "FFmpeg未安装";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,321 @@
|
||||||
|
package com.ruoyi.system.utils;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化版FFmpeg工具类
|
||||||
|
* 用于音频文件合并等操作
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
public class FFmpegUtilsSimple {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(FFmpegUtilsSimple.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找FFmpeg路径
|
||||||
|
*/
|
||||||
|
private static String findFFmpegPath() {
|
||||||
|
// 首先尝试项目内部的FFmpeg
|
||||||
|
String projectFFmpegPath = getProjectFFmpegPath();
|
||||||
|
if (projectFFmpegPath != null && isExecutable(projectFFmpegPath)) {
|
||||||
|
logger.info("使用项目内部FFmpeg: {}", projectFFmpegPath);
|
||||||
|
return projectFFmpegPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果项目内部没有,尝试系统安装的FFmpeg
|
||||||
|
String[] systemPaths = {
|
||||||
|
"ffmpeg",
|
||||||
|
"C:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"C:\\Program Files\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"D:\\ffmpeg\\bin\\ffmpeg.exe",
|
||||||
|
"/usr/bin/ffmpeg",
|
||||||
|
"/usr/local/bin/ffmpeg"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (String path : systemPaths) {
|
||||||
|
if (isExecutable(path)) {
|
||||||
|
logger.info("使用系统FFmpeg: {}", path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.error("未找到可用的FFmpeg");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取项目内部的FFmpeg路径
|
||||||
|
*/
|
||||||
|
private static String getProjectFFmpegPath() {
|
||||||
|
try {
|
||||||
|
// 尝试从classpath中查找
|
||||||
|
String classpathPath = System.getProperty("user.dir") + "/ruoyi-system/src/main/resources/ffmpeg/ffmpeg.exe";
|
||||||
|
File classpathFile = new File(classpathPath);
|
||||||
|
if (classpathFile.exists()) {
|
||||||
|
return classpathFile.getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从资源中获取
|
||||||
|
String resourcePath = "/ffmpeg/ffmpeg.exe";
|
||||||
|
java.net.URL resourceUrl = FFmpegUtilsSimple.class.getResource(resourcePath);
|
||||||
|
if (resourceUrl != null) {
|
||||||
|
return new File(resourceUrl.toURI()).getAbsolutePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取项目内部FFmpeg路径失败", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查文件是否可执行
|
||||||
|
*/
|
||||||
|
private static boolean isExecutable(String path) {
|
||||||
|
try {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(path, "-version");
|
||||||
|
Process process = pb.start();
|
||||||
|
int exitCode = process.waitFor();
|
||||||
|
return exitCode == 0;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合并音频文件
|
||||||
|
*/
|
||||||
|
public static boolean mergeAudioFiles(List<String> inputFiles, String outputFile) {
|
||||||
|
try {
|
||||||
|
logger.info("开始合并音频文件,输入文件数量: {}, 输出文件: {}", inputFiles.size(), outputFile);
|
||||||
|
|
||||||
|
// 检查输入文件
|
||||||
|
for (String inputFile : inputFiles) {
|
||||||
|
File file = new File(inputFile);
|
||||||
|
if (!file.exists()) {
|
||||||
|
logger.error("输入文件不存在: {}", inputFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!file.canRead()) {
|
||||||
|
logger.error("输入文件无法读取: {}", inputFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检测音频格式
|
||||||
|
String format = detectAudioFormat(inputFile);
|
||||||
|
logger.info("验证输入文件: {} (大小: {} bytes, 格式: {})", inputFile, file.length(), format);
|
||||||
|
|
||||||
|
// 如果检测到Opus格式,记录警告
|
||||||
|
if ("Opus".equals(format)) {
|
||||||
|
logger.warn("检测到Opus格式音频文件: {},将进行转码", inputFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建输出目录
|
||||||
|
File outputDir = new File(outputFile).getParentFile();
|
||||||
|
if (!outputDir.exists()) {
|
||||||
|
outputDir.mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用系统命令方式合并
|
||||||
|
return mergeWithSystemCommand(inputFiles, outputFile);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("合并音频文件失败", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用系统命令方式合并
|
||||||
|
*/
|
||||||
|
private static boolean mergeWithSystemCommand(List<String> inputFiles, String outputFile) throws IOException {
|
||||||
|
// 创建输入文件列表
|
||||||
|
Path inputListFile = Files.createTempFile("input_list", ".txt");
|
||||||
|
try {
|
||||||
|
// 写入文件列表
|
||||||
|
StringBuilder content = new StringBuilder();
|
||||||
|
for (String inputFile : inputFiles) {
|
||||||
|
content.append("file '").append(inputFile).append("'\n");
|
||||||
|
logger.info("添加输入文件: {}", inputFile);
|
||||||
|
}
|
||||||
|
Files.write(inputListFile, content.toString().getBytes());
|
||||||
|
|
||||||
|
// 记录输入文件列表内容
|
||||||
|
logger.info("输入文件列表内容:\n{}", content.toString());
|
||||||
|
|
||||||
|
// 构建系统命令
|
||||||
|
String ffmpegPath = findFFmpegPath();
|
||||||
|
if (ffmpegPath == null) {
|
||||||
|
logger.error("未找到可用的FFmpeg");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建FFmpeg命令 - 使用转码而不是复制
|
||||||
|
String[] command = {
|
||||||
|
ffmpegPath,
|
||||||
|
"-f", "concat",
|
||||||
|
"-safe", "0",
|
||||||
|
"-i", inputListFile.toString(),
|
||||||
|
"-c:a", "libmp3lame", // 使用MP3编码器
|
||||||
|
"-b:a", "128k", // 设置比特率
|
||||||
|
"-ar", "44100", // 设置采样率
|
||||||
|
"-ac", "1", // 设置为单声道
|
||||||
|
outputFile
|
||||||
|
};
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(command);
|
||||||
|
|
||||||
|
// 记录完整的FFmpeg命令
|
||||||
|
logger.info("执行FFmpeg命令: {}", String.join(" ", command));
|
||||||
|
|
||||||
|
// 设置工作目录
|
||||||
|
pb.directory(new File(System.getProperty("user.dir")));
|
||||||
|
|
||||||
|
// 合并错误和标准输出
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
|
||||||
|
// 执行命令
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
// 读取输出
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitCode;
|
||||||
|
try {
|
||||||
|
exitCode = process.waitFor();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.error("FFmpeg进程被中断", e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exitCode != 0) {
|
||||||
|
logger.error("FFmpeg合并失败,退出码: {}, 输出: {}", exitCode, output.toString());
|
||||||
|
|
||||||
|
// 分析错误原因
|
||||||
|
String errorOutput = output.toString();
|
||||||
|
if (errorOutput.contains("No such file or directory")) {
|
||||||
|
logger.error("错误原因: 输入文件不存在或路径错误");
|
||||||
|
} else if (errorOutput.contains("Permission denied")) {
|
||||||
|
logger.error("错误原因: 权限不足,无法读取输入文件或写入输出文件");
|
||||||
|
} else if (errorOutput.contains("Invalid data found")) {
|
||||||
|
logger.error("错误原因: 输入文件格式无效或损坏");
|
||||||
|
} else if (errorOutput.contains("opus") && errorOutput.contains("Invalid audio stream")) {
|
||||||
|
logger.error("错误原因: 输入文件包含Opus音频流,需要转码为MP3格式");
|
||||||
|
} else if (errorOutput.contains("Invalid audio stream")) {
|
||||||
|
logger.error("错误原因: 音频流格式不兼容,需要转码");
|
||||||
|
} else {
|
||||||
|
logger.error("错误原因: 未知错误,请检查FFmpeg输出信息");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证输出文件
|
||||||
|
File outputFileObj = new File(outputFile);
|
||||||
|
if (outputFileObj.exists() && outputFileObj.length() > 0) {
|
||||||
|
logger.info("音频文件合并成功: {} (大小: {} bytes)", outputFile, outputFileObj.length());
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error("合并后的文件不存在或为空: {}", outputFile);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// 清理临时文件
|
||||||
|
Files.deleteIfExists(inputListFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查FFmpeg是否可用
|
||||||
|
*/
|
||||||
|
public static boolean isFFmpegAvailable() {
|
||||||
|
return findFFmpegPath() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取FFmpeg版本信息
|
||||||
|
*/
|
||||||
|
public static String getFFmpegVersion() {
|
||||||
|
try {
|
||||||
|
String ffmpegPath = findFFmpegPath();
|
||||||
|
if (ffmpegPath != null) {
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(ffmpegPath, "-version");
|
||||||
|
Process process = pb.start();
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
return reader.readLine();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取FFmpeg版本失败", e);
|
||||||
|
}
|
||||||
|
return "FFmpeg未安装";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测音频文件格式
|
||||||
|
*/
|
||||||
|
public static String detectAudioFormat(String filePath) {
|
||||||
|
try {
|
||||||
|
String ffmpegPath = findFFmpegPath();
|
||||||
|
if (ffmpegPath == null) {
|
||||||
|
return "未知";
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(
|
||||||
|
ffmpegPath,
|
||||||
|
"-i", filePath,
|
||||||
|
"-hide_banner"
|
||||||
|
);
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
Process process = pb.start();
|
||||||
|
|
||||||
|
java.io.BufferedReader reader = new java.io.BufferedReader(
|
||||||
|
new java.io.InputStreamReader(process.getInputStream())
|
||||||
|
);
|
||||||
|
StringBuilder output = new StringBuilder();
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
output.append(line).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
process.waitFor();
|
||||||
|
|
||||||
|
String outputStr = output.toString();
|
||||||
|
if (outputStr.contains("Audio: mp3")) {
|
||||||
|
return "MP3";
|
||||||
|
} else if (outputStr.contains("Audio: opus")) {
|
||||||
|
return "Opus";
|
||||||
|
} else if (outputStr.contains("Audio: aac")) {
|
||||||
|
return "AAC";
|
||||||
|
} else if (outputStr.contains("Audio: wav")) {
|
||||||
|
return "WAV";
|
||||||
|
} else {
|
||||||
|
return "未知格式";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("检测音频格式失败: {}", filePath, e);
|
||||||
|
return "检测失败";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
|
@ -0,0 +1,303 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
|
||||||
|
<el-form-item label="订单号" prop="orderId">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.orderId"
|
||||||
|
placeholder="请输入订单号"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="金额" prop="price">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.price"
|
||||||
|
placeholder="请输入金额"
|
||||||
|
clearable
|
||||||
|
@keyup.enter.native="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<!-- 操作按钮 -->
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="3">
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
icon="el-icon-edit"
|
||||||
|
size="medium"
|
||||||
|
style="font-weight:bold;font-size:16px;"
|
||||||
|
@click="showMarginChangeDialog"
|
||||||
|
>质保金变动</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="15"></el-col>
|
||||||
|
<el-col :span="3" style="text-align:right;">
|
||||||
|
<el-button @click="$emit('close')">关闭</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-table v-loading="loading" :data="workerMarginLogList" @selection-change="handleSelectionChange" element-loading-text="正在加载师傅质保金明细数据...">
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column label="ID" align="center" prop="id" width="80" />
|
||||||
|
<el-table-column label="订单号" align="center" prop="orderId" min-width="120" />
|
||||||
|
<el-table-column label="金额" align="center" prop="price" width="120">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span style="color: #E6A23C; font-weight: bold; font-size: 14px;">
|
||||||
|
¥{{ formatMoney(scope.row.price) }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="原因" align="center" prop="reamk" min-width="120" />
|
||||||
|
<el-table-column label="创建时间" align="center" prop="createdAt" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createdAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="更新时间" align="center" prop="updatedAt" width="180">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<span>{{ parseTime(scope.row.updatedAt, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<!-- 分页 -->
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:total="total"
|
||||||
|
:page.sync="queryParams.pageNum"
|
||||||
|
:limit.sync="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 空数据提示 -->
|
||||||
|
<div v-if="!loading && workerMarginLogList.length === 0" style="text-align: center; padding: 40px; color: #909399;">
|
||||||
|
<i class="el-icon-warning" style="font-size: 48px; margin-bottom: 16px;"></i>
|
||||||
|
<p>暂无师傅质保金明细数据</p>
|
||||||
|
<p v-if="queryParams.uid">师傅ID: {{ queryParams.uid }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 质保金变动弹窗 -->
|
||||||
|
<el-dialog :title="'质保金变动'" :visible.sync="marginChangeDialogVisible" width="500px" append-to-body>
|
||||||
|
<el-form :model="marginChangeForm" :rules="marginChangeRules" ref="marginChangeForm" label-width="100px">
|
||||||
|
<el-form-item label="变动金额" prop="price">
|
||||||
|
<el-input
|
||||||
|
v-model="marginChangeForm.price"
|
||||||
|
placeholder="请输入变动金额(正数为增加,负数为减少)"
|
||||||
|
type="number"
|
||||||
|
step="0.01"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="变动原因" prop="reamk">
|
||||||
|
<el-input
|
||||||
|
v-model="marginChangeForm.reamk"
|
||||||
|
type="textarea"
|
||||||
|
:rows="3"
|
||||||
|
placeholder="请输入变动原因"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<div slot="footer" class="dialog-footer">
|
||||||
|
<el-button @click="marginChangeDialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="submitMarginChange" :loading="marginChangeLoading">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { listWorkerMarginLog, addWorkerMarginLog } from '@/api/system/WorkerMarginLog'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'WorkerMarginLogDetailTable',
|
||||||
|
props: {
|
||||||
|
workerId: {
|
||||||
|
type: [String, Number],
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: false,
|
||||||
|
total: 0,
|
||||||
|
workerMarginLogList: [],
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
uid: null,
|
||||||
|
orderId: null,
|
||||||
|
price: null
|
||||||
|
},
|
||||||
|
ids: [],
|
||||||
|
single: true,
|
||||||
|
multiple: true,
|
||||||
|
// 质保金变动相关
|
||||||
|
marginChangeDialogVisible: false,
|
||||||
|
marginChangeLoading: false,
|
||||||
|
marginChangeForm: {
|
||||||
|
price: null,
|
||||||
|
reamk: ''
|
||||||
|
},
|
||||||
|
marginChangeRules: {
|
||||||
|
|
||||||
|
reamk: [
|
||||||
|
{ required: true, message: '请输入变动原因', trigger: 'blur' },
|
||||||
|
{ min: 1, max: 200, message: '变动原因长度在 1 到 200 个字符', trigger: 'blur' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
workerId: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
console.log('WorkerMarginLogDetailTable - workerId changed:', newVal);
|
||||||
|
if (newVal) {
|
||||||
|
this.queryParams.uid = newVal;
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
console.log('WorkerMarginLogDetailTable - component mounted');
|
||||||
|
console.log('组件挂载成功,workerId:', this.workerId);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getList() {
|
||||||
|
console.log('开始加载师傅质保金明细数据,workerId:', this.queryParams.uid);
|
||||||
|
console.log('查询参数:', this.queryParams);
|
||||||
|
if (!this.queryParams.uid) {
|
||||||
|
console.warn('workerId为空,无法加载数据');
|
||||||
|
this.$message.warning('师傅ID不能为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.loading = true;
|
||||||
|
|
||||||
|
listWorkerMarginLog(this.queryParams).then(response => {
|
||||||
|
console.log('师傅质保金明细数据加载成功:', response);
|
||||||
|
this.workerMarginLogList = response.rows || [];
|
||||||
|
this.total = response.total || 0;
|
||||||
|
this.loading = false;
|
||||||
|
console.log('设置数据:', this.workerMarginLogList);
|
||||||
|
console.log('总数:', this.total);
|
||||||
|
|
||||||
|
if (this.workerMarginLogList.length === 0) {
|
||||||
|
console.log('没有找到质保金明细数据');
|
||||||
|
this.$message.info('该师傅暂无质保金明细记录');
|
||||||
|
}
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('师傅质保金明细数据加载失败:', error);
|
||||||
|
console.error('错误详情:', error.response);
|
||||||
|
this.loading = false;
|
||||||
|
|
||||||
|
if (error.response && error.response.status === 403) {
|
||||||
|
this.$message.error('没有权限查看师傅质保金明细');
|
||||||
|
} else if (error.response && error.response.status === 404) {
|
||||||
|
this.$message.error('接口不存在,请检查后端服务');
|
||||||
|
} else {
|
||||||
|
this.$message.error('数据加载失败: ' + (error.message || '未知错误'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
formatMoney(money) {
|
||||||
|
if (!money) return '0.00';
|
||||||
|
return parseFloat(money).toFixed(2);
|
||||||
|
},
|
||||||
|
handleQuery() {
|
||||||
|
this.queryParams.pageNum = 1;
|
||||||
|
this.getList();
|
||||||
|
},
|
||||||
|
resetQuery() {
|
||||||
|
this.queryParams.orderId = null;
|
||||||
|
this.queryParams.price = null;
|
||||||
|
this.handleQuery();
|
||||||
|
},
|
||||||
|
parseTime(time, pattern) {
|
||||||
|
if (!time) return '';
|
||||||
|
const date = new Date(time);
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const m = ('0' + (date.getMonth() + 1)).slice(-2);
|
||||||
|
const d = ('0' + date.getDate()).slice(-2);
|
||||||
|
const h = ('0' + date.getHours()).slice(-2);
|
||||||
|
const i = ('0' + date.getMinutes()).slice(-2);
|
||||||
|
const s = ('0' + date.getSeconds()).slice(-2);
|
||||||
|
|
||||||
|
if (pattern === '{y}-{m}-{d} {h}:{i}:{s}') {
|
||||||
|
return `${y}-${m}-${d} ${h}:${i}:${s}`;
|
||||||
|
} else if (pattern === '{y}-{m}-{d}') {
|
||||||
|
return `${y}-${m}-${d}`;
|
||||||
|
}
|
||||||
|
return `${y}-${m}-${d}`;
|
||||||
|
},
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.ids = selection.map(item => item.id)
|
||||||
|
this.single = selection.length!==1
|
||||||
|
this.multiple = !selection.length
|
||||||
|
},
|
||||||
|
// 显示质保金变动弹窗
|
||||||
|
showMarginChangeDialog() {
|
||||||
|
this.marginChangeForm = {
|
||||||
|
price: null,
|
||||||
|
reamk: ''
|
||||||
|
};
|
||||||
|
this.marginChangeDialogVisible = true;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.marginChangeForm && this.$refs.marginChangeForm.clearValidate();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
// 提交质保金变动
|
||||||
|
submitMarginChange() {
|
||||||
|
this.$refs.marginChangeForm.validate((valid) => {
|
||||||
|
if (valid) {
|
||||||
|
this.marginChangeLoading = true;
|
||||||
|
|
||||||
|
const data = {
|
||||||
|
uid: this.queryParams.uid,
|
||||||
|
price: 0-parseFloat(this.marginChangeForm.price),
|
||||||
|
reamk: this.marginChangeForm.reamk
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('提交质保金变动数据:', data);
|
||||||
|
|
||||||
|
addWorkerMarginLog(data).then(response => {
|
||||||
|
this.$message.success('质保金变动成功');
|
||||||
|
this.marginChangeDialogVisible = false;
|
||||||
|
this.marginChangeLoading = false;
|
||||||
|
|
||||||
|
// 刷新列表
|
||||||
|
this.getList();
|
||||||
|
|
||||||
|
// 通知父组件更新师傅信息
|
||||||
|
this.$emit('margin-changed');
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('质保金变动失败:', error);
|
||||||
|
this.marginChangeLoading = false;
|
||||||
|
|
||||||
|
if (error.response && error.response.status === 403) {
|
||||||
|
this.$message.error('没有权限进行质保金变动');
|
||||||
|
} else {
|
||||||
|
this.$message.error('质保金变动失败: ' + (error.message || '未知错误'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.el-table .el-table__body-wrapper {
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue