项目重构: 整理目录结构, 更新前后端代码, 添加测试和数据库迁移

This commit is contained in:
AIGC Developer
2025-12-30 10:24:19 +08:00
parent 5344148a1c
commit 38630dbb66
117 changed files with 1987 additions and 1316 deletions

View File

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