项目重构: 整理目录结构, 更新前后端代码, 添加测试和数据库迁移
This commit is contained in:
@@ -40,6 +40,38 @@ public class PaymentService {
|
||||
@Transactional(readOnly = true) public long countByStatus(PaymentStatus status) { return paymentRepository.countByStatus(status); }
|
||||
@Transactional(readOnly = true) public long countByUserIdAndStatus(Long userId, PaymentStatus status) { return paymentRepository.countByUserIdAndStatus(userId, status); }
|
||||
|
||||
/**
|
||||
* 更新支付方式和描述(带事务,解决懒加载问题)
|
||||
*/
|
||||
@Transactional
|
||||
public Payment updatePaymentMethod(Long paymentId, String method, String description, String username) {
|
||||
// 使用findByIdWithUserAndOrder一次性加载User和Order,避免懒加载异常
|
||||
Payment payment = paymentRepository.findByIdWithUserAndOrder(paymentId)
|
||||
.orElseThrow(() -> new RuntimeException("支付记录不存在"));
|
||||
|
||||
// 检查权限
|
||||
if (!payment.getUser().getUsername().equals(username)) {
|
||||
throw new RuntimeException("无权限修改此支付记录");
|
||||
}
|
||||
|
||||
// 只有PENDING状态的支付才能修改支付方式
|
||||
if (payment.getStatus() != PaymentStatus.PENDING) {
|
||||
throw new RuntimeException("只有待支付状态的订单才能修改支付方式");
|
||||
}
|
||||
|
||||
payment.setPaymentMethod(PaymentMethod.valueOf(method));
|
||||
if (description != null && !description.isEmpty()) {
|
||||
payment.setDescription(description);
|
||||
// 同时更新关联订单的描述
|
||||
if (payment.getOrder() != null) {
|
||||
payment.getOrder().setDescription(description);
|
||||
// 由于在同一事务中,Order会自动保存
|
||||
}
|
||||
}
|
||||
|
||||
return paymentRepository.save(payment);
|
||||
}
|
||||
|
||||
public Payment updatePaymentStatus(Long paymentId, PaymentStatus newStatus) {
|
||||
Payment payment = paymentRepository.findById(paymentId).orElseThrow(() -> new RuntimeException("Not found"));
|
||||
payment.setStatus(newStatus);
|
||||
@@ -48,7 +80,9 @@ public class PaymentService {
|
||||
}
|
||||
|
||||
public Payment confirmPaymentSuccess(Long paymentId, String externalTransactionId) {
|
||||
Payment payment = paymentRepository.findById(paymentId).orElseThrow(() -> new RuntimeException("Not found"));
|
||||
// 使用findByIdWithUserAndOrder确保加载User和Order,避免懒加载问题
|
||||
Payment payment = paymentRepository.findByIdWithUserAndOrder(paymentId)
|
||||
.orElseThrow(() -> new RuntimeException("Not found"));
|
||||
|
||||
// 检查是否已经处理过(防止重复增加积分和重复创建订单)
|
||||
if (payment.getStatus() == PaymentStatus.SUCCESS) {
|
||||
@@ -62,19 +96,14 @@ public class PaymentService {
|
||||
Payment savedPayment = paymentRepository.save(payment);
|
||||
|
||||
// 支付成功后更新订单状态为已支付
|
||||
// 注意:积分添加逻辑已移至 OrderService.handlePointsForStatusChange
|
||||
// 当订单状态变为 PAID 时会自动添加积分,避免重复添加
|
||||
try {
|
||||
updateOrderStatusForPayment(savedPayment);
|
||||
} catch (Exception e) {
|
||||
logger.error("支付成功但更新订单状态失败: paymentId={}, error={}", paymentId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
// 支付成功后增加用户积分
|
||||
try {
|
||||
addPointsForPayment(savedPayment);
|
||||
} catch (Exception e) {
|
||||
logger.error("支付成功但增加积分失败: paymentId={}, error={}", paymentId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return savedPayment;
|
||||
}
|
||||
|
||||
@@ -95,9 +124,8 @@ public class PaymentService {
|
||||
}
|
||||
|
||||
try {
|
||||
// 更新订单状态为已支付
|
||||
order.setStatus(OrderStatus.PAID);
|
||||
order.setPaidAt(LocalDateTime.now());
|
||||
// 直接调用orderService.updateOrderStatus,不要在这里修改order状态
|
||||
// orderService.updateOrderStatus会正确处理状态变更和积分添加
|
||||
orderService.updateOrderStatus(order.getId(), OrderStatus.PAID);
|
||||
|
||||
logger.info("✅ 订单状态更新为已支付: orderId={}, orderNumber={}, paymentId={}",
|
||||
@@ -207,19 +235,33 @@ public class PaymentService {
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public Payment createPayment(String username, String orderId, String amountStr, String method) {
|
||||
public Payment createPayment(String username, String orderId, String amountStr, String method, String description) {
|
||||
// 检查是否已存在相同 orderId 的支付记录
|
||||
Optional<Payment> existing = paymentRepository.findByOrderId(orderId);
|
||||
if (existing.isPresent()) {
|
||||
Payment existingPayment = existing.get();
|
||||
// 如果已存在且状态是 PENDING,直接返回
|
||||
// 如果已存在且状态是 PENDING,检查是否是同一用户且金额相同
|
||||
if (existingPayment.getStatus() == PaymentStatus.PENDING) {
|
||||
logger.info("复用已存在的PENDING支付记录: orderId={}, paymentId={}", orderId, existingPayment.getId());
|
||||
return existingPayment;
|
||||
// 验证用户和金额是否匹配
|
||||
if (existingPayment.getUser().getUsername().equals(username) &&
|
||||
existingPayment.getAmount().compareTo(new BigDecimal(amountStr)) == 0) {
|
||||
logger.info("复用已存在的PENDING支付记录: orderId={}, paymentId={}", orderId, existingPayment.getId());
|
||||
return existingPayment;
|
||||
} else {
|
||||
// 用户或金额不匹配,生成新的 orderId
|
||||
logger.warn("已存在相同orderId的PENDING支付,但用户或金额不匹配,生成新orderId: {}", orderId);
|
||||
orderId = orderId + "_" + System.currentTimeMillis();
|
||||
}
|
||||
} else if (existingPayment.getStatus() == PaymentStatus.SUCCESS) {
|
||||
// 如果已存在成功状态的支付,绝对不能复用,必须生成新的 orderId
|
||||
logger.warn("⚠️ 已存在相同orderId的成功支付记录,生成新orderId避免冲突: orderId={}, existingPaymentId={}, status={}",
|
||||
orderId, existingPayment.getId(), existingPayment.getStatus());
|
||||
orderId = orderId + "_" + System.currentTimeMillis();
|
||||
} else {
|
||||
// 如果是其他状态(FAILED、CANCELLED等),生成新的 orderId
|
||||
orderId = orderId + "_" + System.currentTimeMillis();
|
||||
logger.info("已存在相同orderId但状态为{},生成新orderId: {}", existingPayment.getStatus(), orderId);
|
||||
}
|
||||
// 如果是其他状态,生成新的 orderId
|
||||
orderId = orderId + "_" + System.currentTimeMillis();
|
||||
logger.info("已存在相同orderId但状态为{},生成新orderId: {}", existingPayment.getStatus(), orderId);
|
||||
}
|
||||
|
||||
User user = null;
|
||||
@@ -237,11 +279,24 @@ public class PaymentService {
|
||||
order.setStatus(OrderStatus.PENDING); // 待支付状态
|
||||
order.setOrderType(OrderType.SUBSCRIPTION);
|
||||
|
||||
// 根据金额设置订单描述
|
||||
if (amount.compareTo(new BigDecimal("259.00")) >= 0) {
|
||||
order.setDescription("专业版会员订阅 - " + amount + "元");
|
||||
} else if (amount.compareTo(new BigDecimal("59.00")) >= 0) {
|
||||
order.setDescription("标准版会员订阅 - " + amount + "元");
|
||||
// 使用前端传递的描述,如果没有则根据orderId中的套餐类型设置
|
||||
if (description != null && !description.isEmpty()) {
|
||||
order.setDescription(description);
|
||||
} else if (orderId != null && orderId.contains("_")) {
|
||||
// 从orderId中提取套餐类型(如 SUB_standard_xxx -> standard)
|
||||
String[] parts = orderId.split("_");
|
||||
if (parts.length >= 2) {
|
||||
String planType = parts[1];
|
||||
if ("standard".equalsIgnoreCase(planType)) {
|
||||
order.setDescription("标准版会员订阅 - " + amount + "元");
|
||||
} else if ("premium".equalsIgnoreCase(planType)) {
|
||||
order.setDescription("专业版会员订阅 - " + amount + "元");
|
||||
} else {
|
||||
order.setDescription("会员订阅 - " + amount + "元");
|
||||
}
|
||||
} else {
|
||||
order.setDescription("会员订阅 - " + amount + "元");
|
||||
}
|
||||
} else {
|
||||
order.setDescription("会员订阅 - " + amount + "元");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user