package com.talentcard.server.business.merchant; import com.alibaba.fastjson.JSON; import com.talentcard.server.business.DatabaseRows; import com.talentcard.server.business.payment.huifu.HuifuOnboardingProgressGateway; import com.talentcard.server.common.BusinessException; import com.talentcard.server.common.ErrorCode; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; /** Six independent evidence-backed stages; provider calls never hold application row locks. */ @Service public class MerchantOnboardingProgressService { public static final String RECOVERY_HUIFU_ID = "6666000241335656"; private static final Map LABELS = new LinkedHashMap<>(); static { LABELS.put("basic", "基础审核"); LABELS.put("wechat", "微信支付"); LABELS.put("appId", "人才端绑定"); LABELS.put("realname", "实名与授权"); LABELS.put("delay", "延迟入账"); LABELS.put("settlement", "自动结算"); } private final JdbcTemplate jdbc; private final HuifuOnboardingProgressGateway gateway; public MerchantOnboardingProgressService(JdbcTemplate jdbc, HuifuOnboardingProgressGateway gateway) { this.jdbc=jdbc; this.gateway=gateway; } public boolean supports(Map application) { return RECOVERY_HUIFU_ID.equals(text(application.get("providerHuifuId"))); } public Map refresh(Long applicationId, boolean retry) { Map application=application(applicationId); if (!supports(application)) throw new BusinessException(ErrorCode.CONFLICT,"This recovery is limited to the designated existing merchant"); jdbc.update("insert ignore into merchant_onboarding_progress(application_id,stages_json) values (?, '{}')", applicationId); String owner=UUID.randomUUID().toString(); if(jdbc.update("update merchant_onboarding_progress set sync_owner=?,sync_until=date_add(now(),interval 5 minute) where application_id=? and (sync_owner is null or sync_until response=gateway.queryApplication(text(application.get("providerHuifuId")), text(application.get("providerApplyNo"))); if (!"00000000".equals(text(response.get("resp_code")))) { String error="20002".equals(text(response.get("resp_code"))) ? "汇付查询权限不足(20002),请联系平台运营核实接口权限" : "汇付查询未成功,请稍后刷新或联系平台运营"; jdbc.update("update merchant_onboarding_progress set sync_error=? where application_id=? and sync_owner=?",error,applicationId,owner); } else { Map stages = savedStages(applicationId); String audit=text(response.get("apply_status")); if ("Y".equals(audit) && "S".equals(text(response.get("reg_status")))) stages.put("basic", "confirmed"); else if (!"confirmed".equals(stages.get("basic"))) stages.put("basic",List.of("N","F").contains(audit) ? "failed" : "pending"); jdbc.update("update merchant_onboarding_progress set stages_json=?,last_synced_at=now(),sync_error='' where application_id=? and sync_owner=?",JSON.toJSONString(stages),applicationId,owner); Map capabilities=gateway.queryCapabilities(text(application.get("providerHuifuId"))); if (capabilities != null && "00000000".equals(text(capabilities.get("resp_code")))) { stages.put("delay", "Y".equals(capabilities.get("delay_flag")) && "N".equals(capabilities.get("forced_delay_flag")) ? "confirmed" : "unknown"); jdbc.update("update merchant_onboarding_progress set stages_json=? where application_id=? and sync_owner=?",JSON.toJSONString(stages),applicationId,owner); } jdbc.update("update merchant_onboarding_progress set last_synced_at=now() where application_id=? and sync_owner=?",applicationId,owner); } } catch (RuntimeException exception) { jdbc.update("update merchant_onboarding_progress set sync_error=? where application_id=? and sync_owner=?","查询结果暂未确认,已保留最后有效进度,请稍后刷新",applicationId,owner); } finally { jdbc.update("update merchant_onboarding_progress set sync_owner=null,sync_until=null where application_id=? and sync_owner=?",applicationId,owner); } return view(application(applicationId)); } public Map view(Map application) { Map result=new LinkedHashMap<>(); result.put("id",text(application.get("id"))); result.put("applicationNo",text(application.get("applicationNo"))); result.put("status","pending"); result.put("subjectType",text(application.get("subjectType"))); result.put("submittedAt",text(application.get("createdAt"))); result.put("retryable",false); result.put("refreshable",true); result.put("lastSyncedAt",""); result.put("syncError",""); List> saved=rows("select * from merchant_onboarding_progress where application_id=?",application.get("id")); Map stages=new LinkedHashMap<>(); if (!saved.isEmpty()) { stages=JSON.parseObject(text(saved.get(0).get("stagesJson")), Map.class); result.put("lastSyncedAt",text(saved.get(0).get("lastSyncedAt"))); result.put("syncError",text(saved.get(0).get("syncError"))); } List> progress=new ArrayList<>(); for (var entry:LABELS.entrySet()) progress.add(Map.of("key",entry.getKey(),"label",entry.getValue(),"status",text(stages.getOrDefault(entry.getKey(),"unknown")),"reason","尚未取得通道生效证据")); result.put("stages",progress); result.put("reason","请刷新进度,核实原申请与各项能力"); return result; } private Map application(Long id) { List> records=rows("select * from merchant_onboarding_application where id=? and initial_bank_card_id is not null",id); if(records.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND,"Onboarding application not found"); return records.get(0); } @SuppressWarnings("unchecked") private Map savedStages(Long id) { return JSON.parseObject(jdbc.queryForObject("select stages_json from merchant_onboarding_progress where application_id=?",String.class,id),Map.class); } private List> rows(String sql,Object... args) { return DatabaseRows.normalize(jdbc.queryForList(sql,args)); } private static String text(Object value) { return value==null ? "" : String.valueOf(value); } }