支付记录保存异常解决方案

📅 2026/8/10 14:33:05
支付记录保存异常解决方案
根据错误日志我发现了两个关键问题问题分析问题1:payment_method字段数据被截断 - 数据库字段长度不足或传入了超长值问题2:payment_message_record表缺少error_message字段解决方案方案一修改代码处理临时方案修改DamageAssessmentReportServiceImpl.java中的savePaymentRecord方法限制payment_method长度/** * 保存支付记录 */ private void savePaymentRecord(Long reportId, PaymentSuccessMessageDTO paymentMessage, String paymentStatus) { try { // 获取报告信息 MapString, Object reportInfo damageReportPaymentMapper.selectReportBasicInfo(reportId); if (reportInfo null) { throw new RuntimeException(报告不存在); } DamageReportPayment payment new DamageReportPayment(); payment.setReportId(reportId); payment.setPaymentType(STORE_MANAGER); // 默认店长支付 payment.setPayerName((String) reportInfo.get(customer_id)); // 使用客户ID作为支付方名称 payment.setPayerContact(paymentMessage.getMicroNo()); payment.setPaymentAmount(paymentMessage.getPayAmount()); // 限制 payment_method 字段长度假设数据库为 VARCHAR(20) String payType paymentMessage.getPayType(); if (payType ! null payType.length() 20) { payType payType.substring(0, 20); log.warn(支付方式过长被截断原值: {}, 截断后: {}, paymentMessage.getPayType(), payType); } payment.setPaymentMethod(payType); payment.setTransactionNo(paymentMessage.getTransactionNo()); payment.setPaymentProofUrl(paymentMessage.getPaymentProofUrl()); // 转换 LocalDateTime 为 Date if (paymentMessage.getPayTime() ! null) { payment.setPaymentTime(java.sql.Timestamp.valueOf(paymentMessage.getPayTime())); } else { payment.setPaymentTime(new java.util.Date()); } payment.setPaymentStatus(paymentStatus); payment.setConfirmedBy(系统自动); payment.setConfirmedTime(new java.util.Date()); payment.setCreateTime(new java.util.Date()); payment.setRemark(支付单号: paymentMessage.getMchPayNo()); damageReportPaymentMapper.insert(payment); log.info(保存支付记录成功支付ID: {}, 报告ID: {}, payment.getPaymentId(), reportId); } catch (Exception e) { log.error(保存支付记录失败报告ID: {}, reportId, e); throw new RuntimeException(保存支付记录失败: e.getMessage()); } }方案二修改 PaymentMessageAckService 处理异常记录修改PaymentMessageAckService.java中的recordMessageFailure方法/** * 记录消息处理失败 */ Transactional(rollbackFor Exception.class) public void recordMessageFailure(String messageId, String mchPayNo, String error) { if (messageId null) { return; } try { PaymentMessageRecord record messageRecordMapper.selectByMessageId(messageId); if (record ! null) { record.setStatus(FAILED); // 检查是否有 error_message 字段没有则记录到 remark try { record.setErrorMessage(error); } catch (Exception e) { // 如果实体类没有 errorMessage 字段记录到日志 log.warn(PaymentMessageRecord 实体没有 errorMessage 字段错误信息: {}, error); record.setRemark(错误: (error ! null error.length() 100 ? error.substring(0, 100) : error)); } record.setRetryCount(record.getRetryCount() 1); record.setUpdateTime(LocalDateTime.now()); messageRecordMapper.updateById(record); } else { // 创建新记录 record new PaymentMessageRecord(); record.setMessageId(messageId); record.setMchPayNo(mchPayNo); record.setStatus(FAILED); try { record.setErrorMessage(error); } catch (Exception e) { // 如果实体类没有 errorMessage 字段记录到 remark record.setRemark(错误: (error ! null error.length() 100 ? error.substring(0, 100) : error)); } record.setRetryCount(1); record.setCreateTime(LocalDateTime.now()); record.setUpdateTime(LocalDateTime.now()); messageRecordMapper.insert(record); } log.warn(消息处理失败记录消息ID: {}, 错误: {}, messageId, error); } catch (Exception e) { log.error(记录消息处理失败信息异常消息ID: {}, messageId, e); // 仅记录到日志不抛出异常 } finally { // 清理处理中的状态 String processingKey MESSAGE_PROCESSING_KEY messageId; redisTemplate.delete(processingKey); // 清理分布式锁 String lockKey MESSAGE_LOCK_KEY messageId; redisTemplate.delete(lockKey); } }方案三更新 PaymentMessageRecord 实体类推荐如果PaymentMessageRecord实体类缺少errorMessage字段需要添加// 在 PaymentMessageRecord 实体类中添加 public class PaymentMessageRecord { // ... 其他字段 private String errorMessage; // 错误信息 // getter 和 setter public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage errorMessage; } }方案四数据库表结构调整根治方案创建或修改表的SQL语句-- 1. 修改 damage_report_payment 表的 payment_method 字段长度如果太短 ALTER TABLE damage_report_payment MODIFY COLUMN payment_method VARCHAR(50) DEFAULT NULL COMMENT 支付方式; -- 2. 检查并添加 payment_message_record 表的 error_message 字段 ALTER TABLE payment_message_record ADD COLUMN error_message VARCHAR(500) DEFAULT NULL COMMENT 错误信息;快速修复方案对于紧急情况先修复PaymentSuccessConsumer.java中的错误处理/** * 监听支付成功消息 - 主要处理逻辑 */ RabbitListener( queues RabbitMQConfig.PAYMENT_SUCCESS_QUEUE, containerFactory rabbitListenerContainerFactory ) Transactional(rollbackFor Exception.class) public void handlePaymentSuccess(Message message) { String messageId null; String mchPayNo null; Long reportId null; try { // ... 原有代码 } catch (Exception e) { log.error(处理支付成功消息失败消息ID: {}, 支付单号: {}, 报告ID: {}, messageId, mchPayNo, reportId, e); // 简化错误处理避免二次异常 try { if (messageId ! null) { // 仅记录到日志不调用可能失败的方法 log.error(消息处理失败消息ID: {}, 支付单号: {}, 错误: {}, messageId, mchPayNo, e.getMessage()); } } catch (Exception ex) { log.error(记录失败信息异常, ex); } // 拒绝消息让消息重试 nackMessage(message); } }建议操作步骤立即实施先采用方案一限制字段长度和简化错误处理方案短期修复实施方案三更新实体类和方案四修改表结构长期优化检查所有数据库字段长度是否足够添加数据验证逻辑在插入前检查字段长度完善异常处理机制避免因记录失败导致主流程中断这样可以快速解决当前的数据库插入异常问题保证支付流程能正常进行。根据错误日志我们可以看到两个主要问题第一个错误是保存支付记录时payment_method字段数据被截断。这通常是因为数据库表中该字段的长度不足以存储我们传入的值。第二个错误是在记录消息处理失败时payment_message_record表中没有error_message字段。针对这两个问题我们需要进行以下修复问题1: payment_method字段数据被截断在DamageAssessmentReportServiceImpl的savePaymentRecord方法中我们设置payment_method字段的值为paymentMessage.getPayType()。我们需要检查这个值的长度并确保它不超过数据库表中payment_method字段的定义长度。但是更可能的原因是数据库表中payment_method字段的长度定义得太短比如可能是VARCHAR(10)而传入的值可能超过10个字符。我们可以通过两种方式解决在代码中截断字符串确保不超过数据库字段长度。修改数据库表结构增加字段长度。考虑到这是一个生产环境我们首先检查数据库中damage_report_payment表的payment_method字段长度如果过短需要修改表结构。但是如果无法立即修改表结构我们可以先在代码中截断。假设数据库字段长度为50我们可以这样处理String paymentMethod paymentMessage.getPayType(); if (paymentMethod ! null paymentMethod.length() 50) { paymentMethod paymentMethod.substring(0, 50); } payment.setPaymentMethod(paymentMethod);但是从错误日志中我们无法直接知道字段长度所以我们需要先确认数据库表结构。如果无法确认我们可以先设置为一个合理的截断长度比如50。问题2: payment_message_record表缺少error_message字段在PaymentMessageAckService的recordMessageFailure方法中我们尝试更新error_message字段但是数据库表中没有这个字段。我们需要检查payment_message_record表的结构并添加这个字段或者修改代码不更新这个字段。但是从代码逻辑来看我们需要记录错误信息所以应该添加这个字段。如果暂时不能修改表结构我们可以先注释掉设置error_message的代码但这会导致错误信息无法记录。因此建议的解决方案是修改表结构添加error_message字段。如果无法立即修改我们可以先修改代码将错误信息记录到其他字段比如remark或者先不记录。由于我们无法直接修改数据库这里先提供代码层面的修复对于问题1在DamageAssessmentReportServiceImpl的savePaymentRecord方法中截断payment_method字段的值。同时为了安全起见也对其他可能超长的字段进行截断。对于问题2在PaymentMessageAckService的recordMessageFailure方法中我们暂时不设置error_message或者将错误信息设置到已有的字段中。但是由于错误日志显示payment_message_record表没有error_message字段我们考虑先注释掉这行代码避免更新失败。同时我们可以将错误信息记录到日志中。具体修改步骤修改1截断payment_method字段在DamageAssessmentReportServiceImpl.java的savePaymentRecord方法中找到设置paymentMethod的地方进行截断。假设数据库字段长度为50我们修改如下// 截断payment_method防止数据库字段长度不够 String paymentMethod paymentMessage.getPayType(); if (paymentMethod ! null paymentMethod.length() 50) { paymentMethod paymentMethod.substring(0, 50); } payment.setPaymentMethod(paymentMethod);修改2避免更新不存在的error_message字段在PaymentMessageAckService.java的recordMessageFailure方法中我们暂时注释掉设置error_message的代码或者将其设置到其他字段例如remark。由于我们无法知道表结构这里先注释掉并记录日志。原代码record.setErrorMessage(error);修改为// 暂时注释掉因为表结构中没有error_message字段 // record.setErrorMessage(error); // 将错误信息记录到日志中 log.error(消息处理失败消息ID: {}, 错误: {}, messageId, error);但是注意在PaymentMessageRecord实体类中可能没有errorMessage字段所以我们需要检查实体类。如果实体类没有该字段那么MyBatis在更新时就会报错。因此我们还需要检查实体类PaymentMessageRecord是否有errorMessage字段。如果没有我们需要在实体类中添加该字段并配置MyBatis映射。如果暂时不能修改实体类那么只能注释掉这行代码。根据错误日志我们暂时注释掉这行代码并记录错误信息到日志。另外我们还需要注意在DamageAssessmentReportServiceImpl的savePaymentRecord方法中我们使用了damageReportPaymentMapper.insert(payment)而DamageReportPaymentMapper.xml中有一个insert语句这个语句包含了payment_method字段。所以我们需要确保传入的payment_method值不超过数据库字段长度。总结修改在DamageAssessmentReportServiceImpl的savePaymentRecord方法中截断payment_method字符串。在PaymentMessageAckService的recordMessageFailure方法中注释掉record.setErrorMessage(error);并记录错误日志。由于我们无法修改数据库只能先通过代码避免错误。如果数据库字段长度不够建议后续修改数据库表结构。现在我们按照上述修改方案给出具体的代码修改。