talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java
@@ -350,29 +350,51 @@
        int safePage = Math.max(page, 1);
        int safeSize = Math.min(Math.max(size, 1), 50);
        StringBuilder where = new StringBuilder(" where 1=1");
        List<Object> params = new ArrayList<>();
        String cityFilter = blank(cityName) ? clean(cityCode) : clean(cityName);
        int citySeparator = cityFilter.lastIndexOf(' ');
        if (citySeparator >= 0 && citySeparator + 1 < cityFilter.length()) cityFilter = cityFilter.substring(citySeparator + 1).trim();
        cityFilter = cityKeyword(cityFilter);
        if (!blank(cityFilter)) {
        List<Object> whereParams = new ArrayList<>();
        BankBranchKeyword parsed = BankBranchKeyword.parse(keyword);
        if (parsed.clearingCode().length() > 0) {
            where.append(" and clearing_code like ?");
            whereParams.add("%" + parsed.clearingCode() + "%");
        } else if (parsed.bankName().length() > 0) {
            appendLikeColumn(where, whereParams, "bank_name", parsed.bankName());
            // 总行名已经限定过,剩下的片段只比支行名,不再拿它去匹配总行名。
            if (parsed.branchName().length() > 0) appendLikeColumn(where, whereParams, "branch_name", parsed.branchName());
        } else if (parsed.branchName().length() > 0) {
            // 没识别出银行名时两列都试:「泰隆」这类不在表里的银行只能由 bank_name 命中。
            where.append(" and (branch_name like ? or bank_name like ?)");
            String match = "%" + cityFilter + "%";
            params.add(match);
            params.add(match);
            whereParams.add("%" + parsed.branchName() + "%");
            whereParams.add("%" + parsed.branchName() + "%");
        }
        if (!blank(keyword)) {
            where.append(" and (branch_name like ? or bank_name like ? or clearing_code = ?)");
            String match = "%" + clean(keyword) + "%";
            params.add(match);
            params.add(match);
            params.add(clean(keyword));
        Long total = jdbc.queryForObject("select count(*) from bank_branch_directory" + where, Long.class, whereParams.toArray());
        // 开户城市只用于把本地网点排在前面,不再作为过滤条件:目录里的支行名由县区名构成
        // (「…邻水丰禾支行」里没有「广安」),按地市名过滤会把整批县区级网点筛掉,跨市开户的
        // 商户就永远搜不到自己的开户行。排序兼顾两点:常见关键词先给就近网点,跨市网点也还搜得到。
        String cityHint = cityHint(cityCode, cityName);
        List<Object> pageParams = new ArrayList<>(whereParams);
        String orderBy = " order by branch_name";
        if (!blank(cityHint)) {
            orderBy = " order by case when branch_name like ? or bank_name like ? then 0 else 1 end, branch_name";
            pageParams.add("%" + cityHint + "%");
            pageParams.add("%" + cityHint + "%");
        }
        Long total = jdbc.queryForObject("select count(*) from bank_branch_directory" + where, Long.class, params.toArray());
        List<Map<String, Object>> values = rows("select clearing_code as id, clearing_code as branch_code, branch_name, head_bank_code, bank_name from bank_branch_directory" + where + " order by branch_name limit ? offset ?", appendPaging(params, safeSize, (safePage - 1) * safeSize));
        List<Map<String, Object>> values = rows("select clearing_code as id, clearing_code as branch_code, branch_name, head_bank_code, bank_name from bank_branch_directory" + where + orderBy + " limit ? offset ?", appendPaging(pageParams, safeSize, (safePage - 1) * safeSize));
        return new PageResult<>(values, safePage, safeSize, total == null ? 0 : total);
    }
    private void appendLikeColumn(StringBuilder where, List<Object> params, String column, String value) {
        where.append(" and ").append(column).append(" like ?");
        params.add("%" + value + "%");
    }
    /** 从「四川省 攀枝花市」这类选择器取值里取出用作排序提示的地市名。 */
    private String cityHint(String cityCode, String cityName) {
        String filter = blank(cityName) ? clean(cityCode) : clean(cityName);
        int separator = filter.lastIndexOf(' ');
        if (separator >= 0 && separator + 1 < filter.length()) filter = filter.substring(separator + 1).trim();
        return cityKeyword(filter);
    }
    private void requireApproved(MerchantPrincipal principal) { if (!"approved".equals(onboardingStatus(principal).get("status"))) throw new BusinessException(ErrorCode.CONFLICT, "商户进件尚未审核通过"); }
    private void submitBankCardToHuifu(MerchantPrincipal principal, Map<String, Object> card, String cardNumber, Map<String, Object> branch) {
        List<Map<String, Object>> onboarding = rows("select * from merchant_onboarding_application where merchant_id=? and store_id=? and initial_bank_card_id is not null and status='approved' and provider_huifu_id is not null order by id desc limit 1", principal.merchantId(), principal.storeId());