package com.talentcard.server.business.payment; import java.util.List; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** * Ledger for money facts that disagree with local state. * *
A callback or a provider query may describe a payment that local business rules refuse to * accept: another merchant, another amount, another order, a cancelled order or a payment whose * merchant binding is missing. Those cases must never be silently dropped and never auto-fulfil the * order, so every one of them is recorded as an open row an administrator can inspect. */ @Service public class PaymentIssueRecorder { static final String PROVIDER = "HUIFU"; private final JdbcTemplate jdbc; public PaymentIssueRecorder(JdbcTemplate jdbc) { this.jdbc = jdbc; } public void issue(String type, String businessNo, String orderNo, String expected, String actual, long amount, String message) { jdbc.update(""" insert into payment_reconciliation_issue(issue_type, provider, business_no, order_no, expected_status, actual_status, amount, detail_message) values (?, ?, ?, ?, ?, ?, ?, ?) on duplicate key update detected_at = now(), expected_status = values(expected_status), actual_status = values(actual_status), amount = values(amount), detail_message = values(detail_message) """, type, PROVIDER, businessNo, emptyToNull(orderNo), emptyToNull(expected), emptyToNull(actual), amount, truncate(message)); } /** * 把一笔业务仍未结的问题记为已解决。 * *
唯一键是 {@code (issue_type, provider, business_no, status)},而同一笔业务会被「解掉、又出现、
* 再解掉」(对账每一轮都可能把同一条问题关掉再开出来)。直接把 open 行改成 resolved 会撞上已经存在
* 的那条 resolved 行,所以先把上一条结论行清掉再落新结论。删除是有条件的:只有当确实存在一条 open 行
* (也就是真的要有新结论)时才动它,人工写下的结论不会因为一次无关的对账轮次被抹掉。
*/
public void resolve(String type, String businessNo, String by) {
List