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.
|
*
|
* <p>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));
|
}
|
|
/**
|
* 把一笔业务仍未结的问题记为已解决。
|
*
|
* <p>唯一键是 {@code (issue_type, provider, business_no, status)},而同一笔业务会被「解掉、又出现、
|
* 再解掉」(对账每一轮都可能把同一条问题关掉再开出来)。直接把 open 行改成 resolved 会撞上已经存在
|
* 的那条 resolved 行,所以先把上一条结论行清掉再落新结论。删除是有条件的:只有当确实存在一条 open 行
|
* (也就是真的要有新结论)时才动它,人工写下的结论不会因为一次无关的对账轮次被抹掉。
|
*/
|
public void resolve(String type, String businessNo, String by) {
|
List<Long> superseded = jdbc.queryForList("""
|
select r.id from payment_reconciliation_issue r
|
where r.issue_type = ? and r.provider = ? and r.business_no = ? and r.status = 'resolved'
|
and exists (select 1 from payment_reconciliation_issue open_issue
|
where open_issue.issue_type = r.issue_type and open_issue.provider = r.provider
|
and open_issue.business_no = r.business_no and open_issue.status = 'open')
|
""", Long.class, type, PROVIDER, businessNo);
|
for (Long id : superseded) jdbc.update("delete from payment_reconciliation_issue where id = ?", id);
|
jdbc.update("update payment_reconciliation_issue set status = 'resolved', resolved_at = now(), resolved_by = ? where issue_type = ? and provider = ? and business_no = ? and status = 'open'", by, type, PROVIDER, businessNo);
|
}
|
|
private String emptyToNull(String value) { return value == null || value.isBlank() ? null : value; }
|
|
private String truncate(String value) { if (value == null) return ""; return value.length() > 500 ? value.substring(0, 500) : value; }
|
}
|