11.1 交易系统概述
线上/线下交易系统是SaaS平台的核心业务系统,负责处理各种交易场景下的订单创建、支付处理、交易确认等关键业务流程。该系统需要支持多渠道、多支付方式、多业务模式的复杂交易需求。
11.1.1 系统重要性
交易系统在SaaS平台中具有以下重要作用:
- 业务核心:交易系统是电商业务的核心,直接关系到企业的收入和用户体验
- 数据中心:汇聚了用户行为、商品销售、支付流水等关键业务数据
- 集成枢纽:连接用户系统、商品系统、库存系统、支付系统等多个子系统
- 风控中心:承担交易风险识别、防欺诈、资金安全等重要职责
11.1.2 核心价值
线上/线下交易系统为企业带来的核心价值包括:
-
统一交易体验
- 线上线下一体化的交易流程
- 统一的用户账户和积分体系
- 一致的商品信息和价格策略
-
多渠道支持
- 支持PC端、移动端、小程序等多种线上渠道
- 支持门店POS、自助终端等线下渠道
- 支持直播带货、社交电商等新兴渠道
-
灵活支付方式
- 支持微信支付、支付宝、银行卡等多种支付方式
- 支持分期付款、货到付款等特殊支付模式
- 支持企业对公支付和个人支付
-
智能风控
- 实时交易风险评估
- 异常交易自动拦截
- 多维度风险指标监控
11.1.3 业务收益指标
交易系统的关键业务指标包括:
java
/**
* 交易系统业务指标
*/
public class TransactionMetrics {
// 交易量指标
private BigDecimal dailyTransactionVolume; // 日交易额
private Long dailyTransactionCount; // 日交易笔数
private BigDecimal averageOrderValue; // 平均订单价值
// 成功率指标
private Double transactionSuccessRate; // 交易成功率
private Double paymentSuccessRate; // 支付成功率
private Double orderCompletionRate; // 订单完成率
// 性能指标
private Long averageResponseTime; // 平均响应时间
private Double systemAvailability; // 系统可用性
private Long peakTPS; // 峰值TPS
// 风控指标
private Double fraudDetectionRate; // 欺诈检测率
private Double falsePositiveRate; // 误报率
private BigDecimal riskTransactionAmount; // 风险交易金额
// 用户体验指标
private Double customerSatisfactionScore; // 客户满意度
private Double cartAbandonmentRate; // 购物车放弃率
private Double checkoutConversionRate; // 结算转化率
// 构造函数
public TransactionMetrics() {
this.dailyTransactionVolume = BigDecimal.ZERO;
this.dailyTransactionCount = 0L;
this.averageOrderValue = BigDecimal.ZERO;
this.transactionSuccessRate = 0.0;
this.paymentSuccessRate = 0.0;
this.orderCompletionRate = 0.0;
this.averageResponseTime = 0L;
this.systemAvailability = 0.0;
this.peakTPS = 0L;
this.fraudDetectionRate = 0.0;
this.falsePositiveRate = 0.0;
this.riskTransactionAmount = BigDecimal.ZERO;
this.customerSatisfactionScore = 0.0;
this.cartAbandonmentRate = 0.0;
this.checkoutConversionRate = 0.0;
}
/**
* 计算交易转化率
*/
public Double calculateConversionRate(Long totalVisitors) {
if (totalVisitors == null || totalVisitors == 0) {
return 0.0;
}
return (dailyTransactionCount.doubleValue() / totalVisitors) * 100;
}
/**
* 计算平均订单价值
*/
public BigDecimal calculateAverageOrderValue() {
if (dailyTransactionCount == 0) {
return BigDecimal.ZERO;
}
return dailyTransactionVolume.divide(
BigDecimal.valueOf(dailyTransactionCount),
2,
RoundingMode.HALF_UP
);
}
/**
* 评估系统健康度
*/
public SystemHealthLevel evaluateSystemHealth() {
double healthScore = 0.0;
// 成功率权重 40%
healthScore += (transactionSuccessRate + paymentSuccessRate + orderCompletionRate) / 3 * 0.4;
// 性能权重 30%
double performanceScore = Math.min(systemAvailability, 100.0);
if (averageResponseTime < 1000) {
performanceScore += 10; // 响应时间奖励
}
healthScore += Math.min(performanceScore, 100.0) * 0.3;
// 风控权重 20%
double riskScore = 100.0 - falsePositiveRate;
if (fraudDetectionRate > 95.0) {
riskScore += 5; // 风控效果奖励
}
healthScore += Math.min(riskScore, 100.0) * 0.2;
// 用户体验权重 10%
healthScore += customerSatisfactionScore * 0.1;
if (healthScore >= 90) {
return SystemHealthLevel.EXCELLENT;
} else if (healthScore >= 80) {
return SystemHealthLevel.GOOD;
} else if (healthScore >= 70) {
return SystemHealthLevel.FAIR;
} else if (healthScore >= 60) {
return SystemHealthLevel.POOR;
} else {
return SystemHealthLevel.CRITICAL;
}
}
// Getters and Setters
public BigDecimal getDailyTransactionVolume() { return dailyTransactionVolume; }
public void setDailyTransactionVolume(BigDecimal dailyTransactionVolume) { this.dailyTransactionVolume = dailyTransactionVolume; }
public Long getDailyTransactionCount() { return dailyTransactionCount; }
public void setDailyTransactionCount(Long dailyTransactionCount) { this.dailyTransactionCount = dailyTransactionCount; }
public BigDecimal getAverageOrderValue() { return averageOrderValue; }
public void setAverageOrderValue(BigDecimal averageOrderValue) { this.averageOrderValue = averageOrderValue; }
public Double getTransactionSuccessRate() { return transactionSuccessRate; }
public void setTransactionSuccessRate(Double transactionSuccessRate) { this.transactionSuccessRate = transactionSuccessRate; }
public Double getPaymentSuccessRate() { return paymentSuccessRate; }
public void setPaymentSuccessRate(Double paymentSuccessRate) { this.paymentSuccessRate = paymentSuccessRate; }
public Double getOrderCompletionRate() { return orderCompletionRate; }
public void setOrderCompletionRate(Double orderCompletionRate) { this.orderCompletionRate = orderCompletionRate; }
public Long getAverageResponseTime() { return averageResponseTime; }
public void setAverageResponseTime(Long averageResponseTime) { this.averageResponseTime = averageResponseTime; }
public Double getSystemAvailability() { return systemAvailability; }
public void setSystemAvailability(Double systemAvailability) { this.systemAvailability = systemAvailability; }
public Long getPeakTPS() { return peakTPS; }
public void setPeakTPS(Long peakTPS) { this.peakTPS = peakTPS; }
public Double getFraudDetectionRate() { return fraudDetectionRate; }
public void setFraudDetectionRate(Double fraudDetectionRate) { this.fraudDetectionRate = fraudDetectionRate; }
public Double getFalsePositiveRate() { return falsePositiveRate; }
public void setFalsePositiveRate(Double falsePositiveRate) { this.falsePositiveRate = falsePositiveRate; }
public BigDecimal getRiskTransactionAmount() { return riskTransactionAmount; }
public void setRiskTransactionAmount(BigDecimal riskTransactionAmount) { this.riskTransactionAmount = riskTransactionAmount; }
public Double getCustomerSatisfactionScore() { return customerSatisfactionScore; }
public void setCustomerSatisfactionScore(Double customerSatisfactionScore) { this.customerSatisfactionScore = customerSatisfactionScore; }
public Double getCartAbandonmentRate() { return cartAbandonmentRate; }
public void setCartAbandonmentRate(Double cartAbandonmentRate) { this.cartAbandonmentRate = cartAbandonmentRate; }
public Double getCheckoutConversionRate() { return checkoutConversionRate; }
public void setCheckoutConversionRate(Double checkoutConversionRate) { this.checkoutConversionRate = checkoutConversionRate; }
}
/**
* 系统健康度等级
*/
public enum SystemHealthLevel {
EXCELLENT("优秀", "系统运行状态优秀,各项指标表现良好"),
GOOD("良好", "系统运行状态良好,部分指标有优化空间"),
FAIR("一般", "系统运行状态一般,需要关注关键指标"),
POOR("较差", "系统运行状态较差,需要及时优化"),
CRITICAL("严重", "系统运行状态严重,需要立即处理");
private final String description;
private final String recommendation;
SystemHealthLevel(String description, String recommendation) {
this.description = description;
this.recommendation = recommendation;
}
public String getDescription() {
return description;
}
public String getRecommendation() {
return recommendation;
}
}
11.2 系统架构设计
11.2.1 整体架构
线上/线下交易系统采用微服务架构,主要包括以下核心服务:
外部系统 数据层 基础服务层 业务服务层 网关层 客户端层 第三方支付 银行系统 物流系统 短信服务 交易数据库 支付数据库 订单数据库 Redis缓存 消息队列 用户服务 商品服务 库存服务 优惠服务 物流服务 交易服务 支付服务 订单服务 风控服务 通知服务 API网关 负载均衡 Web端 移动端 小程序 POS终端 自助设备
11.2.2 核心设计原则
交易系统的设计遵循以下核心原则:
-
高可用性原则
- 系统可用性要求达到99.9%以上
- 支持多活部署和故障自动切换
- 关键路径冗余设计
-
数据一致性原则
- 交易数据强一致性保证
- 分布式事务管理
- 最终一致性补偿机制
-
安全性原则
- 端到端数据加密
- 多层次身份认证
- 实时风险监控
-
可扩展性原则
- 水平扩展能力
- 微服务解耦设计
- 弹性伸缩支持
-
性能优化原则
- 缓存优先策略
- 异步处理机制
- 数据库读写分离
11.2.3 架构质量属性
java
/**
* 交易系统架构质量属性
*/
public class TransactionArchitectureQuality {
// 可用性要求
private Double availabilityTarget = 99.9; // 目标可用性
private Long maxDowntimePerMonth = 43200L; // 每月最大停机时间(秒)
private Integer recoveryTimeObjective = 300; // 恢复时间目标(秒)
// 性能要求
private Long maxResponseTime = 3000L; // 最大响应时间(毫秒)
private Long averageResponseTime = 1000L; // 平均响应时间(毫秒)
private Integer maxConcurrentUsers = 100000; // 最大并发用户数
private Integer peakTPS = 10000; // 峰值TPS
// 可扩展性要求
private Double horizontalScalability = 10.0; // 水平扩展倍数
private Integer maxServiceInstances = 100; // 最大服务实例数
private Double elasticScalingThreshold = 80.0; // 弹性伸缩阈值
// 安全性要求
private String encryptionStandard = "AES-256"; // 加密标准
private Integer authenticationLevels = 3; // 认证层级
private Double fraudDetectionAccuracy = 95.0; // 欺诈检测准确率
// 一致性要求
private String consistencyLevel = "STRONG"; // 一致性级别
private Long maxEventualConsistencyDelay = 5000L; // 最终一致性最大延迟(毫秒)
private Double dataIntegrityTarget = 99.99; // 数据完整性目标
/**
* 评估架构质量
*/
public ArchitectureQualityAssessment assessQuality(
Double currentAvailability,
Long currentResponseTime,
Integer currentTPS,
Double currentSecurityScore) {
ArchitectureQualityAssessment assessment = new ArchitectureQualityAssessment();
// 可用性评估
assessment.setAvailabilityScore(calculateAvailabilityScore(currentAvailability));
// 性能评估
assessment.setPerformanceScore(calculatePerformanceScore(currentResponseTime, currentTPS));
// 安全性评估
assessment.setSecurityScore(currentSecurityScore);
// 综合评分
double overallScore = (assessment.getAvailabilityScore() * 0.3 +
assessment.getPerformanceScore() * 0.3 +
assessment.getSecurityScore() * 0.4);
assessment.setOverallScore(overallScore);
// 评估等级
assessment.setQualityLevel(determineQualityLevel(overallScore));
return assessment;
}
private Double calculateAvailabilityScore(Double currentAvailability) {
if (currentAvailability >= availabilityTarget) {
return 100.0;
}
return (currentAvailability / availabilityTarget) * 100;
}
private Double calculatePerformanceScore(Long currentResponseTime, Integer currentTPS) {
double responseScore = Math.max(0, 100 - (currentResponseTime - averageResponseTime) / 10.0);
double tpsScore = Math.min(100, (currentTPS.doubleValue() / peakTPS) * 100);
return (responseScore + tpsScore) / 2;
}
private QualityLevel determineQualityLevel(Double score) {
if (score >= 90) {
return QualityLevel.EXCELLENT;
} else if (score >= 80) {
return QualityLevel.GOOD;
} else if (score >= 70) {
return QualityLevel.ACCEPTABLE;
} else {
return QualityLevel.NEEDS_IMPROVEMENT;
}
}
// Getters and Setters
public Double getAvailabilityTarget() { return availabilityTarget; }
public void setAvailabilityTarget(Double availabilityTarget) { this.availabilityTarget = availabilityTarget; }
public Long getMaxDowntimePerMonth() { return maxDowntimePerMonth; }
public void setMaxDowntimePerMonth(Long maxDowntimePerMonth) { this.maxDowntimePerMonth = maxDowntimePerMonth; }
public Integer getRecoveryTimeObjective() { return recoveryTimeObjective; }
public void setRecoveryTimeObjective(Integer recoveryTimeObjective) { this.recoveryTimeObjective = recoveryTimeObjective; }
public Long getMaxResponseTime() { return maxResponseTime; }
public void setMaxResponseTime(Long maxResponseTime) { this.maxResponseTime = maxResponseTime; }
public Long getAverageResponseTime() { return averageResponseTime; }
public void setAverageResponseTime(Long averageResponseTime) { this.averageResponseTime = averageResponseTime; }
public Integer getMaxConcurrentUsers() { return maxConcurrentUsers; }
public void setMaxConcurrentUsers(Integer maxConcurrentUsers) { this.maxConcurrentUsers = maxConcurrentUsers; }
public Integer getPeakTPS() { return peakTPS; }
public void setPeakTPS(Integer peakTPS) { this.peakTPS = peakTPS; }
public Double getHorizontalScalability() { return horizontalScalability; }
public void setHorizontalScalability(Double horizontalScalability) { this.horizontalScalability = horizontalScalability; }
public Integer getMaxServiceInstances() { return maxServiceInstances; }
public void setMaxServiceInstances(Integer maxServiceInstances) { this.maxServiceInstances = maxServiceInstances; }
public Double getElasticScalingThreshold() { return elasticScalingThreshold; }
public void setElasticScalingThreshold(Double elasticScalingThreshold) { this.elasticScalingThreshold = elasticScalingThreshold; }
public String getEncryptionStandard() { return encryptionStandard; }
public void setEncryptionStandard(String encryptionStandard) { this.encryptionStandard = encryptionStandard; }
public Integer getAuthenticationLevels() { return authenticationLevels; }
public void setAuthenticationLevels(Integer authenticationLevels) { this.authenticationLevels = authenticationLevels; }
public Double getFraudDetectionAccuracy() { return fraudDetectionAccuracy; }
public void setFraudDetectionAccuracy(Double fraudDetectionAccuracy) { this.fraudDetectionAccuracy = fraudDetectionAccuracy; }
public String getConsistencyLevel() { return consistencyLevel; }
public void setConsistencyLevel(String consistencyLevel) { this.consistencyLevel = consistencyLevel; }
public Long getMaxEventualConsistencyDelay() { return maxEventualConsistencyDelay; }
public void setMaxEventualConsistencyDelay(Long maxEventualConsistencyDelay) { this.maxEventualConsistencyDelay = maxEventualConsistencyDelay; }
public Double getDataIntegrityTarget() { return dataIntegrityTarget; }
public void setDataIntegrityTarget(Double dataIntegrityTarget) { this.dataIntegrityTarget = dataIntegrityTarget; }
}
/**
* 架构质量评估结果
*/
public class ArchitectureQualityAssessment {
private Double availabilityScore;
private Double performanceScore;
private Double securityScore;
private Double overallScore;
private QualityLevel qualityLevel;
private List<String> recommendations;
public ArchitectureQualityAssessment() {
this.recommendations = new ArrayList<>();
}
// Getters and Setters
public Double getAvailabilityScore() { return availabilityScore; }
public void setAvailabilityScore(Double availabilityScore) { this.availabilityScore = availabilityScore; }
public Double getPerformanceScore() { return performanceScore; }
public void setPerformanceScore(Double performanceScore) { this.performanceScore = performanceScore; }
public Double getSecurityScore() { return securityScore; }
public void setSecurityScore(Double securityScore) { this.securityScore = securityScore; }
public Double getOverallScore() { return overallScore; }
public void setOverallScore(Double overallScore) { this.overallScore = overallScore; }
public QualityLevel getQualityLevel() { return qualityLevel; }
public void setQualityLevel(QualityLevel qualityLevel) { this.qualityLevel = qualityLevel; }
public List<String> getRecommendations() { return recommendations; }
public void setRecommendations(List<String> recommendations) { this.recommendations = recommendations; }
}
/**
* 质量等级
*/
public enum QualityLevel {
EXCELLENT("优秀"),
GOOD("良好"),
ACCEPTABLE("可接受"),
NEEDS_IMPROVEMENT("需要改进");
private final String description;
QualityLevel(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
11.3 交易流程设计
11.3.1 标准交易流程
标准的线上/线下交易流程包括以下关键步骤:
客户端 API网关 交易服务 订单服务 库存服务 支付服务 风控服务 通知服务 1. 发起交易请求 2. 路由到交易服务 3. 风险评估 4. 风险评估结果 5. 检查库存 6. 库存检查结果 7. 预留库存 8. 库存预留成功 9. 创建订单 10. 订单创建成功 11. 发起支付 12. 支付结果 13. 确认库存扣减 14. 更新订单状态 15. 发送通知 16. 交易成功 17. 释放预留库存 18. 取消订单 19. 交易失败 alt [支付成功] [支付失败] 20. 库存不足 alt [库存充足] [库存不足] 21. 交易被拒绝 alt [风险评估通过] [风险评估不通过] 客户端 API网关 交易服务 订单服务 库存服务 支付服务 风控服务 通知服务
11.3.2 交易状态机
交易系统采用状态机模式管理交易生命周期:
java
/**
* 交易状态枚举
*/
public enum TransactionStatus {
INITIATED("已发起", "交易已发起,等待处理"),
RISK_CHECKING("风控检查中", "正在进行风险评估"),
RISK_APPROVED("风控通过", "风险评估通过"),
RISK_REJECTED("风控拒绝", "风险评估不通过,交易被拒绝"),
INVENTORY_CHECKING("库存检查中", "正在检查商品库存"),
INVENTORY_RESERVED("库存已预留", "商品库存已预留"),
INVENTORY_INSUFFICIENT("库存不足", "商品库存不足"),
ORDER_CREATING("订单创建中", "正在创建订单"),
ORDER_CREATED("订单已创建", "订单创建成功"),
PAYMENT_PROCESSING("支付处理中", "正在处理支付"),
PAYMENT_SUCCESS("支付成功", "支付处理成功"),
PAYMENT_FAILED("支付失败", "支付处理失败"),
COMPLETED("交易完成", "交易成功完成"),
CANCELLED("交易取消", "交易已取消"),
FAILED("交易失败", "交易处理失败"),
TIMEOUT("交易超时", "交易处理超时");
private final String description;
private final String message;
TransactionStatus(String description, String message) {
this.description = description;
this.message = message;
}
public String getDescription() {
return description;
}
public String getMessage() {
return message;
}
/**
* 判断是否为终态
*/
public boolean isFinalStatus() {
return this == COMPLETED || this == CANCELLED ||
this == FAILED || this == TIMEOUT ||
this == RISK_REJECTED || this == INVENTORY_INSUFFICIENT;
}
/**
* 判断是否为成功状态
*/
public boolean isSuccessStatus() {
return this == COMPLETED;
}
/**
* 获取允许的下一状态
*/
public Set<TransactionStatus> getAllowedNextStatuses() {
switch (this) {
case INITIATED:
return Set.of(RISK_CHECKING, CANCELLED);
case RISK_CHECKING:
return Set.of(RISK_APPROVED, RISK_REJECTED, TIMEOUT);
case RISK_APPROVED:
return Set.of(INVENTORY_CHECKING, CANCELLED);
case INVENTORY_CHECKING:
return Set.of(INVENTORY_RESERVED, INVENTORY_INSUFFICIENT, TIMEOUT);
case INVENTORY_RESERVED:
return Set.of(ORDER_CREATING, CANCELLED);
case ORDER_CREATING:
return Set.of(ORDER_CREATED, FAILED, TIMEOUT);
case ORDER_CREATED:
return Set.of(PAYMENT_PROCESSING, CANCELLED);
case PAYMENT_PROCESSING:
return Set.of(PAYMENT_SUCCESS, PAYMENT_FAILED, TIMEOUT);
case PAYMENT_SUCCESS:
return Set.of(COMPLETED);
case PAYMENT_FAILED:
return Set.of(FAILED, CANCELLED);
default:
return Set.of(); // 终态不允许转换
}
}
}
/**
* 交易状态机
*/
@Component
public class TransactionStateMachine {
private static final Logger logger = LoggerFactory.getLogger(TransactionStateMachine.class);
/**
* 状态转换
*/
public boolean transitionTo(Transaction transaction, TransactionStatus targetStatus, String reason) {
TransactionStatus currentStatus = transaction.getStatus();
// 检查状态转换是否合法
if (!isValidTransition(currentStatus, targetStatus)) {
logger.warn("Invalid status transition from {} to {} for transaction {}",
currentStatus, targetStatus, transaction.getTransactionId());
return false;
}
// 执行状态转换前的操作
if (!executePreTransitionActions(transaction, currentStatus, targetStatus)) {
logger.error("Pre-transition actions failed for transaction {}",
transaction.getTransactionId());
return false;
}
// 更新状态
TransactionStatus previousStatus = transaction.getStatus();
transaction.setStatus(targetStatus);
transaction.setLastUpdated(LocalDateTime.now());
// 记录状态变更历史
TransactionStatusHistory history = new TransactionStatusHistory(
transaction.getTransactionId(),
previousStatus,
targetStatus,
reason,
LocalDateTime.now()
);
transaction.addStatusHistory(history);
// 执行状态转换后的操作
executePostTransitionActions(transaction, previousStatus, targetStatus);
logger.info("Transaction {} status changed from {} to {}, reason: {}",
transaction.getTransactionId(), previousStatus, targetStatus, reason);
return true;
}
/**
* 检查状态转换是否合法
*/
private boolean isValidTransition(TransactionStatus from, TransactionStatus to) {
if (from == null || to == null) {
return false;
}
return from.getAllowedNextStatuses().contains(to);
}
/**
* 执行状态转换前的操作
*/
private boolean executePreTransitionActions(Transaction transaction,
TransactionStatus from,
TransactionStatus to) {
try {
switch (to) {
case RISK_CHECKING:
return validateTransactionData(transaction);
case INVENTORY_CHECKING:
return validateRiskApproval(transaction);
case ORDER_CREATING:
return validateInventoryReservation(transaction);
case PAYMENT_PROCESSING:
return validateOrderCreation(transaction);
case COMPLETED:
return validatePaymentSuccess(transaction);
default:
return true;
}
} catch (Exception e) {
logger.error("Error executing pre-transition actions", e);
return false;
}
}
/**
* 执行状态转换后的操作
*/
private void executePostTransitionActions(Transaction transaction,
TransactionStatus from,
TransactionStatus to) {
try {
switch (to) {
case RISK_CHECKING:
// 触发风控检查
triggerRiskAssessment(transaction);
break;
case INVENTORY_CHECKING:
// 触发库存检查
triggerInventoryCheck(transaction);
break;
case ORDER_CREATING:
// 触发订单创建
triggerOrderCreation(transaction);
break;
case PAYMENT_PROCESSING:
// 触发支付处理
triggerPaymentProcessing(transaction);
break;
case COMPLETED:
// 发送完成通知
sendCompletionNotification(transaction);
break;
case CANCELLED:
case FAILED:
case TIMEOUT:
// 执行清理操作
executeCleanupActions(transaction);
break;
}
} catch (Exception e) {
logger.error("Error executing post-transition actions", e);
}
}
11.5 支付系统集成
11.5.1 支付服务架构
支付系统是交易系统的核心组件,需要支持多种支付方式和支付渠道:
java
/**
* 支付服务接口
*/
public interface PaymentService {
/**
* 处理支付
*/
PaymentResult processPayment(Transaction transaction);
/**
* 查询支付状态
*/
PaymentStatus queryPaymentStatus(String paymentId);
/**
* 退款
*/
RefundResult processRefund(RefundRequest request);
/**
* 支付回调处理
*/
void handlePaymentCallback(PaymentCallbackRequest request);
/**
* 获取支持的支付方式
*/
List<PaymentMethod> getSupportedPaymentMethods(String channelType);
}
/**
* 支付服务实现
*/
@Service
public class PaymentServiceImpl implements PaymentService {
private static final Logger logger = LoggerFactory.getLogger(PaymentServiceImpl.class);
private final Map<String, PaymentProcessor> paymentProcessors;
private final PaymentRepository paymentRepository;
private final PaymentConfigService paymentConfigService;
private final PaymentEventPublisher eventPublisher;
private final RedisTemplate<String, Object> redisTemplate;
public PaymentServiceImpl(List<PaymentProcessor> processors,
PaymentRepository paymentRepository,
PaymentConfigService paymentConfigService,
PaymentEventPublisher eventPublisher,
RedisTemplate<String, Object> redisTemplate) {
this.paymentProcessors = processors.stream()
.collect(Collectors.toMap(
PaymentProcessor::getPaymentMethod,
Function.identity()
));
this.paymentRepository = paymentRepository;
this.paymentConfigService = paymentConfigService;
this.eventPublisher = eventPublisher;
this.redisTemplate = redisTemplate;
}
@Override
public PaymentResult processPayment(Transaction transaction) {
try {
// 创建支付记录
Payment payment = createPayment(transaction);
// 选择支付处理器
PaymentProcessor processor = selectPaymentProcessor(payment);
if (processor == null) {
return PaymentResult.failed("不支持的支付方式");
}
// 执行支付
PaymentResult result = processor.processPayment(payment);
// 更新支付记录
updatePaymentRecord(payment, result);
// 发布支付事件
if (result.isSuccess()) {
eventPublisher.publishPaymentSuccess(payment);
} else {
eventPublisher.publishPaymentFailed(payment, result.getErrorMessage());
}
return result;
} catch (Exception e) {
logger.error("Payment processing error for transaction: {}",
transaction.getTransactionId(), e);
return PaymentResult.failed("支付处理异常: " + e.getMessage());
}
}
@Override
public PaymentStatus queryPaymentStatus(String paymentId) {
try {
Payment payment = paymentRepository.findById(paymentId)
.orElseThrow(() -> new PaymentNotFoundException("Payment not found: " + paymentId));
PaymentProcessor processor = paymentProcessors.get(payment.getPaymentMethod());
if (processor != null) {
return processor.queryPaymentStatus(payment);
}
return PaymentStatus.UNKNOWN;
} catch (Exception e) {
logger.error("Error querying payment status: {}", paymentId, e);
return PaymentStatus.UNKNOWN;
}
}
@Override
public RefundResult processRefund(RefundRequest request) {
try {
// 验证退款请求
validateRefundRequest(request);
// 获取原支付记录
Payment originalPayment = paymentRepository.findById(request.getOriginalPaymentId())
.orElseThrow(() -> new PaymentNotFoundException("Original payment not found"));
// 检查退款条件
if (!canRefund(originalPayment, request.getRefundAmount())) {
return RefundResult.failed("不满足退款条件");
}
// 创建退款记录
Refund refund = createRefund(request, originalPayment);
// 执行退款
PaymentProcessor processor = paymentProcessors.get(originalPayment.getPaymentMethod());
RefundResult result = processor.processRefund(refund);
// 更新退款记录
updateRefundRecord(refund, result);
// 发布退款事件
if (result.isSuccess()) {
eventPublisher.publishRefundSuccess(refund);
} else {
eventPublisher.publishRefundFailed(refund, result.getErrorMessage());
}
return result;
} catch (Exception e) {
logger.error("Refund processing error", e);
return RefundResult.failed("退款处理异常: " + e.getMessage());
}
}
@Override
public void handlePaymentCallback(PaymentCallbackRequest request) {
try {
// 验证回调签名
if (!verifyCallbackSignature(request)) {
logger.warn("Invalid payment callback signature: {}", request.getPaymentId());
return;
}
// 防重复处理
String lockKey = "payment:callback:" + request.getPaymentId();
Boolean lockAcquired = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "locked", Duration.ofMinutes(5));
if (!lockAcquired) {
logger.warn("Payment callback already processing: {}", request.getPaymentId());
return;
}
try {
processPaymentCallback(request);
} finally {
redisTemplate.delete(lockKey);
}
} catch (Exception e) {
logger.error("Error handling payment callback", e);
}
}
@Override
public List<PaymentMethod> getSupportedPaymentMethods(String channelType) {
return paymentConfigService.getSupportedPaymentMethods(channelType);
}
private Payment createPayment(Transaction transaction) {
Payment payment = new Payment();
payment.setTransactionId(transaction.getTransactionId());
payment.setCustomerId(transaction.getCustomerId());
payment.setMerchantId(transaction.getMerchantId());
payment.setAmount(transaction.getAmount());
payment.setCurrency(transaction.getCurrency());
payment.setChannelType(transaction.getChannelType().name());
payment.setStatus(PaymentStatus.PENDING);
payment.setCreatedTime(LocalDateTime.now());
// 根据渠道选择默认支付方式
String defaultPaymentMethod = paymentConfigService.getDefaultPaymentMethod(
transaction.getChannelType().name());
payment.setPaymentMethod(defaultPaymentMethod);
return paymentRepository.save(payment);
}
private PaymentProcessor selectPaymentProcessor(Payment payment) {
return paymentProcessors.get(payment.getPaymentMethod());
}
private void updatePaymentRecord(Payment payment, PaymentResult result) {
payment.setStatus(result.isSuccess() ? PaymentStatus.SUCCESS : PaymentStatus.FAILED);
payment.setPaymentTransactionId(result.getPaymentTransactionId());
payment.setErrorCode(result.getErrorCode());
payment.setErrorMessage(result.getErrorMessage());
payment.setCompletedTime(LocalDateTime.now());
payment.setLastUpdated(LocalDateTime.now());
paymentRepository.save(payment);
}
private boolean canRefund(Payment originalPayment, BigDecimal refundAmount) {
// 检查支付状态
if (originalPayment.getStatus() != PaymentStatus.SUCCESS) {
return false;
}
// 检查退款金额
BigDecimal totalRefunded = paymentRepository.getTotalRefundedAmount(originalPayment.getPaymentId());
BigDecimal availableAmount = originalPayment.getAmount().subtract(totalRefunded);
return refundAmount.compareTo(availableAmount) <= 0;
}
private Refund createRefund(RefundRequest request, Payment originalPayment) {
Refund refund = new Refund();
refund.setOriginalPaymentId(originalPayment.getPaymentId());
refund.setTransactionId(originalPayment.getTransactionId());
refund.setCustomerId(originalPayment.getCustomerId());
refund.setMerchantId(originalPayment.getMerchantId());
refund.setRefundAmount(request.getRefundAmount());
refund.setCurrency(originalPayment.getCurrency());
refund.setReason(request.getReason());
refund.setStatus(RefundStatus.PENDING);
refund.setCreatedTime(LocalDateTime.now());
return refund;
}
private void updateRefundRecord(Refund refund, RefundResult result) {
refund.setStatus(result.isSuccess() ? RefundStatus.SUCCESS : RefundStatus.FAILED);
refund.setRefundTransactionId(result.getRefundTransactionId());
refund.setErrorCode(result.getErrorCode());
refund.setErrorMessage(result.getErrorMessage());
refund.setCompletedTime(LocalDateTime.now());
refund.setLastUpdated(LocalDateTime.now());
}
private boolean verifyCallbackSignature(PaymentCallbackRequest request) {
// 实现签名验证逻辑
return true; // 简化实现
}
private void processPaymentCallback(PaymentCallbackRequest request) {
Payment payment = paymentRepository.findById(request.getPaymentId())
.orElseThrow(() -> new PaymentNotFoundException("Payment not found"));
// 更新支付状态
PaymentStatus newStatus = PaymentStatus.valueOf(request.getStatus());
if (payment.getStatus() != newStatus) {
payment.setStatus(newStatus);
payment.setPaymentTransactionId(request.getPaymentTransactionId());
payment.setLastUpdated(LocalDateTime.now());
if (newStatus == PaymentStatus.SUCCESS) {
payment.setCompletedTime(LocalDateTime.now());
}
paymentRepository.save(payment);
// 发布状态变更事件
eventPublisher.publishPaymentStatusChanged(payment, newStatus);
}
}
private void validateRefundRequest(RefundRequest request) {
if (request == null) {
throw new IllegalArgumentException("Refund request cannot be null");
}
if (StringUtils.isBlank(request.getOriginalPaymentId())) {
throw new IllegalArgumentException("Original payment ID cannot be blank");
}
if (request.getRefundAmount() == null ||
request.getRefundAmount().compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("Refund amount must be positive");
}
}
}
/**
* 支付处理器接口
*/
public interface PaymentProcessor {
/**
* 获取支付方式
*/
String getPaymentMethod();
/**
* 处理支付
*/
PaymentResult processPayment(Payment payment);
/**
* 查询支付状态
*/
PaymentStatus queryPaymentStatus(Payment payment);
/**
* 处理退款
*/
RefundResult processRefund(Refund refund);
/**
* 是否支持该渠道
*/
boolean supportsChannel(String channelType);
}
/**
* 微信支付处理器
*/
@Component
public class WechatPaymentProcessor implements PaymentProcessor {
private static final Logger logger = LoggerFactory.getLogger(WechatPaymentProcessor.class);
private final WechatPayClient wechatPayClient;
private final PaymentConfigService configService;
public WechatPaymentProcessor(WechatPayClient wechatPayClient,
PaymentConfigService configService) {
this.wechatPayClient = wechatPayClient;
this.configService = configService;
}
@Override
public String getPaymentMethod() {
return "WECHAT_PAY";
}
@Override
public PaymentResult processPayment(Payment payment) {
try {
// 构建微信支付请求
WechatPayRequest request = buildWechatPayRequest(payment);
// 调用微信支付API
WechatPayResponse response = wechatPayClient.createOrder(request);
if (response.isSuccess()) {
return PaymentResult.success(
response.getTransactionId(),
getPaymentMethod(),
"WECHAT",
payment.getAmount()
);
} else {
return PaymentResult.failed(
response.getErrorCode(),
response.getErrorMessage()
);
}
} catch (Exception e) {
logger.error("Wechat payment processing error", e);
return PaymentResult.failed("SYSTEM_ERROR", "微信支付系统异常");
}
}
@Override
public PaymentStatus queryPaymentStatus(Payment payment) {
try {
WechatPayQueryResponse response = wechatPayClient.queryOrder(
payment.getPaymentTransactionId());
return mapWechatStatusToPaymentStatus(response.getTradeState());
} catch (Exception e) {
logger.error("Error querying wechat payment status", e);
return PaymentStatus.UNKNOWN;
}
}
@Override
public RefundResult processRefund(Refund refund) {
try {
WechatRefundRequest request = buildWechatRefundRequest(refund);
WechatRefundResponse response = wechatPayClient.refund(request);
if (response.isSuccess()) {
return RefundResult.success(
response.getRefundId(),
refund.getRefundAmount()
);
} else {
return RefundResult.failed(
response.getErrorCode(),
response.getErrorMessage()
);
}
} catch (Exception e) {
logger.error("Wechat refund processing error", e);
return RefundResult.failed("SYSTEM_ERROR", "微信退款系统异常");
}
}
@Override
public boolean supportsChannel(String channelType) {
return Arrays.asList("WECHAT_MINI", "MOBILE_APP", "WEB").contains(channelType);
}
private WechatPayRequest buildWechatPayRequest(Payment payment) {
WechatPayConfig config = configService.getWechatPayConfig(payment.getMerchantId());
WechatPayRequest request = new WechatPayRequest();
request.setAppId(config.getAppId());
request.setMchId(config.getMerchantId());
request.setOutTradeNo(payment.getPaymentId());
request.setTotalFee(payment.getAmount().multiply(BigDecimal.valueOf(100)).intValue()); // 转换为分
request.setBody("商品购买");
request.setNotifyUrl(config.getNotifyUrl());
request.setTradeType(getTradeType(payment.getChannelType()));
return request;
}
private WechatRefundRequest buildWechatRefundRequest(Refund refund) {
WechatPayConfig config = configService.getWechatPayConfig(refund.getMerchantId());
WechatRefundRequest request = new WechatRefundRequest();
request.setAppId(config.getAppId());
request.setMchId(config.getMerchantId());
request.setOutTradeNo(refund.getOriginalPaymentId());
request.setOutRefundNo(refund.getRefundId());
request.setTotalFee(refund.getOriginalAmount().multiply(BigDecimal.valueOf(100)).intValue());
request.setRefundFee(refund.getRefundAmount().multiply(BigDecimal.valueOf(100)).intValue());
request.setRefundDesc(refund.getReason());
return request;
}
private String getTradeType(String channelType) {
switch (channelType) {
case "WECHAT_MINI":
return "JSAPI";
case "MOBILE_APP":
return "APP";
case "WEB":
return "NATIVE";
default:
return "JSAPI";
}
}
private PaymentStatus mapWechatStatusToPaymentStatus(String tradeState) {
switch (tradeState) {
case "SUCCESS":
return PaymentStatus.SUCCESS;
case "REFUND":
return PaymentStatus.REFUNDED;
case "NOTPAY":
return PaymentStatus.PENDING;
case "CLOSED":
return PaymentStatus.CANCELLED;
case "REVOKED":
return PaymentStatus.CANCELLED;
case "USERPAYING":
return PaymentStatus.PROCESSING;
case "PAYERROR":
return PaymentStatus.FAILED;
default:
return PaymentStatus.UNKNOWN;
}
}
}
/**
* 支付宝支付处理器
*/
@Component
public class AlipayPaymentProcessor implements PaymentProcessor {
private static final Logger logger = LoggerFactory.getLogger(AlipayPaymentProcessor.class);
private final AlipayClient alipayClient;
private final PaymentConfigService configService;
public AlipayPaymentProcessor(AlipayClient alipayClient,
PaymentConfigService configService) {
this.alipayClient = alipayClient;
this.configService = configService;
}
@Override
public String getPaymentMethod() {
return "ALIPAY";
}
@Override
public PaymentResult processPayment(Payment payment) {
try {
AlipayTradeCreateRequest request = buildAlipayRequest(payment);
AlipayTradeCreateResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
return PaymentResult.success(
response.getTradeNo(),
getPaymentMethod(),
"ALIPAY",
payment.getAmount()
);
} else {
return PaymentResult.failed(
response.getCode(),
response.getMsg()
);
}
} catch (Exception e) {
logger.error("Alipay payment processing error", e);
return PaymentResult.failed("SYSTEM_ERROR", "支付宝支付系统异常");
}
}
@Override
public PaymentStatus queryPaymentStatus(Payment payment) {
try {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
request.setBizContent("{\"out_trade_no\":\"" + payment.getPaymentId() + "\"}");
AlipayTradeQueryResponse response = alipayClient.execute(request);
return mapAlipayStatusToPaymentStatus(response.getTradeStatus());
} catch (Exception e) {
logger.error("Error querying alipay payment status", e);
return PaymentStatus.UNKNOWN;
}
}
@Override
public RefundResult processRefund(Refund refund) {
try {
AlipayTradeRefundRequest request = buildAlipayRefundRequest(refund);
AlipayTradeRefundResponse response = alipayClient.execute(request);
if (response.isSuccess()) {
return RefundResult.success(
response.getTradeNo(),
refund.getRefundAmount()
);
} else {
return RefundResult.failed(
response.getCode(),
response.getMsg()
);
}
} catch (Exception e) {
logger.error("Alipay refund processing error", e);
return RefundResult.failed("SYSTEM_ERROR", "支付宝退款系统异常");
}
}
@Override
public boolean supportsChannel(String channelType) {
return Arrays.asList("ALIPAY_MINI", "MOBILE_APP", "WEB", "MOBILE_WEB").contains(channelType);
}
private AlipayTradeCreateRequest buildAlipayRequest(Payment payment) {
AlipayConfig config = configService.getAlipayConfig(payment.getMerchantId());
AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
request.setNotifyUrl(config.getNotifyUrl());
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", payment.getPaymentId());
bizContent.put("total_amount", payment.getAmount().toString());
bizContent.put("subject", "商品购买");
bizContent.put("buyer_id", payment.getCustomerId());
request.setBizContent(JSON.toJSONString(bizContent));
return request;
}
private AlipayTradeRefundRequest buildAlipayRefundRequest(Refund refund) {
AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
Map<String, Object> bizContent = new HashMap<>();
bizContent.put("out_trade_no", refund.getOriginalPaymentId());
bizContent.put("refund_amount", refund.getRefundAmount().toString());
bizContent.put("refund_reason", refund.getReason());
bizContent.put("out_request_no", refund.getRefundId());
request.setBizContent(JSON.toJSONString(bizContent));
return request;
}
private PaymentStatus mapAlipayStatusToPaymentStatus(String tradeStatus) {
switch (tradeStatus) {
case "TRADE_SUCCESS":
case "TRADE_FINISHED":
return PaymentStatus.SUCCESS;
case "WAIT_BUYER_PAY":
return PaymentStatus.PENDING;
case "TRADE_CLOSED":
return PaymentStatus.CANCELLED;
default:
return PaymentStatus.UNKNOWN;
}
}
}
/**
* 支付状态枚举
*/
public enum PaymentStatus {
PENDING("待支付"),
PROCESSING("支付中"),
SUCCESS("支付成功"),
FAILED("支付失败"),
CANCELLED("已取消"),
REFUNDED("已退款"),
PARTIAL_REFUNDED("部分退款"),
UNKNOWN("未知状态");
private final String description;
PaymentStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public boolean isFinalStatus() {
return this == SUCCESS || this == FAILED || this == CANCELLED || this == REFUNDED;
}
}
/**
* 退款状态枚举
*/
public enum RefundStatus {
PENDING("退款中"),
SUCCESS("退款成功"),
FAILED("退款失败"),
CANCELLED("已取消");
private final String description;
RefundStatus(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
11.5.2 支付数据模型
支付系统的数据模型包括支付记录、退款记录等:
java
/**
* 支付记录
*/
@Entity
@Table(name = "payments")
public class Payment {
@Id
private String paymentId;
@Column(nullable = false)
private String transactionId;
@Column(nullable = false)
private String customerId;
@Column(nullable = false)
private String merchantId;
@Column(nullable = false, precision = 19, scale = 2)
private BigDecimal amount;
@Column(length = 3)
private String currency;
@Column(nullable = false)
private String paymentMethod;
@Column
private String channelType;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private PaymentStatus status;
@Column
private String paymentTransactionId;
@Column
private String errorCode;
@Column(length = 500)
private String errorMessage;
@Column(nullable = false)
private LocalDateTime createdTime;
@Column(nullable = false)
private LocalDateTime lastUpdated;
private LocalDateTime completedTime;
@Column(columnDefinition = "TEXT")
private String metadata;
// 构造函数
public Payment() {
this.paymentId = generatePaymentId();
this.createdTime = LocalDateTime.now();
this.lastUpdated = LocalDateTime.now();
this.status = PaymentStatus.PENDING;
this.currency = "CNY";
}
private String generatePaymentId() {
return "PAY" + System.currentTimeMillis() +
String.format("%04d", new Random().nextInt(10000));
}
// Getters and Setters
public String getPaymentId() { return paymentId; }
public void setPaymentId(String paymentId) { this.paymentId = paymentId; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getMerchantId() { return merchantId; }
public void setMerchantId(String merchantId) { this.merchantId = merchantId; }
public BigDecimal getAmount() { return amount; }
public void setAmount(BigDecimal amount) { this.amount = amount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getPaymentMethod() { return paymentMethod; }
public void setPaymentMethod(String paymentMethod) { this.paymentMethod = paymentMethod; }
public String getChannelType() { return channelType; }
public void setChannelType(String channelType) { this.channelType = channelType; }
public PaymentStatus getStatus() { return status; }
public void setStatus(PaymentStatus status) { this.status = status; }
public String getPaymentTransactionId() { return paymentTransactionId; }
public void setPaymentTransactionId(String paymentTransactionId) { this.paymentTransactionId = paymentTransactionId; }
public String getErrorCode() { return errorCode; }
public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
public String getErrorMessage() { return errorMessage; }
public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getLastUpdated() { return lastUpdated; }
public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; }
public LocalDateTime getCompletedTime() { return completedTime; }
public void setCompletedTime(LocalDateTime completedTime) { this.completedTime = completedTime; }
public String getMetadata() { return metadata; }
public void setMetadata(String metadata) { this.metadata = metadata; }
}
/**
* 退款记录
*/
@Entity
@Table(name = "refunds")
public class Refund {
@Id
private String refundId;
@Column(nullable = false)
private String originalPaymentId;
@Column(nullable = false)
private String transactionId;
@Column(nullable = false)
private String customerId;
@Column(nullable = false)
private String merchantId;
@Column(nullable = false, precision = 19, scale = 2)
private BigDecimal originalAmount;
@Column(nullable = false, precision = 19, scale = 2)
private BigDecimal refundAmount;
@Column(length = 3)
private String currency;
@Column(length = 500)
private String reason;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private RefundStatus status;
@Column
private String refundTransactionId;
@Column
private String errorCode;
@Column(length = 500)
private String errorMessage;
@Column(nullable = false)
private LocalDateTime createdTime;
@Column(nullable = false)
private LocalDateTime lastUpdated;
private LocalDateTime completedTime;
// 构造函数
public Refund() {
this.refundId = generateRefundId();
this.createdTime = LocalDateTime.now();
this.lastUpdated = LocalDateTime.now();
this.status = RefundStatus.PENDING;
this.currency = "CNY";
}
private String generateRefundId() {
return "REF" + System.currentTimeMillis() +
String.format("%04d", new Random().nextInt(10000));
}
// Getters and Setters
public String getRefundId() { return refundId; }
public void setRefundId(String refundId) { this.refundId = refundId; }
public String getOriginalPaymentId() { return originalPaymentId; }
public void setOriginalPaymentId(String originalPaymentId) { this.originalPaymentId = originalPaymentId; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public String getMerchantId() { return merchantId; }
public void setMerchantId(String merchantId) { this.merchantId = merchantId; }
public BigDecimal getOriginalAmount() { return originalAmount; }
public void setOriginalAmount(BigDecimal originalAmount) { this.originalAmount = originalAmount; }
public BigDecimal getRefundAmount() { return refundAmount; }
public void setRefundAmount(BigDecimal refundAmount) { this.refundAmount = refundAmount; }
public String getCurrency() { return currency; }
public void setCurrency(String currency) { this.currency = currency; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
public RefundStatus getStatus() { return status; }
public void setStatus(RefundStatus status) { this.status = status; }
public String getRefundTransactionId() { return refundTransactionId; }
public void setRefundTransactionId(String refundTransactionId) { this.refundTransactionId = refundTransactionId; }
public String getErrorCode() { return errorCode; }
public void setErrorCode(String errorCode) { this.errorCode = errorCode; }
public String getErrorMessage() { return errorMessage; }
public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getLastUpdated() { return lastUpdated; }
public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; }
public LocalDateTime getCompletedTime() { return completedTime; }
public void setCompletedTime(LocalDateTime completedTime) { this.completedTime = completedTime; }
}
11.6 风控系统
11.6.1 风控评估引擎
风控系统是交易系统的重要组成部分,用于识别和防范交易风险:
java
/**
* 风控评估服务
*/
@Service
public class RiskAssessmentService {
private static final Logger logger = LoggerFactory.getLogger(RiskAssessmentService.class);
private final List<RiskRule> riskRules;
private final RiskConfigService riskConfigService;
private final CustomerRiskProfileService customerRiskProfileService;
private final TransactionHistoryService transactionHistoryService;
private final BlacklistService blacklistService;
private final RedisTemplate<String, Object> redisTemplate;
public RiskAssessmentService(List<RiskRule> riskRules,
RiskConfigService riskConfigService,
CustomerRiskProfileService customerRiskProfileService,
TransactionHistoryService transactionHistoryService,
BlacklistService blacklistService,
RedisTemplate<String, Object> redisTemplate) {
this.riskRules = riskRules;
this.riskConfigService = riskConfigService;
this.customerRiskProfileService = customerRiskProfileService;
this.transactionHistoryService = transactionHistoryService;
this.blacklistService = blacklistService;
this.redisTemplate = redisTemplate;
}
/**
* 评估交易风险
*/
public RiskAssessmentResult assessRisk(Transaction transaction) {
try {
// 创建风险评估上下文
RiskAssessmentContext context = createAssessmentContext(transaction);
// 执行风险规则
List<RiskRuleResult> ruleResults = executeRiskRules(context);
// 计算风险分数
int totalRiskScore = calculateTotalRiskScore(ruleResults);
// 确定风险等级
RiskLevel riskLevel = determineRiskLevel(totalRiskScore);
// 确定处理决策
RiskDecision decision = determineRiskDecision(riskLevel, ruleResults);
// 创建评估结果
RiskAssessmentResult result = new RiskAssessmentResult(
transaction.getTransactionId(),
riskLevel,
totalRiskScore,
decision,
ruleResults,
LocalDateTime.now()
);
// 记录风险评估日志
logRiskAssessment(transaction, result);
// 缓存评估结果
cacheAssessmentResult(result);
return result;
} catch (Exception e) {
logger.error("Risk assessment error for transaction: {}",
transaction.getTransactionId(), e);
// 返回默认高风险结果
return createDefaultHighRiskResult(transaction.getTransactionId());
}
}
/**
* 获取客户风险画像
*/
public CustomerRiskProfile getCustomerRiskProfile(String customerId) {
try {
// 先从缓存获取
String cacheKey = "customer:risk:profile:" + customerId;
CustomerRiskProfile cached = (CustomerRiskProfile) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
// 构建客户风险画像
CustomerRiskProfile profile = customerRiskProfileService.buildRiskProfile(customerId);
// 缓存结果
redisTemplate.opsForValue().set(cacheKey, profile, Duration.ofHours(1));
return profile;
} catch (Exception e) {
logger.error("Error getting customer risk profile: {}", customerId, e);
return CustomerRiskProfile.createDefault(customerId);
}
}
/**
* 更新风险规则
*/
public void updateRiskRules(List<RiskRuleConfig> ruleConfigs) {
try {
riskConfigService.updateRiskRules(ruleConfigs);
// 清除相关缓存
clearRiskCache();
logger.info("Risk rules updated successfully, count: {}", ruleConfigs.size());
} catch (Exception e) {
logger.error("Error updating risk rules", e);
throw new RiskConfigurationException("Failed to update risk rules", e);
}
}
private RiskAssessmentContext createAssessmentContext(Transaction transaction) {
RiskAssessmentContext context = new RiskAssessmentContext();
context.setTransaction(transaction);
// 获取客户风险画像
CustomerRiskProfile customerProfile = getCustomerRiskProfile(transaction.getCustomerId());
context.setCustomerProfile(customerProfile);
// 获取交易历史
List<Transaction> recentTransactions = transactionHistoryService
.getRecentTransactions(transaction.getCustomerId(), Duration.ofDays(30));
context.setRecentTransactions(recentTransactions);
// 检查黑名单
boolean isBlacklisted = blacklistService.isBlacklisted(
transaction.getCustomerId(),
transaction.getMerchantId()
);
context.setBlacklisted(isBlacklisted);
// 获取设备信息
DeviceInfo deviceInfo = getDeviceInfo(transaction);
context.setDeviceInfo(deviceInfo);
return context;
}
private List<RiskRuleResult> executeRiskRules(RiskAssessmentContext context) {
List<RiskRuleResult> results = new ArrayList<>();
for (RiskRule rule : riskRules) {
try {
if (rule.isApplicable(context)) {
RiskRuleResult result = rule.evaluate(context);
results.add(result);
}
} catch (Exception e) {
logger.error("Error executing risk rule: {}", rule.getRuleName(), e);
// 创建错误结果
RiskRuleResult errorResult = new RiskRuleResult(
rule.getRuleName(),
RiskLevel.HIGH,
100, // 最高风险分数
"规则执行异常: " + e.getMessage()
);
results.add(errorResult);
}
}
return results;
}
private int calculateTotalRiskScore(List<RiskRuleResult> ruleResults) {
return ruleResults.stream()
.mapToInt(RiskRuleResult::getRiskScore)
.sum();
}
private RiskLevel determineRiskLevel(int totalRiskScore) {
RiskThresholds thresholds = riskConfigService.getRiskThresholds();
if (totalRiskScore >= thresholds.getHighRiskThreshold()) {
return RiskLevel.HIGH;
} else if (totalRiskScore >= thresholds.getMediumRiskThreshold()) {
return RiskLevel.MEDIUM;
} else {
return RiskLevel.LOW;
}
}
private RiskDecision determineRiskDecision(RiskLevel riskLevel, List<RiskRuleResult> ruleResults) {
// 检查是否有阻断规则触发
boolean hasBlockingRule = ruleResults.stream()
.anyMatch(result -> result.isBlocking());
if (hasBlockingRule) {
return RiskDecision.REJECT;
}
switch (riskLevel) {
case HIGH:
return RiskDecision.MANUAL_REVIEW;
case MEDIUM:
return RiskDecision.ADDITIONAL_VERIFICATION;
case LOW:
default:
return RiskDecision.APPROVE;
}
}
private void logRiskAssessment(Transaction transaction, RiskAssessmentResult result) {
logger.info("Risk assessment completed - Transaction: {}, Risk Level: {}, Score: {}, Decision: {}",
transaction.getTransactionId(),
result.getRiskLevel(),
result.getRiskScore(),
result.getDecision());
}
private void cacheAssessmentResult(RiskAssessmentResult result) {
String cacheKey = "risk:assessment:" + result.getTransactionId();
redisTemplate.opsForValue().set(cacheKey, result, Duration.ofHours(24));
}
private RiskAssessmentResult createDefaultHighRiskResult(String transactionId) {
return new RiskAssessmentResult(
transactionId,
RiskLevel.HIGH,
100,
RiskDecision.MANUAL_REVIEW,
Collections.emptyList(),
LocalDateTime.now()
);
}
private DeviceInfo getDeviceInfo(Transaction transaction) {
// 从交易元数据中提取设备信息
// 简化实现
return new DeviceInfo();
}
private void clearRiskCache() {
Set<String> keys = redisTemplate.keys("risk:*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
/**
* 风控规则接口
*/
public interface RiskRule {
/**
* 规则名称
*/
String getRuleName();
/**
* 规则描述
*/
String getDescription();
/**
* 规则优先级
*/
int getPriority();
/**
* 是否适用于当前上下文
*/
boolean isApplicable(RiskAssessmentContext context);
/**
* 评估风险
*/
RiskRuleResult evaluate(RiskAssessmentContext context);
}
/**
* 交易金额风险规则
*/
@Component
public class TransactionAmountRiskRule implements RiskRule {
private final RiskConfigService configService;
public TransactionAmountRiskRule(RiskConfigService configService) {
this.configService = configService;
}
@Override
public String getRuleName() {
return "TRANSACTION_AMOUNT_RISK";
}
@Override
public String getDescription() {
return "基于交易金额的风险评估";
}
@Override
public int getPriority() {
return 100;
}
@Override
public boolean isApplicable(RiskAssessmentContext context) {
return context.getTransaction() != null;
}
@Override
public RiskRuleResult evaluate(RiskAssessmentContext context) {
Transaction transaction = context.getTransaction();
BigDecimal amount = transaction.getAmount();
AmountRiskConfig config = configService.getAmountRiskConfig();
int riskScore = 0;
RiskLevel riskLevel = RiskLevel.LOW;
String reason = "交易金额正常";
boolean blocking = false;
if (amount.compareTo(config.getHighRiskThreshold()) >= 0) {
riskScore = 50;
riskLevel = RiskLevel.HIGH;
reason = "交易金额超过高风险阈值: " + config.getHighRiskThreshold();
} else if (amount.compareTo(config.getMediumRiskThreshold()) >= 0) {
riskScore = 25;
riskLevel = RiskLevel.MEDIUM;
reason = "交易金额超过中风险阈值: " + config.getMediumRiskThreshold();
}
// 检查是否超过阻断阈值
if (amount.compareTo(config.getBlockingThreshold()) >= 0) {
riskScore = 100;
riskLevel = RiskLevel.HIGH;
reason = "交易金额超过阻断阈值: " + config.getBlockingThreshold();
blocking = true;
}
return new RiskRuleResult(getRuleName(), riskLevel, riskScore, reason, blocking);
}
}
/**
* 交易频率风险规则
*/
@Component
public class TransactionFrequencyRiskRule implements RiskRule {
private final RiskConfigService configService;
public TransactionFrequencyRiskRule(RiskConfigService configService) {
this.configService = configService;
}
@Override
public String getRuleName() {
return "TRANSACTION_FREQUENCY_RISK";
}
@Override
public String getDescription() {
return "基于交易频率的风险评估";
}
@Override
public int getPriority() {
return 90;
}
@Override
public boolean isApplicable(RiskAssessmentContext context) {
return context.getTransaction() != null &&
context.getRecentTransactions() != null;
}
@Override
public RiskRuleResult evaluate(RiskAssessmentContext context) {
List<Transaction> recentTransactions = context.getRecentTransactions();
// 计算最近1小时内的交易次数
LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1);
long recentCount = recentTransactions.stream()
.filter(t -> t.getCreatedTime().isAfter(oneHourAgo))
.count();
FrequencyRiskConfig config = configService.getFrequencyRiskConfig();
int riskScore = 0;
RiskLevel riskLevel = RiskLevel.LOW;
String reason = "交易频率正常";
boolean blocking = false;
if (recentCount >= config.getHighRiskThreshold()) {
riskScore = 40;
riskLevel = RiskLevel.HIGH;
reason = "1小时内交易次数过多: " + recentCount;
} else if (recentCount >= config.getMediumRiskThreshold()) {
riskScore = 20;
riskLevel = RiskLevel.MEDIUM;
reason = "1小时内交易次数较多: " + recentCount;
}
// 检查是否超过阻断阈值
if (recentCount >= config.getBlockingThreshold()) {
riskScore = 100;
riskLevel = RiskLevel.HIGH;
reason = "1小时内交易次数超过阻断阈值: " + recentCount;
blocking = true;
}
return new RiskRuleResult(getRuleName(), riskLevel, riskScore, reason, blocking);
}
}
/**
* 黑名单风险规则
*/
@Component
public class BlacklistRiskRule implements RiskRule {
@Override
public String getRuleName() {
return "BLACKLIST_RISK";
}
@Override
public String getDescription() {
return "黑名单风险检查";
}
@Override
public int getPriority() {
return 200; // 最高优先级
}
@Override
public boolean isApplicable(RiskAssessmentContext context) {
return true;
}
@Override
public RiskRuleResult evaluate(RiskAssessmentContext context) {
if (context.isBlacklisted()) {
return new RiskRuleResult(
getRuleName(),
RiskLevel.HIGH,
100,
"客户或商户在黑名单中",
true // 阻断交易
);
}
return new RiskRuleResult(
getRuleName(),
RiskLevel.LOW,
0,
"未发现黑名单风险"
);
}
}
/**
* 风险评估上下文
*/
public class RiskAssessmentContext {
private Transaction transaction;
private CustomerRiskProfile customerProfile;
private List<Transaction> recentTransactions;
private boolean blacklisted;
private DeviceInfo deviceInfo;
// Getters and Setters
public Transaction getTransaction() { return transaction; }
public void setTransaction(Transaction transaction) { this.transaction = transaction; }
public CustomerRiskProfile getCustomerProfile() { return customerProfile; }
public void setCustomerProfile(CustomerRiskProfile customerProfile) { this.customerProfile = customerProfile; }
public List<Transaction> getRecentTransactions() { return recentTransactions; }
public void setRecentTransactions(List<Transaction> recentTransactions) { this.recentTransactions = recentTransactions; }
public boolean isBlacklisted() { return blacklisted; }
public void setBlacklisted(boolean blacklisted) { this.blacklisted = blacklisted; }
public DeviceInfo getDeviceInfo() { return deviceInfo; }
public void setDeviceInfo(DeviceInfo deviceInfo) { this.deviceInfo = deviceInfo; }
}
/**
* 风险规则结果
*/
public class RiskRuleResult {
private String ruleName;
private RiskLevel riskLevel;
private int riskScore;
private String reason;
private boolean blocking;
private LocalDateTime evaluatedTime;
public RiskRuleResult(String ruleName, RiskLevel riskLevel, int riskScore, String reason) {
this(ruleName, riskLevel, riskScore, reason, false);
}
public RiskRuleResult(String ruleName, RiskLevel riskLevel, int riskScore, String reason, boolean blocking) {
this.ruleName = ruleName;
this.riskLevel = riskLevel;
this.riskScore = riskScore;
this.reason = reason;
this.blocking = blocking;
this.evaluatedTime = LocalDateTime.now();
}
// Getters and Setters
public String getRuleName() { return ruleName; }
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
public RiskLevel getRiskLevel() { return riskLevel; }
public void setRiskLevel(RiskLevel riskLevel) { this.riskLevel = riskLevel; }
public int getRiskScore() { return riskScore; }
public void setRiskScore(int riskScore) { this.riskScore = riskScore; }
public String getReason() { return reason; }
public void setReason(String reason) { this.reason = reason; }
public boolean isBlocking() { return blocking; }
public void setBlocking(boolean blocking) { this.blocking = blocking; }
public LocalDateTime getEvaluatedTime() { return evaluatedTime; }
public void setEvaluatedTime(LocalDateTime evaluatedTime) { this.evaluatedTime = evaluatedTime; }
}
/**
* 风险决策枚举
*/
public enum RiskDecision {
APPROVE("通过"),
ADDITIONAL_VERIFICATION("需要额外验证"),
MANUAL_REVIEW("人工审核"),
REJECT("拒绝");
private final String description;
RiskDecision(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* 客户风险画像
*/
public class CustomerRiskProfile {
private String customerId;
private RiskLevel overallRiskLevel;
private int creditScore;
private BigDecimal averageTransactionAmount;
private int transactionCount;
private LocalDateTime firstTransactionTime;
private LocalDateTime lastTransactionTime;
private int disputeCount;
private int chargebackCount;
private boolean isVerified;
private LocalDateTime createdTime;
private LocalDateTime lastUpdated;
public static CustomerRiskProfile createDefault(String customerId) {
CustomerRiskProfile profile = new CustomerRiskProfile();
profile.setCustomerId(customerId);
profile.setOverallRiskLevel(RiskLevel.MEDIUM);
profile.setCreditScore(500);
profile.setAverageTransactionAmount(BigDecimal.ZERO);
profile.setTransactionCount(0);
profile.setDisputeCount(0);
profile.setChargebackCount(0);
profile.setVerified(false);
profile.setCreatedTime(LocalDateTime.now());
profile.setLastUpdated(LocalDateTime.now());
return profile;
}
// Getters and Setters
public String getCustomerId() { return customerId; }
public void setCustomerId(String customerId) { this.customerId = customerId; }
public RiskLevel getOverallRiskLevel() { return overallRiskLevel; }
public void setOverallRiskLevel(RiskLevel overallRiskLevel) { this.overallRiskLevel = overallRiskLevel; }
public int getCreditScore() { return creditScore; }
public void setCreditScore(int creditScore) { this.creditScore = creditScore; }
public BigDecimal getAverageTransactionAmount() { return averageTransactionAmount; }
public void setAverageTransactionAmount(BigDecimal averageTransactionAmount) { this.averageTransactionAmount = averageTransactionAmount; }
public int getTransactionCount() { return transactionCount; }
public void setTransactionCount(int transactionCount) { this.transactionCount = transactionCount; }
public LocalDateTime getFirstTransactionTime() { return firstTransactionTime; }
public void setFirstTransactionTime(LocalDateTime firstTransactionTime) { this.firstTransactionTime = firstTransactionTime; }
public LocalDateTime getLastTransactionTime() { return lastTransactionTime; }
public void setLastTransactionTime(LocalDateTime lastTransactionTime) { this.lastTransactionTime = lastTransactionTime; }
public int getDisputeCount() { return disputeCount; }
public void setDisputeCount(int disputeCount) { this.disputeCount = disputeCount; }
public int getChargebackCount() { return chargebackCount; }
public void setChargebackCount(int chargebackCount) { this.chargebackCount = chargebackCount; }
public boolean isVerified() { return isVerified; }
public void setVerified(boolean verified) { isVerified = verified; }
public LocalDateTime getCreatedTime() { return createdTime; }
public void setCreatedTime(LocalDateTime createdTime) { this.createdTime = createdTime; }
public LocalDateTime getLastUpdated() { return lastUpdated; }
public void setLastUpdated(LocalDateTime lastUpdated) { this.lastUpdated = lastUpdated; }
}
/**
* 设备信息
*/
public class DeviceInfo {
private String deviceId;
private String deviceType;
private String operatingSystem;
private String browserInfo;
private String ipAddress;
private String location;
private boolean isTrustedDevice;
// Getters and Setters
public String getDeviceId() { return deviceId; }
public void setDeviceId(String deviceId) { this.deviceId = deviceId; }
public String getDeviceType() { return deviceType; }
public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
public String getOperatingSystem() { return operatingSystem; }
public void setOperatingSystem(String operatingSystem) { this.operatingSystem = operatingSystem; }
public String getBrowserInfo() { return browserInfo; }
public void setBrowserInfo(String browserInfo) { this.browserInfo = browserInfo; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
public String getLocation() { return location; }
public void setLocation(String location) { this.location = location; }
public boolean isTrustedDevice() { return isTrustedDevice; }
public void setTrustedDevice(boolean trustedDevice) { isTrustedDevice = trustedDevice; }
}
11.6.2 风控配置管理
风控系统需要灵活的配置管理机制:
java
/**
* 风控配置服务
*/
@Service
public class RiskConfigService {
private static final Logger logger = LoggerFactory.getLogger(RiskConfigService.class);
private final RiskConfigRepository riskConfigRepository;
private final RedisTemplate<String, Object> redisTemplate;
public RiskConfigService(RiskConfigRepository riskConfigRepository,
RedisTemplate<String, Object> redisTemplate) {
this.riskConfigRepository = riskConfigRepository;
this.redisTemplate = redisTemplate;
}
/**
* 获取风险阈值配置
*/
public RiskThresholds getRiskThresholds() {
String cacheKey = "risk:config:thresholds";
RiskThresholds cached = (RiskThresholds) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
RiskThresholds thresholds = riskConfigRepository.getRiskThresholds()
.orElse(RiskThresholds.createDefault());
redisTemplate.opsForValue().set(cacheKey, thresholds, Duration.ofMinutes(30));
return thresholds;
}
/**
* 获取金额风险配置
*/
public AmountRiskConfig getAmountRiskConfig() {
String cacheKey = "risk:config:amount";
AmountRiskConfig cached = (AmountRiskConfig) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
AmountRiskConfig config = riskConfigRepository.getAmountRiskConfig()
.orElse(AmountRiskConfig.createDefault());
redisTemplate.opsForValue().set(cacheKey, config, Duration.ofMinutes(30));
return config;
}
/**
* 获取频率风险配置
*/
public FrequencyRiskConfig getFrequencyRiskConfig() {
String cacheKey = "risk:config:frequency";
FrequencyRiskConfig cached = (FrequencyRiskConfig) redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
FrequencyRiskConfig config = riskConfigRepository.getFrequencyRiskConfig()
.orElse(FrequencyRiskConfig.createDefault());
redisTemplate.opsForValue().set(cacheKey, config, Duration.ofMinutes(30));
return config;
}
/**
* 更新风险规则配置
*/
public void updateRiskRules(List<RiskRuleConfig> ruleConfigs) {
try {
riskConfigRepository.updateRiskRules(ruleConfigs);
// 清除相关缓存
clearConfigCache();
logger.info("Risk rules updated successfully");
} catch (Exception e) {
logger.error("Error updating risk rules", e);
throw new RiskConfigurationException("Failed to update risk rules", e);
}
}
private void clearConfigCache() {
Set<String> keys = redisTemplate.keys("risk:config:*");
if (keys != null && !keys.isEmpty()) {
redisTemplate.delete(keys);
}
}
}
/**
* 风险阈值配置
*/
public class RiskThresholds {
private int lowRiskThreshold;
private int mediumRiskThreshold;
private int highRiskThreshold;
public static RiskThresholds createDefault() {
RiskThresholds thresholds = new RiskThresholds();
thresholds.setLowRiskThreshold(20);
thresholds.setMediumRiskThreshold(50);
thresholds.setHighRiskThreshold(80);
return thresholds;
}
// Getters and Setters
public int getLowRiskThreshold() { return lowRiskThreshold; }
public void setLowRiskThreshold(int lowRiskThreshold) { this.lowRiskThreshold = lowRiskThreshold; }
public int getMediumRiskThreshold() { return mediumRiskThreshold; }
public void setMediumRiskThreshold(int mediumRiskThreshold) { this.mediumRiskThreshold = mediumRiskThreshold; }
public int getHighRiskThreshold() { return highRiskThreshold; }
public void setHighRiskThreshold(int highRiskThreshold) { this.highRiskThreshold = highRiskThreshold; }
}
/**
* 金额风险配置
*/
public class AmountRiskConfig {
private BigDecimal mediumRiskThreshold;
private BigDecimal highRiskThreshold;
private BigDecimal blockingThreshold;
public static AmountRiskConfig createDefault() {
AmountRiskConfig config = new AmountRiskConfig();
config.setMediumRiskThreshold(new BigDecimal("1000.00"));
config.setHighRiskThreshold(new BigDecimal("5000.00"));
config.setBlockingThreshold(new BigDecimal("10000.00"));
return config;
}
// Getters and Setters
public BigDecimal getMediumRiskThreshold() { return mediumRiskThreshold; }
public void setMediumRiskThreshold(BigDecimal mediumRiskThreshold) { this.mediumRiskThreshold = mediumRiskThreshold; }
public BigDecimal getHighRiskThreshold() { return highRiskThreshold; }
public void setHighRiskThreshold(BigDecimal highRiskThreshold) { this.highRiskThreshold = highRiskThreshold; }
public BigDecimal getBlockingThreshold() { return blockingThreshold; }
public void setBlockingThreshold(BigDecimal blockingThreshold) { this.blockingThreshold = blockingThreshold; }
}
/**
* 频率风险配置
*/
public class FrequencyRiskConfig {
private long mediumRiskThreshold;
private long highRiskThreshold;
private long blockingThreshold;
public static FrequencyRiskConfig createDefault() {
FrequencyRiskConfig config = new FrequencyRiskConfig();
config.setMediumRiskThreshold(10);
config.setHighRiskThreshold(20);
config.setBlockingThreshold(50);
return config;
}
// Getters and Setters
public long getMediumRiskThreshold() { return mediumRiskThreshold; }
public void setMediumRiskThreshold(long mediumRiskThreshold) { this.mediumRiskThreshold = mediumRiskThreshold; }
public long getHighRiskThreshold() { return highRiskThreshold; }
public void setHighRiskThreshold(long highRiskThreshold) { this.highRiskThreshold = highRiskThreshold; }
public long getBlockingThreshold() { return blockingThreshold; }
public void setBlockingThreshold(long blockingThreshold) { this.blockingThreshold = blockingThreshold; }
}
11.7 API接口设计
11.7.1 交易API接口
交易系统提供完整的RESTful API接口:
java
/**
* 交易控制器
*/
@RestController
@RequestMapping("/api/v1/transactions")
@Validated
public class TransactionController {
private static final Logger logger = LoggerFactory.getLogger(TransactionController.class);
private final TransactionService transactionService;
private final PaymentService paymentService;
private final RiskAssessmentService riskAssessmentService;
public TransactionController(TransactionService transactionService,
PaymentService paymentService,
RiskAssessmentService riskAssessmentService) {
this.transactionService = transactionService;
this.paymentService = paymentService;
this.riskAssessmentService = riskAssessmentService;
}
/**
* 创建交易
*/
@PostMapping
public ResponseEntity<ApiResponse<TransactionResponse>> createTransaction(
@Valid @RequestBody CreateTransactionRequest request) {
try {
logger.info("Creating transaction for customer: {}, amount: {}",
request.getCustomerId(), request.getAmount());
Transaction transaction = transactionService.createTransaction(request);
TransactionResponse response = convertToResponse(transaction);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (InsufficientInventoryException e) {
logger.warn("Insufficient inventory for transaction: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(ApiResponse.error("INSUFFICIENT_INVENTORY", e.getMessage()));
} catch (RiskAssessmentException e) {
logger.warn("Risk assessment failed for transaction: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(ApiResponse.error("RISK_ASSESSMENT_FAILED", e.getMessage()));
} catch (Exception e) {
logger.error("Error creating transaction", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "交易创建失败"));
}
}
/**
* 处理交易
*/
@PostMapping("/{transactionId}/process")
public ResponseEntity<ApiResponse<TransactionResponse>> processTransaction(
@PathVariable String transactionId,
@Valid @RequestBody ProcessTransactionRequest request) {
try {
logger.info("Processing transaction: {}", transactionId);
Transaction transaction = transactionService.processTransaction(transactionId, request);
TransactionResponse response = convertToResponse(transaction);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (TransactionNotFoundException e) {
logger.warn("Transaction not found: {}", transactionId);
return ResponseEntity.notFound().build();
} catch (InvalidTransactionStatusException e) {
logger.warn("Invalid transaction status: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(ApiResponse.error("INVALID_STATUS", e.getMessage()));
} catch (PaymentProcessingException e) {
logger.error("Payment processing failed for transaction: {}", transactionId, e);
return ResponseEntity.badRequest()
.body(ApiResponse.error("PAYMENT_FAILED", e.getMessage()));
} catch (Exception e) {
logger.error("Error processing transaction: {}", transactionId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "交易处理失败"));
}
}
/**
* 取消交易
*/
@PostMapping("/{transactionId}/cancel")
public ResponseEntity<ApiResponse<TransactionResponse>> cancelTransaction(
@PathVariable String transactionId,
@Valid @RequestBody CancelTransactionRequest request) {
try {
logger.info("Cancelling transaction: {}, reason: {}",
transactionId, request.getReason());
Transaction transaction = transactionService.cancelTransaction(transactionId, request.getReason());
TransactionResponse response = convertToResponse(transaction);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (TransactionNotFoundException e) {
logger.warn("Transaction not found: {}", transactionId);
return ResponseEntity.notFound().build();
} catch (InvalidTransactionStatusException e) {
logger.warn("Cannot cancel transaction in current status: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(ApiResponse.error("INVALID_STATUS", e.getMessage()));
} catch (Exception e) {
logger.error("Error cancelling transaction: {}", transactionId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "交易取消失败"));
}
}
/**
* 查询交易详情
*/
@GetMapping("/{transactionId}")
public ResponseEntity<ApiResponse<TransactionResponse>> getTransaction(
@PathVariable String transactionId) {
try {
Transaction transaction = transactionService.getTransaction(transactionId);
if (transaction == null) {
return ResponseEntity.notFound().build();
}
TransactionResponse response = convertToResponse(transaction);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
logger.error("Error getting transaction: {}", transactionId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "查询交易失败"));
}
}
/**
* 查询交易列表
*/
@GetMapping
public ResponseEntity<ApiResponse<PageResponse<TransactionResponse>>> getTransactions(
@RequestParam(required = false) String customerId,
@RequestParam(required = false) String merchantId,
@RequestParam(required = false) TransactionStatus status,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
try {
TransactionQueryRequest queryRequest = new TransactionQueryRequest();
queryRequest.setCustomerId(customerId);
queryRequest.setMerchantId(merchantId);
queryRequest.setStatus(status);
queryRequest.setStartTime(startTime);
queryRequest.setEndTime(endTime);
queryRequest.setPage(page);
queryRequest.setSize(size);
PageResponse<Transaction> transactions = transactionService.queryTransactions(queryRequest);
List<TransactionResponse> responseList = transactions.getContent().stream()
.map(this::convertToResponse)
.collect(Collectors.toList());
PageResponse<TransactionResponse> response = new PageResponse<>(
responseList,
transactions.getTotalElements(),
transactions.getTotalPages(),
transactions.getCurrentPage(),
transactions.getPageSize()
);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (Exception e) {
logger.error("Error querying transactions", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "查询交易列表失败"));
}
}
/**
* 获取交易统计
*/
@GetMapping("/statistics")
public ResponseEntity<ApiResponse<TransactionStatistics>> getTransactionStatistics(
@RequestParam(required = false) String merchantId,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime,
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) {
try {
TransactionStatistics statistics = transactionService.getTransactionStatistics(
merchantId, startTime, endTime);
return ResponseEntity.ok(ApiResponse.success(statistics));
} catch (Exception e) {
logger.error("Error getting transaction statistics", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "获取交易统计失败"));
}
}
/**
* 重试交易
*/
@PostMapping("/{transactionId}/retry")
public ResponseEntity<ApiResponse<TransactionResponse>> retryTransaction(
@PathVariable String transactionId) {
try {
logger.info("Retrying transaction: {}", transactionId);
Transaction transaction = transactionService.retryTransaction(transactionId);
TransactionResponse response = convertToResponse(transaction);
return ResponseEntity.ok(ApiResponse.success(response));
} catch (TransactionNotFoundException e) {
logger.warn("Transaction not found: {}", transactionId);
return ResponseEntity.notFound().build();
} catch (InvalidTransactionStatusException e) {
logger.warn("Cannot retry transaction in current status: {}", e.getMessage());
return ResponseEntity.badRequest()
.body(ApiResponse.error("INVALID_STATUS", e.getMessage()));
} catch (Exception e) {
logger.error("Error retrying transaction: {}", transactionId, e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error("INTERNAL_ERROR", "交易重试失败"));
}
}
private TransactionResponse convertToResponse(Transaction transaction) {
TransactionResponse response = new TransactionResponse();
response.setTransactionId(transaction.getTransactionId());
response.setCustomerId(transaction.getCustomerId());
response.setMerchantId(transaction.getMerchantId());
response.setStoreId(transaction.getStoreId());
response.setTransactionType(transaction.getTransactionType());
response.setChannelType(transaction.getChannelType());
response.setAmount(transaction.getAmount());
response.setStatus(transaction.getStatus());
response.setCreatedTime(transaction.getCreatedTime());
response.setUpdatedTime(transaction.getUpdatedTime());
response.setCompletedTime(transaction.getCompletedTime());
response.setOrderId(transaction.getOrderId());
response.setPaymentId(transaction.getPaymentId());
response.setInventoryReservationId(transaction.getInventoryReservationId());
// 转换交易商品项
if (transaction.getTransactionItems() != null) {
List<TransactionItemResponse> itemResponses = transaction.getTransactionItems().stream()
.map(this::convertToItemResponse)
.collect(Collectors.toList());
response.setTransactionItems(itemResponses);
}
return response;
}
private TransactionItemResponse convertToItemResponse(TransactionItem item) {
TransactionItemResponse response = new TransactionItemResponse();
response.setProductId(item.getProductId());
response.setProductName(item.getProductName());
response.setQuantity(item.getQuantity());
response.setUnitPrice(item.getUnitPrice());
response.setTotalPrice(item.getTotalPrice());
return response;
}
}
/**
* 通用API响应
*/
public class ApiResponse<T> {
private boolean success;
private String code;
private String message;
private T data;
private LocalDateTime timestamp;
public ApiResponse() {
this.timestamp = LocalDateTime.now();
}
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(true);
response.setCode("SUCCESS");
response.setMessage("操作成功");
response.setData(data);
return response;
}
public static <T> ApiResponse<T> error(String code, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setSuccess(false);
response.setCode(code);
response.setMessage(message);
return response;
}
// Getters and Setters
public boolean isSuccess() { return success; }
public void setSuccess(boolean success) { this.success = success; }
public String getCode() { return code; }
public void setCode(String code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
}
11.9 监控与告警
11.9.1 系统监控
交易系统的全面监控体系:
java
/**
* 交易监控服务
*/
@Service
public class TransactionMonitoringService {
private static final Logger logger = LoggerFactory.getLogger(TransactionMonitoringService.class);
private final MeterRegistry meterRegistry;
private final TransactionRepository transactionRepository;
private final AlertService alertService;
// 监控指标
private final Counter transactionCreatedCounter;
private final Counter transactionCompletedCounter;
private final Counter transactionFailedCounter;
private final Timer transactionProcessingTimer;
private final Gauge activeTransactionsGauge;
public TransactionMonitoringService(MeterRegistry meterRegistry,
TransactionRepository transactionRepository,
AlertService alertService) {
this.meterRegistry = meterRegistry;
this.transactionRepository = transactionRepository;
this.alertService = alertService;
// 初始化监控指标
this.transactionCreatedCounter = Counter.builder("transaction.created")
.description("Number of transactions created")
.register(meterRegistry);
this.transactionCompletedCounter = Counter.builder("transaction.completed")
.description("Number of transactions completed")
.register(meterRegistry);
this.transactionFailedCounter = Counter.builder("transaction.failed")
.description("Number of transactions failed")
.register(meterRegistry);
this.transactionProcessingTimer = Timer.builder("transaction.processing.time")
.description("Transaction processing time")
.register(meterRegistry);
this.activeTransactionsGauge = Gauge.builder("transaction.active")
.description("Number of active transactions")
.register(meterRegistry, this, TransactionMonitoringService::getActiveTransactionCount);
}
/**
* 记录交易创建
*/
public void recordTransactionCreated(Transaction transaction) {
transactionCreatedCounter.increment(
Tags.of(
"merchant_id", transaction.getMerchantId(),
"transaction_type", transaction.getTransactionType().name(),
"channel_type", transaction.getChannelType().name()
)
);
logger.debug("Recorded transaction created: {}", transaction.getTransactionId());
}
/**
* 记录交易完成
*/
public void recordTransactionCompleted(Transaction transaction, Duration processingTime) {
transactionCompletedCounter.increment(
Tags.of(
"merchant_id", transaction.getMerchantId(),
"transaction_type", transaction.getTransactionType().name(),
"channel_type", transaction.getChannelType().name()
)
);
transactionProcessingTimer.record(processingTime);
logger.debug("Recorded transaction completed: {}, processing time: {}ms",
transaction.getTransactionId(), processingTime.toMillis());
}
/**
* 记录交易失败
*/
public void recordTransactionFailed(Transaction transaction, String reason) {
transactionFailedCounter.increment(
Tags.of(
"merchant_id", transaction.getMerchantId(),
"transaction_type", transaction.getTransactionType().name(),
"channel_type", transaction.getChannelType().name(),
"failure_reason", reason
)
);
logger.debug("Recorded transaction failed: {}, reason: {}",
transaction.getTransactionId(), reason);
}
/**
* 获取活跃交易数量
*/
private double getActiveTransactionCount() {
try {
return transactionRepository.countByStatus(TransactionStatus.PENDING);
} catch (Exception e) {
logger.error("Error getting active transaction count", e);
return 0;
}
}
/**
* 获取交易统计信息
*/
public TransactionMonitoringStats getMonitoringStats() {
try {
LocalDateTime now = LocalDateTime.now();
LocalDateTime oneHourAgo = now.minusHours(1);
LocalDateTime oneDayAgo = now.minusDays(1);
TransactionMonitoringStats stats = new TransactionMonitoringStats();
// 最近1小时统计
stats.setLastHourCreated(transactionRepository.countByCreatedTimeBetween(oneHourAgo, now));
stats.setLastHourCompleted(transactionRepository.countByStatusAndCompletedTimeBetween(
TransactionStatus.COMPLETED, oneHourAgo, now));
stats.setLastHourFailed(transactionRepository.countByStatusAndUpdatedTimeBetween(
TransactionStatus.FAILED, oneHourAgo, now));
// 最近24小时统计
stats.setLastDayCreated(transactionRepository.countByCreatedTimeBetween(oneDayAgo, now));
stats.setLastDayCompleted(transactionRepository.countByStatusAndCompletedTimeBetween(
TransactionStatus.COMPLETED, oneDayAgo, now));
stats.setLastDayFailed(transactionRepository.countByStatusAndUpdatedTimeBetween(
TransactionStatus.FAILED, oneDayAgo, now));
// 当前活跃交易
stats.setActivePending(transactionRepository.countByStatus(TransactionStatus.PENDING));
stats.setActiveProcessing(transactionRepository.countByStatus(TransactionStatus.PROCESSING));
// 平均处理时间
stats.setAverageProcessingTime(calculateAverageProcessingTime(oneHourAgo, now));
// 成功率
long totalLastHour = stats.getLastHourCreated();
if (totalLastHour > 0) {
stats.setSuccessRate((double) stats.getLastHourCompleted() / totalLastHour * 100);
}
return stats;
} catch (Exception e) {
logger.error("Error getting monitoring stats", e);
return new TransactionMonitoringStats();
}
}
private Duration calculateAverageProcessingTime(LocalDateTime start, LocalDateTime end) {
try {
List<Transaction> completedTransactions = transactionRepository
.findByStatusAndCompletedTimeBetween(TransactionStatus.COMPLETED, start, end);
if (completedTransactions.isEmpty()) {
return Duration.ZERO;
}
long totalMillis = completedTransactions.stream()
.mapToLong(t -> Duration.between(t.getCreatedTime(), t.getCompletedTime()).toMillis())
.sum();
return Duration.ofMillis(totalMillis / completedTransactions.size());
} catch (Exception e) {
logger.error("Error calculating average processing time", e);
return Duration.ZERO;
}
}
/**
* 健康检查
*/
@Scheduled(fixedRate = 60000) // 每分钟检查一次
public void performHealthCheck() {
try {
TransactionMonitoringStats stats = getMonitoringStats();
// 检查成功率
if (stats.getSuccessRate() < 95.0) {
alertService.sendAlert(AlertLevel.WARNING,
"Transaction success rate is low: " + stats.getSuccessRate() + "%");
}
// 检查活跃交易数量
long activePending = stats.getActivePending();
if (activePending > 1000) {
alertService.sendAlert(AlertLevel.CRITICAL,
"Too many pending transactions: " + activePending);
}
// 检查平均处理时间
Duration avgProcessingTime = stats.getAverageProcessingTime();
if (avgProcessingTime.toSeconds() > 30) {
alertService.sendAlert(AlertLevel.WARNING,
"Average processing time is high: " + avgProcessingTime.toSeconds() + "s");
}
// 检查失败率
long totalLastHour = stats.getLastHourCreated();
if (totalLastHour > 0) {
double failureRate = (double) stats.getLastHourFailed() / totalLastHour * 100;
if (failureRate > 5.0) {
alertService.sendAlert(AlertLevel.WARNING,
"Transaction failure rate is high: " + failureRate + "%");
}
}
} catch (Exception e) {
logger.error("Error during health check", e);
alertService.sendAlert(AlertLevel.CRITICAL, "Health check failed: " + e.getMessage());
}
}
}
/**
* 交易监控统计
*/
public class TransactionMonitoringStats {
private long lastHourCreated;
private long lastHourCompleted;
private long lastHourFailed;
private long lastDayCreated;
private long lastDayCompleted;
private long lastDayFailed;
private long activePending;
private long activeProcessing;
private Duration averageProcessingTime;
private double successRate;
// Getters and Setters
public long getLastHourCreated() { return lastHourCreated; }
public void setLastHourCreated(long lastHourCreated) { this.lastHourCreated = lastHourCreated; }
public long getLastHourCompleted() { return lastHourCompleted; }
public void setLastHourCompleted(long lastHourCompleted) { this.lastHourCompleted = lastHourCompleted; }
public long getLastHourFailed() { return lastHourFailed; }
public void setLastHourFailed(long lastHourFailed) { this.lastHourFailed = lastHourFailed; }
public long getLastDayCreated() { return lastDayCreated; }
public void setLastDayCreated(long lastDayCreated) { this.lastDayCreated = lastDayCreated; }
public long getLastDayCompleted() { return lastDayCompleted; }
public void setLastDayCompleted(long lastDayCompleted) { this.lastDayCompleted = lastDayCompleted; }
public long getLastDayFailed() { return lastDayFailed; }
public void setLastDayFailed(long lastDayFailed) { this.lastDayFailed = lastDayFailed; }
public long getActivePending() { return activePending; }
public void setActivePending(long activePending) { this.activePending = activePending; }
public long getActiveProcessing() { return activeProcessing; }
public void setActiveProcessing(long activeProcessing) { this.activeProcessing = activeProcessing; }
public Duration getAverageProcessingTime() { return averageProcessingTime; }
public void setAverageProcessingTime(Duration averageProcessingTime) { this.averageProcessingTime = averageProcessingTime; }
public double getSuccessRate() { return successRate; }
public void setSuccessRate(double successRate) { this.successRate = successRate; }
}
11.9.2 告警系统
智能告警机制:
java
/**
* 告警服务
*/
@Service
public class AlertService {
private static final Logger logger = LoggerFactory.getLogger(AlertService.class);
private final NotificationService notificationService;
private final AlertConfigRepository alertConfigRepository;
// 告警抑制缓存(防止重复告警)
private final Map<String, LocalDateTime> alertSuppressionCache = new ConcurrentHashMap<>();
private static final Duration ALERT_SUPPRESSION_DURATION = Duration.ofMinutes(15);
public AlertService(NotificationService notificationService,
AlertConfigRepository alertConfigRepository) {
this.notificationService = notificationService;
this.alertConfigRepository = alertConfigRepository;
}
/**
* 发送告警
*/
public void sendAlert(AlertLevel level, String message) {
try {
String alertKey = generateAlertKey(level, message);
// 检查告警抑制
if (isAlertSuppressed(alertKey)) {
logger.debug("Alert suppressed: {}", message);
return;
}
Alert alert = new Alert();
alert.setAlertId(UUID.randomUUID().toString());
alert.setLevel(level);
alert.setMessage(message);
alert.setTimestamp(LocalDateTime.now());
alert.setSource("TransactionSystem");
// 根据告警级别选择通知方式
List<String> recipients = getAlertRecipients(level);
for (String recipient : recipients) {
switch (level) {
case CRITICAL:
// 关键告警:短信 + 邮件 + 钉钉
notificationService.sendSms(recipient, formatAlertMessage(alert));
notificationService.sendEmail(recipient, "Critical Alert", formatAlertMessage(alert));
notificationService.sendDingTalk(formatAlertMessage(alert));
break;
case WARNING:
// 警告告警:邮件 + 钉钉
notificationService.sendEmail(recipient, "Warning Alert", formatAlertMessage(alert));
notificationService.sendDingTalk(formatAlertMessage(alert));
break;
case INFO:
// 信息告警:邮件
notificationService.sendEmail(recipient, "Info Alert", formatAlertMessage(alert));
break;
}
}
// 记录告警抑制
alertSuppressionCache.put(alertKey, LocalDateTime.now());
logger.info("Alert sent: level={}, message={}", level, message);
} catch (Exception e) {
logger.error("Error sending alert", e);
}
}
/**
* 发送交易相关告警
*/
public void sendTransactionAlert(String transactionId, AlertLevel level, String message) {
try {
Alert alert = new Alert();
alert.setAlertId(UUID.randomUUID().toString());
alert.setLevel(level);
alert.setMessage(message);
alert.setTimestamp(LocalDateTime.now());
alert.setSource("TransactionSystem");
alert.setTransactionId(transactionId);
String fullMessage = String.format("Transaction Alert [%s]: %s", transactionId, message);
sendAlert(level, fullMessage);
} catch (Exception e) {
logger.error("Error sending transaction alert for: {}", transactionId, e);
}
}
/**
* 批量告警
*/
public void sendBatchAlert(List<String> transactionIds, AlertLevel level, String message) {
try {
if (transactionIds.size() > 10) {
// 如果交易数量过多,发送汇总告警
String summaryMessage = String.format("%s (affected %d transactions)",
message, transactionIds.size());
sendAlert(level, summaryMessage);
} else {
// 否则发送详细告警
String detailMessage = String.format("%s (transactions: %s)",
message, String.join(", ", transactionIds));
sendAlert(level, detailMessage);
}
} catch (Exception e) {
logger.error("Error sending batch alert", e);
}
}
private String generateAlertKey(AlertLevel level, String message) {
return level.name() + ":" + message.hashCode();
}
private boolean isAlertSuppressed(String alertKey) {
LocalDateTime lastAlertTime = alertSuppressionCache.get(alertKey);
if (lastAlertTime == null) {
return false;
}
return Duration.between(lastAlertTime, LocalDateTime.now()).compareTo(ALERT_SUPPRESSION_DURATION) < 0;
}
private List<String> getAlertRecipients(AlertLevel level) {
try {
AlertConfig config = alertConfigRepository.findByLevel(level);
return config != null ? config.getRecipients() : getDefaultRecipients();
} catch (Exception e) {
logger.error("Error getting alert recipients", e);
return getDefaultRecipients();
}
}
private List<String> getDefaultRecipients() {
return Arrays.asList("admin@company.com", "ops@company.com");
}
private String formatAlertMessage(Alert alert) {
return String.format("[%s] %s\nTime: %s\nSource: %s",
alert.getLevel(),
alert.getMessage(),
alert.getTimestamp().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME),
alert.getSource());
}
/**
* 清理过期的告警抑制缓存
*/
@Scheduled(fixedRate = 300000) // 每5分钟清理一次
public void cleanupSuppressionCache() {
try {
LocalDateTime cutoff = LocalDateTime.now().minus(ALERT_SUPPRESSION_DURATION);
alertSuppressionCache.entrySet().removeIf(entry ->
entry.getValue().isBefore(cutoff));
logger.debug("Cleaned up alert suppression cache, remaining entries: {}",
alertSuppressionCache.size());
} catch (Exception e) {
logger.error("Error cleaning up suppression cache", e);
}
}
}
/**
* 告警实体
*/
public class Alert {
private String alertId;
private AlertLevel level;
private String message;
private LocalDateTime timestamp;
private String source;
private String transactionId;
// Getters and Setters
public String getAlertId() { return alertId; }
public void setAlertId(String alertId) { this.alertId = alertId; }
public AlertLevel getLevel() { return level; }
public void setLevel(AlertLevel level) { this.level = level; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public String getSource() { return source; }
public void setSource(String source) { this.source = source; }
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
}
/**
* 告警级别
*/
public enum AlertLevel {
INFO,
WARNING,
CRITICAL
}
/**
* 告警配置
*/
public class AlertConfig {
private AlertLevel level;
private List<String> recipients;
private boolean smsEnabled;
private boolean emailEnabled;
private boolean dingTalkEnabled;
// Getters and Setters
public AlertLevel getLevel() { return level; }
public void setLevel(AlertLevel level) { this.level = level; }
public List<String> getRecipients() { return recipients; }
public void setRecipients(List<String> recipients) { this.recipients = recipients; }
public boolean isSmsEnabled() { return smsEnabled; }
public void setSmsEnabled(boolean smsEnabled) { this.smsEnabled = smsEnabled; }
public boolean isEmailEnabled() { return emailEnabled; }
public void setEmailEnabled(boolean emailEnabled) { this.emailEnabled = emailEnabled; }
public boolean isDingTalkEnabled() { return dingTalkEnabled; }
public void setDingTalkEnabled(boolean dingTalkEnabled) { this.dingTalkEnabled = dingTalkEnabled; }
}
11.10 安全防护
11.10.1 数据加密
敏感数据加密保护:
java
/**
* 数据加密服务
*/
@Service
public class EncryptionService {
private static final Logger logger = LoggerFactory.getLogger(EncryptionService.class);
private final AESUtil aesUtil;
private final RSAUtil rsaUtil;
@Value("${encryption.aes.key}")
private String aesKey;
@Value("${encryption.rsa.public-key}")
private String rsaPublicKey;
@Value("${encryption.rsa.private-key}")
private String rsaPrivateKey;
public EncryptionService(AESUtil aesUtil, RSAUtil rsaUtil) {
this.aesUtil = aesUtil;
this.rsaUtil = rsaUtil;
}
/**
* 加密敏感交易数据
*/
public EncryptedTransactionData encryptTransactionData(Transaction transaction) {
try {
EncryptedTransactionData encryptedData = new EncryptedTransactionData();
// 加密客户ID
encryptedData.setEncryptedCustomerId(aesUtil.encrypt(transaction.getCustomerId(), aesKey));
// 加密支付信息(如果存在)
if (transaction.getPaymentResult() != null) {
String paymentInfo = objectMapper.writeValueAsString(transaction.getPaymentResult());
encryptedData.setEncryptedPaymentInfo(aesUtil.encrypt(paymentInfo, aesKey));
}
// 加密元数据(如果包含敏感信息)
if (transaction.getMetadata() != null && !transaction.getMetadata().isEmpty()) {
String metadata = objectMapper.writeValueAsString(transaction.getMetadata());
encryptedData.setEncryptedMetadata(aesUtil.encrypt(metadata, aesKey));
}
encryptedData.setTransactionId(transaction.getTransactionId());
encryptedData.setEncryptionTimestamp(LocalDateTime.now());
return encryptedData;
} catch (Exception e) {
logger.error("Error encrypting transaction data: {}", transaction.getTransactionId(), e);
throw new EncryptionException("Failed to encrypt transaction data", e);
}
}
/**
* 解密交易数据
*/
public Transaction decryptTransactionData(EncryptedTransactionData encryptedData, Transaction transaction) {
try {
// 解密客户ID
if (encryptedData.getEncryptedCustomerId() != null) {
String customerId = aesUtil.decrypt(encryptedData.getEncryptedCustomerId(), aesKey);
transaction.setCustomerId(customerId);
}
// 解密支付信息
if (encryptedData.getEncryptedPaymentInfo() != null) {
String paymentInfo = aesUtil.decrypt(encryptedData.getEncryptedPaymentInfo(), aesKey);
PaymentResult paymentResult = objectMapper.readValue(paymentInfo, PaymentResult.class);
transaction.setPaymentResult(paymentResult);
}
// 解密元数据
if (encryptedData.getEncryptedMetadata() != null) {
String metadata = aesUtil.decrypt(encryptedData.getEncryptedMetadata(), aesKey);
Map<String, Object> metadataMap = objectMapper.readValue(metadata,
new TypeReference<Map<String, Object>>() {});
transaction.setMetadata(metadataMap);
}
return transaction;
} catch (Exception e) {
logger.error("Error decrypting transaction data: {}", encryptedData.getTransactionId(), e);
throw new DecryptionException("Failed to decrypt transaction data", e);
}
}
/**
* 加密支付凭证
*/
public String encryptPaymentCredentials(String credentials) {
try {
return rsaUtil.encrypt(credentials, rsaPublicKey);
} catch (Exception e) {
logger.error("Error encrypting payment credentials", e);
throw new EncryptionException("Failed to encrypt payment credentials", e);
}
}
/**
* 解密支付凭证
*/
public String decryptPaymentCredentials(String encryptedCredentials) {
try {
return rsaUtil.decrypt(encryptedCredentials, rsaPrivateKey);
} catch (Exception e) {
logger.error("Error decrypting payment credentials", e);
throw new DecryptionException("Failed to decrypt payment credentials", e);
}
}
/**
* 生成数据摘要
*/
public String generateDataDigest(String data) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
} catch (Exception e) {
logger.error("Error generating data digest", e);
throw new SecurityException("Failed to generate data digest", e);
}
}
/**
* 验证数据完整性
*/
public boolean verifyDataIntegrity(String data, String expectedDigest) {
try {
String actualDigest = generateDataDigest(data);
return actualDigest.equals(expectedDigest);
} catch (Exception e) {
logger.error("Error verifying data integrity", e);
return false;
}
}
}
/**
* 加密的交易数据
*/
public class EncryptedTransactionData {
private String transactionId;
private String encryptedCustomerId;
private String encryptedPaymentInfo;
private String encryptedMetadata;
private LocalDateTime encryptionTimestamp;
// Getters and Setters
public String getTransactionId() { return transactionId; }
public void setTransactionId(String transactionId) { this.transactionId = transactionId; }
public String getEncryptedCustomerId() { return encryptedCustomerId; }
public void setEncryptedCustomerId(String encryptedCustomerId) { this.encryptedCustomerId = encryptedCustomerId; }
public String getEncryptedPaymentInfo() { return encryptedPaymentInfo; }
public void setEncryptedPaymentInfo(String encryptedPaymentInfo) { this.encryptedPaymentInfo = encryptedPaymentInfo; }
public String getEncryptedMetadata() { return encryptedMetadata; }
public void setEncryptedMetadata(String encryptedMetadata) { this.encryptedMetadata = encryptedMetadata; }
public LocalDateTime getEncryptionTimestamp() { return encryptionTimestamp; }
public void setEncryptionTimestamp(LocalDateTime encryptionTimestamp) { this.encryptionTimestamp = encryptionTimestamp; }
}
11.10.2 访问控制
基于角色的访问控制:
java
/**
* 交易访问控制服务
*/
@Service
public class TransactionAccessControlService {
private static final Logger logger = LoggerFactory.getLogger(TransactionAccessControlService.class);
private final UserService userService;
private final RoleService roleService;
public TransactionAccessControlService(UserService userService, RoleService roleService) {
this.userService = userService;
this.roleService = roleService;
}
/**
* 检查交易访问权限
*/
public boolean hasTransactionAccess(String userId, String transactionId, TransactionOperation operation) {
try {
User user = userService.getUser(userId);
if (user == null) {
logger.warn("User not found: {}", userId);
return false;
}
// 检查用户角色权限
List<Role> userRoles = roleService.getUserRoles(userId);
for (Role role : userRoles) {
if (hasRolePermission(role, operation)) {
logger.debug("Access granted for user {} with role {} to perform {} on transaction {}",
userId, role.getName(), operation, transactionId);
return true;
}
}
// 检查资源级权限(如用户只能访问自己的交易)
if (hasResourcePermission(user, transactionId, operation)) {
logger.debug("Resource-level access granted for user {} to perform {} on transaction {}",
userId, operation, transactionId);
return true;
}
logger.warn("Access denied for user {} to perform {} on transaction {}",
userId, operation, transactionId);
return false;
} catch (Exception e) {
logger.error("Error checking transaction access for user: {}, transaction: {}",
userId, transactionId, e);
return false;
}
}
/**
* 检查批量操作权限
*/
public boolean hasBatchOperationAccess(String userId, List<String> transactionIds, TransactionOperation operation) {
try {
User user = userService.getUser(userId);
if (user == null) {
return false;
}
// 检查是否有批量操作权限
List<Role> userRoles = roleService.getUserRoles(userId);
boolean hasBatchPermission = userRoles.stream()
.anyMatch(role -> role.getPermissions().contains("BATCH_" + operation.name()));
if (!hasBatchPermission) {
logger.warn("User {} does not have batch operation permission for {}", userId, operation);
return false;
}
// 检查每个交易的访问权限
for (String transactionId : transactionIds) {
if (!hasTransactionAccess(userId, transactionId, operation)) {
return false;
}
}
return true;
} catch (Exception e) {
logger.error("Error checking batch operation access for user: {}", userId, e);
return false;
}
}
/**
* 检查商户访问权限
*/
public boolean hasMerchantAccess(String userId, String merchantId) {
try {
User user = userService.getUser(userId);
if (user == null) {
return false;
}
// 超级管理员可以访问所有商户
if (user.isSuperAdmin()) {
return true;
}
// 检查用户是否属于该商户
return user.getMerchantIds().contains(merchantId);
} catch (Exception e) {
logger.error("Error checking merchant access for user: {}, merchant: {}", userId, merchantId, e);
return false;
}
}
private boolean hasRolePermission(Role role, TransactionOperation operation) {
return role.getPermissions().contains(operation.getPermission());
}
private boolean hasResourcePermission(User user, String transactionId, TransactionOperation operation) {
// 实现资源级权限检查逻辑
// 例如:用户只能查看自己创建的交易
if (operation == TransactionOperation.VIEW) {
// 这里可以查询交易的创建者是否为当前用户
return true; // 简化实现
}
return false;
}
/**
* 记录访问日志
*/
public void logAccess(String userId, String transactionId, TransactionOperation operation, boolean granted) {
try {
AccessLog accessLog = new AccessLog();
accessLog.setUserId(userId);
accessLog.setResourceType("TRANSACTION");
accessLog.setResourceId(transactionId);
accessLog.setOperation(operation.name());
accessLog.setAccessGranted(granted);
accessLog.setTimestamp(LocalDateTime.now());
accessLog.setIpAddress(getCurrentUserIpAddress());
// 保存访问日志
saveAccessLog(accessLog);
} catch (Exception e) {
logger.error("Error logging access", e);
}
}
private String getCurrentUserIpAddress() {
// 获取当前用户IP地址的实现
return "127.0.0.1"; // 简化实现
}
private void saveAccessLog(AccessLog accessLog) {
// 保存访问日志的实现
logger.info("Access log: {}", accessLog);
}
}
/**
* 交易操作枚举
*/
public enum TransactionOperation {
VIEW("TRANSACTION_VIEW"),
CREATE("TRANSACTION_CREATE"),
UPDATE("TRANSACTION_UPDATE"),
DELETE("TRANSACTION_DELETE"),
PROCESS("TRANSACTION_PROCESS"),
CANCEL("TRANSACTION_CANCEL"),
REFUND("TRANSACTION_REFUND"),
RETRY("TRANSACTION_RETRY");
private final String permission;
TransactionOperation(String permission) {
this.permission = permission;
}
public String getPermission() {
return permission;
}
}
/**
* 访问日志
*/
public class AccessLog {
private String userId;
private String resourceType;
private String resourceId;
private String operation;
private boolean accessGranted;
private LocalDateTime timestamp;
private String ipAddress;
// Getters and Setters
public String getUserId() { return userId; }
public void setUserId(String userId) { this.userId = userId; }
public String getResourceType() { return resourceType; }
public void setResourceType(String resourceType) { this.resourceType = resourceType; }
public String getResourceId() { return resourceId; }
public void setResourceId(String resourceId) { this.resourceId = resourceId; }
public String getOperation() { return operation; }
public void setOperation(String operation) { this.operation = operation; }
public boolean isAccessGranted() { return accessGranted; }
public void setAccessGranted(boolean accessGranted) { this.accessGranted = accessGranted; }
public LocalDateTime getTimestamp() { return timestamp; }
public void setTimestamp(LocalDateTime timestamp) { this.timestamp = timestamp; }
public String getIpAddress() { return ipAddress; }
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
@Override
public String toString() {
return String.format("AccessLog{userId='%s', resourceType='%s', resourceId='%s', operation='%s', accessGranted=%s, timestamp=%s, ipAddress='%s'}",
userId, resourceType, resourceId, operation, accessGranted, timestamp, ipAddress);
}
}
11.11 总结
线上/线下交易系统是SaaS平台的核心组件,承载着企业的核心业务流程。本章详细阐述了交易系统的设计与实现,主要包括以下几个方面:
11.11.1 系统架构特点
- 微服务架构:采用微服务架构设计,实现了服务的解耦和独立部署
- 分层设计:清晰的分层架构,包括客户端层、网关层、业务服务层、基础服务层和数据层
- 高可用性:通过负载均衡、故障转移、熔断降级等机制保证系统高可用
- 数据一致性:采用分布式事务、事件驱动等方式保证数据一致性
11.11.2 核心功能实现
- 交易流程管理:完整的交易生命周期管理,包括创建、处理、完成、取消等状态
- 支付系统集成:支持多种支付方式,提供统一的支付接口和处理流程
- 风控系统:多维度风险评估,包括金额风险、频率风险、黑名单检查等
- 状态机管理:基于状态机的交易状态管理,确保状态转换的正确性
11.11.3 技术特点
- 异步处理:大量使用异步处理提升系统性能和用户体验
- 缓存优化:多层缓存策略,包括应用缓存、分布式缓存等
- 数据库优化:索引优化、分区策略、读写分离等数据库优化手段
- 监控告警:全面的监控体系和智能告警机制
11.11.4 安全保障
- 数据加密:敏感数据的加密存储和传输
- 访问控制:基于角色的访问控制和资源级权限管理
- 审计日志:完整的操作审计和访问日志记录
- 风险防控:多层次的风险防控机制
11.11.5 实践建议
- 渐进式架构:从单体架构逐步演进到微服务架构
- 性能优化:持续进行性能监控和优化
- 安全第一:将安全考虑贯穿整个系统设计和实现过程
- 可观测性:建立完善的监控、日志和链路追踪体系
- 容灾备份:制定完善的容灾备份和恢复策略
11.11.6 发展趋势
- 云原生:向云原生架构演进,利用容器化、服务网格等技术
- AI赋能:利用人工智能技术提升风控能力和用户体验
- 实时处理:更多实时数据处理和分析能力
- 跨境支付:支持更多跨境支付场景和合规要求
- 开放生态:构建开放的支付生态,支持更多第三方集成
通过本章的学习,读者可以全面了解线上/线下交易系统的设计思路和实现方法,为构建高性能、高可用、安全可靠的交易系统提供参考和指导。