1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
 
/**
 * 一次性只读探针:定位 2026-09-24 那笔支付创建 409 的通道原始回执。
 *
 * <p>只跑 select,不写库。输出同时打到 stdout 与 UTF-8 文件(Git Bash 控制台编码不可靠)。
 */
public class Payment409Probe {
 
    private static final String[] DATABASES = {
            "talent_card2", "talent_card", "talent_card_verify1", "talent_card_dryrun"
    };
 
    public static void main(String[] args) throws Exception {
        Path out = Path.of(args.length > 0 ? args[0] : ".scratch/payment-409-probe/out.txt");
        Files.createDirectories(out.getParent());
        StringBuilder text = new StringBuilder();
        String base = "jdbc:mysql://127.0.0.1:8134/?useUnicode=true&characterEncoding=utf8"
                + "&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&useSSL=false";
        try (Connection connection = DriverManager.getConnection(base, "root", "123456")) {
            for (String database : DATABASES) {
                section(text, "===== " + database + " =====");
                try (Statement statement = connection.createStatement()) {
                    statement.execute("use `" + database + "`");
                } catch (SQLException exception) {
                    text.append("数据库不可用:").append(exception.getMessage()).append('\n');
                    continue;
                }
                dump(text, connection, "最近的支付流水",
                        "select payment_no, order_no, amount, status, provider_huifu_id, "
                                + "provider_response_code, provider_response_message, created_at, paid_at "
                                + "from payment_transaction order by id desc limit 15");
                dump(text, connection, "今天创建的支付流水(金额/状态/回执)",
                        "select payment_no, order_no, amount, status, provider_response_code, provider_response_message, created_at "
                                + "from payment_transaction where created_at >= '2026-09-24 00:00:00' order by id desc limit 20");
                dump(text, connection, "最近的对账问题记录",
                        "select issue_type, business_no, status, expected_value, actual_value, amount, description, created_at "
                                + "from payment_reconciliation_issue order by id desc limit 15");
                dump(text, connection, "商户微信阶段快照",
                        "select application_id, sync_owner, last_synced_at, sync_error, stages_json "
                                + "from merchant_onboarding_progress order by id desc limit 8");
            }
        }
        System.out.println(text);
        Files.writeString(out, text.toString(), StandardCharsets.UTF_8);
        System.out.println("\nwritten to " + out.toAbsolutePath());
    }
 
    private static void section(StringBuilder text, String title) {
        text.append('\n').append(title).append('\n');
    }
 
    private static void dump(StringBuilder text, Connection connection, String title, String sql) {
        text.append("\n--- ").append(title).append(" ---\n");
        try (Statement statement = connection.createStatement(); ResultSet rs = statement.executeQuery(sql)) {
            ResultSetMetaData meta = rs.getMetaData();
            int columns = meta.getColumnCount();
            int rows = 0;
            while (rs.next()) {
                rows++;
                for (int i = 1; i <= columns; i++) {
                    text.append(meta.getColumnLabel(i)).append('=').append(value(rs.getObject(i))).append("  ");
                }
                text.append('\n');
            }
            if (rows == 0) text.append("(空)\n");
        } catch (SQLException exception) {
            text.append("查询失败:").append(exception.getMessage()).append('\n');
        }
    }
 
    private static String value(Object raw) {
        if (raw == null) return "NULL";
        String text = String.valueOf(raw).trim();
        return text.isEmpty() ? "\"\"" : text;
    }
}