Ver código fonte

e签宝调整及有成接口报销推送调整

fyz 1 semana atrás
pai
commit
b1171938b2

+ 1 - 1
mjava-sdzl/src/main/java/com/malk/sdzl/common/EqbYidaFormConfig.java

@@ -35,7 +35,7 @@ public final class EqbYidaFormConfig {
                 "textField_mq625xkd",
                 "radioField_mq625xj9",
                 "attachmentField_mq625xl3",
-                "attachmentField_mq625xl5",
+                "attachmentField_mvrtkf2b",
                 "textField_mq625xl4"));
         add(configs, new EqbYidaFormConfig(
                 "FORM-76FFD768A0FC47128ED4706DB7E9A638JZGF",

+ 28 - 0
mjava-sdzl/src/main/java/com/malk/sdzl/controller/YqbOptionPushController.java

@@ -4,12 +4,14 @@ import com.malk.sdzl.entity.dto.YqbApiResponse;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushRequest;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushResult;
 import com.malk.sdzl.entity.dto.YqbCategoryCreateRequest;
+import com.malk.sdzl.entity.dto.YqbCostCenterUpdateRequest;
 import com.malk.sdzl.entity.dto.YqbInvoiceTitleImportResult;
 import com.malk.sdzl.entity.dto.YqbInvoiceTitleRequest;
 import com.malk.sdzl.entity.dto.YqbInvoiceTitleSyncResult;
 import com.malk.sdzl.entity.dto.YqbOptionPushRequest;
 import com.malk.sdzl.entity.dto.YqbOptionPushResult;
 import com.malk.sdzl.entity.dto.YqbSupplierDedupResult;
+import com.malk.sdzl.entity.dto.YqbSupplierPayAccountUpdateRequest;
 import com.malk.sdzl.entity.dto.YqbSupplierFormPushRequest;
 import com.malk.sdzl.entity.dto.YqbSupplierFormPushResult;
 import com.malk.sdzl.entity.dto.YqbReceiptAccountImportResult;
@@ -105,6 +107,32 @@ public class YqbOptionPushController {
         return McR.success(result);
     }
 
+    /**
+     * 修改成本中心。categoryCode取请求参数,itemCode固定取oldItemCode。
+     * POST /api/sdzl/yqb/option/cost-center/update
+     */
+    @PostMapping("/update")
+    public McR updateCostCenter(@RequestBody YqbCostCenterUpdateRequest request) {
+        log.info("开始修改易企报成本中心,oldItemCode={},itemName={}",
+                request == null ? null : request.getOldItemCode(),
+                request == null ? null : request.getItemName());
+        YqbApiResponse<Object> result = yqbOptionPushService.updateCostCenter(request);
+        return McR.success(result);
+    }
+
+    /**
+     * 按供应商名称维护易企报收款账号关联,categoryCode固定为02。
+     * POST /api/sdzl/yqb/option/supplier/pay-account/update
+     */
+    @PostMapping("/supplier/pay-account/update")
+    public McR updateSupplierPayAccount(
+            @RequestBody YqbSupplierPayAccountUpdateRequest request) {
+        log.info("开始按名称更新易企报供应商收款账号关联,itemName={}",
+                request == null ? null : request.getItemName());
+        YqbApiResponse<Object> result = yqbOptionPushService.updateSupplierPayAccount(request);
+        return McR.success(result);
+    }
+
     /**
      * Excel 批量推送。
      * POST /api/sdzl/yqb/option/upload,Content-Type: multipart/form-data,文件字段名:file。

+ 5 - 0
mjava-sdzl/src/main/java/com/malk/sdzl/entity/commission/master/SharingAdvancePayment.java

@@ -343,6 +343,11 @@ public class SharingAdvancePayment {
     @JsonSetter(nulls = Nulls.AS_EMPTY)
     private String operator;
 
+    /**
+     * 结算方
+     */
+    @JsonSetter(nulls = Nulls.AS_EMPTY)
+    private String clearingParty;
 //    /**
 //     * 编码
 //     */

+ 1 - 1
mjava-sdzl/src/main/java/com/malk/sdzl/entity/dto/YqbAssessmentCostCenterPushRequest.java

@@ -18,7 +18,7 @@ public class YqbAssessmentCostCenterPushRequest implements Serializable {
     private String assessmentSubjectCode;
     /** 考核主体名称,对应新增考核主体请求中的 itemName。 */
     private String assessmentSubject;
-    /** 考核主体关联的辅助核算编码,多个编码以英文逗号分隔。 */
+    /** 考核主体关联的辅助核算编码,由服务合并原有关联和本次成本中心编码后写入。 */
     private String auxiliaryCodes;
     /** 成本中心编码,对应新增成本中心请求中的 itemCode。 */
     private String costCenterCode;

+ 2 - 6
mjava-sdzl/src/main/java/com/malk/sdzl/entity/dto/YqbSupplierFormPushRequest.java

@@ -13,14 +13,10 @@ public class YqbSupplierFormPushRequest implements Serializable {
     /** 宜搭供应商表单实例 ID。 */
     private String formInstanceId;
 
-    /**
-     * 推送类型:新增或变更。新增时不传 oldItemCode;变更时 oldItemCode 必填。
-     */
+    /** 推送类型:新增或变更。 */
     private String type;
 
-    /**
-     * 变更前的供应商辅助选项编码;type 为“变更”时必填。
-     */
+    /** 兼容旧请求中的原供应商编码;当前变更逻辑使用表单中的 itemCode 作为 oldItemCode。 */
     private String oldItemCode;
 
     public static final String TYPE_ADD = "新增";

+ 5 - 2
mjava-sdzl/src/main/java/com/malk/sdzl/service/Impl/CommissionServiceImpl.java

@@ -688,6 +688,7 @@ public class CommissionServiceImpl implements CommissionService {
         String plateName = advancePayment.getPlateName();
         String plateCode = advancePayment.getPlateCode();
         String operator = advancePayment.getOperator();
+        String clearingParty = advancePayment.getClearingParty();
         String accountingEntityNumber = advancePayment.getAccountingEntityNumber();
 
         List<String> userIds = Arrays.asList(operator);
@@ -767,7 +768,8 @@ public class CommissionServiceImpl implements CommissionService {
 //                                "serialNumberField_mq..., " +
                                 "textField_mq68ylrz, " +
                                 "textField_mraba4a4, " +
-                                "radioField_ms2ouakx",
+                                "radioField_ms2ouakx, " +
+                                "textField_mq625xiw",
                         billsAttachment,
                         returnFile,
                         remarks,
@@ -823,7 +825,8 @@ public class CommissionServiceImpl implements CommissionService {
                         userIds,
                         accountingEntityNumber,
                         oaCoding,
-                        isElectronicSign)))
+                        isElectronicSign,
+                        clearingParty)))
                 .build(), YDConf.FORM_OPERATION.start);
 
         return data;

+ 32 - 7
mjava-sdzl/src/main/java/com/malk/sdzl/service/Impl/EqbServiceImpl.java

@@ -48,6 +48,12 @@ import java.util.concurrent.TimeoutException;
 
 @Service
 public class EqbServiceImpl implements EqbService {
+
+    /** 对方签署方式字段:授权人按个人签署,其余方式按企业经办人签署。 */
+    private static final String COUNTERPARTY_SIGNING_METHOD_FIELD_ID = "textField_mq625xix";
+    /** 对方机构名称字段,企业签署方的 orgSignerInfo.orgName 取此字段。 */
+    private static final String COUNTERPARTY_ORG_NAME_FIELD_ID = "textField_mq625xiw";
+    private static final String AUTHORIZED_PERSON_SIGNING_METHOD = "授权人";
     private static final Logger LOG = LoggerFactory.getLogger(EqbServiceImpl.class);
     // e签宝单文件上限50MB;此处仅校验总大小,未解析各页大小。
     private static final long MAX_UPLOAD_FILE_SIZE = 50L * 1024 * 1024;
@@ -425,6 +431,8 @@ public class EqbServiceImpl implements EqbService {
                 formConfig.getCounterpartyRecipientFieldId(), "对方接收人");
         String counterpartyPhone = requireFormText(formData,
                 formConfig.getCounterpartyPhoneFieldId(), "对方接收电话");
+        String counterpartySigningMethod = trimToEmpty(
+                UtilMap.getString(formData, COUNTERPARTY_SIGNING_METHOD_FIELD_ID));
         String ourCompany = requireFormText(formData,
                 formConfig.getOurCompanyFieldId(), "我方签约公司");
         String sealType = trimToEmpty(
@@ -433,13 +441,30 @@ public class EqbServiceImpl implements EqbService {
         JSONArray signers = new JSONArray();
 
         JSONObject counterpartySigner = new JSONObject();
-        counterpartySigner.put("signerType", 0);
-        JSONObject counterpartyPsnInfo = new JSONObject();
-        counterpartyPsnInfo.put("psnName", counterpartyName);
-        JSONObject counterpartySignerInfo = new JSONObject();
-        counterpartySignerInfo.put("psnInfo", counterpartyPsnInfo);
-        counterpartySignerInfo.put("psnAccount", counterpartyPhone);
-        counterpartySigner.put("psnSignerInfo", counterpartySignerInfo);
+        if (AUTHORIZED_PERSON_SIGNING_METHOD.equals(counterpartySigningMethod)) {
+            // 对方签署方式为“授权人”时,沿用个人签署方参数结构。
+            counterpartySigner.put("signerType", 0);
+            JSONObject counterpartyPsnInfo = new JSONObject();
+            counterpartyPsnInfo.put("psnName", counterpartyName);
+            JSONObject counterpartySignerInfo = new JSONObject();
+            counterpartySignerInfo.put("psnInfo", counterpartyPsnInfo);
+            counterpartySignerInfo.put("psnAccount", counterpartyPhone);
+            counterpartySigner.put("psnSignerInfo", counterpartySignerInfo);
+        } else {
+            // 其他签署方式按企业签署方传参,机构名称来自宜搭的对方机构名称字段。
+            String counterpartyOrgName = requireFormText(
+                    formData, COUNTERPARTY_ORG_NAME_FIELD_ID, "对方机构名称");
+            counterpartySigner.put("signerType", 1);
+            JSONObject orgSignerInfo = new JSONObject();
+            orgSignerInfo.put("orgName", counterpartyOrgName);
+            JSONObject transactorInfo = new JSONObject();
+            transactorInfo.put("psnAccount", counterpartyPhone);
+            JSONObject transactorPsnInfo = new JSONObject();
+            transactorPsnInfo.put("psnName", counterpartyName);
+            transactorInfo.put("psnInfo", transactorPsnInfo);
+            orgSignerInfo.put("transactorInfo", transactorInfo);
+            counterpartySigner.put("orgSignerInfo", orgSignerInfo);
+        }
         counterpartySigner.put("signFields", Collections.singletonList(
                 createSignField("甲方", assignedSealId)));
         signers.add(counterpartySigner);

+ 180 - 11
mjava-sdzl/src/main/java/com/malk/sdzl/service/Impl/YqbOptionPushServiceImpl.java

@@ -9,11 +9,13 @@ import com.malk.sdzl.entity.dto.YqbApiResponse;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushRequest;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushResult;
 import com.malk.sdzl.entity.dto.YqbCategoryCreateRequest;
+import com.malk.sdzl.entity.dto.YqbCostCenterUpdateRequest;
 import com.malk.sdzl.entity.dto.YqbOptionExcelRow;
 import com.malk.sdzl.entity.dto.YqbOptionPushRequest;
 import com.malk.sdzl.entity.dto.YqbOptionPushResult;
 import com.malk.sdzl.entity.dto.YqbSupplierDedupExcelRow;
 import com.malk.sdzl.entity.dto.YqbSupplierDedupResult;
+import com.malk.sdzl.entity.dto.YqbSupplierPayAccountUpdateRequest;
 import com.malk.sdzl.service.YqbOptionPushService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -56,6 +58,10 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
     private static final String SELECT_HOLD_NAME_AND_CARD_ID_SQL =
             "SELECT holdName, cardId FROM sdzlcard "
                     + "WHERE holdName IS NOT NULL AND cardId IS NOT NULL";
+    /** 按收款户名查询全部收款账户 ID。 */
+    private static final String SELECT_CARD_IDS_BY_HOLD_NAME_SQL =
+            "SELECT cardId FROM sdzlcard "
+                    + "WHERE holdName = ? AND cardId IS NOT NULL";
     /** 对方开放接口限流安全间隔,约 9 QPS。 */
     private static final long MIN_REQUEST_INTERVAL_MS = 110L;
     /** 单条调用最大尝试次数(含首次)。 */
@@ -87,12 +93,22 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         return yqbApiClient.post(ADD_OPTION_PATH, request.toRequestBody(), "新增选项");
     }
 
+    /** 修改一条辅助选项,调用易企报修改接口并保留请求中传入的业务字段。 */
+    @Override
+    public YqbApiResponse<Object> updateOne(YqbOptionPushRequest request) {
+        validateRequest(request);
+        if (isBlank(request.getOldItemCode())) {
+            throw new IllegalArgumentException("修改辅助选项时oldItemCode不能为空");
+        }
+        return yqbApiClient.post(UPDATE_OPTION_PATH, request.toRequestBody(), "修改选项");
+    }
+
     /**
      * 依次新增成本中心和考核主体。
      *
      * <p>成本中心的 categoryCode 固定为 09,且不传 parentCode 和 auxiliaryCodes。
      * 随后查询 categoryCode=07 的考核主体:itemCode 不存在时新增,存在时修改;
-     * 新增和修改考核主体均传入调用方提供的 auxiliaryCodes。</p>
+     * 新增和修改时将主体原有关联编码与本次成本中心编码合并后传入 auxiliaryCodes。</p>
      */
     @Override
     public YqbAssessmentCostCenterPushResult pushAssessmentSubjectAndCostCenter(
@@ -127,10 +143,15 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
             return result;
         }
 
-        boolean assessmentSubjectExisted = containsItemCode(
+        JSONObject assessmentSubjectItem = findItemByCode(
                 queryResult.getData(), request.getAssessmentSubjectCode());
+        boolean assessmentSubjectExisted = assessmentSubjectItem != null;
         result.setAssessmentSubjectExisted(assessmentSubjectExisted);
 
+        // 保留考核主体原有关联成本中心,并追加本次新增的成本中心编码;重复编码只保留一次。
+        request.setAuxiliaryCodes(mergeAuxiliaryCodes(
+                assessmentSubjectItem, request.getCostCenterCode()));
+
         // 第三步:不存在则新增,存在则以相同的新旧 itemCode 修改考核主体。
         YqbOptionPushRequest assessmentSubjectRequest = new YqbOptionPushRequest();
         assessmentSubjectRequest.setCategoryCode(ASSESSMENT_SUBJECT_CATEGORY_CODE);
@@ -149,17 +170,73 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         return result;
     }
 
+    /**
+     * 修改成本中心,只向易企报传递四个接口字段。
+     * categoryCode使用请求参数,itemCode直接使用oldItemCode,避免调用方传入错误编码。
+     */
+    @Override
+    public YqbApiResponse<Object> updateCostCenter(YqbCostCenterUpdateRequest request) {
+        validateCostCenterUpdateRequest(request);
+        Map<String, Object> requestBody = new LinkedHashMap<>();
+        requestBody.put("oldItemCode", request.getOldItemCode().trim());
+        requestBody.put("categoryCode", request.getCategoryCode().trim());
+        requestBody.put("itemCode", request.getOldItemCode().trim());
+        requestBody.put("itemName", request.getItemName().trim());
+        return yqbApiClient.post(UPDATE_OPTION_PATH, requestBody, "修改成本中心");
+    }
+
+    /**
+     * 按名称查询供应商辅助选项并维护其收款账号关联。
+     * 查询和修改请求均只使用 categoryCode=02;修改时不透传任何其他字段。
+     */
+    @Override
+    public YqbApiResponse<Object> updateSupplierPayAccount(
+            YqbSupplierPayAccountUpdateRequest request) {
+        validateSupplierPayAccountUpdateRequest(request);
+        String itemName = request.getItemName().trim();
+
+        // 先按名称获取易企报辅助选项编码,后续新旧编码都使用该返回值。
+        Map<String, Object> queryBody = new LinkedHashMap<>();
+        queryBody.put("categoryCode", SUPPLIER_CATEGORY_CODE);
+        queryBody.put("itemName", itemName);
+        YqbApiResponse<Object> queryResponse = yqbApiClient.post(
+                QUERY_OPTION_LIST_PATH, queryBody, "查询供应商辅助选项");
+        if (queryResponse == null || !queryResponse.isSuccess()) {
+            return queryResponse;
+        }
+        JSONObject item = findItemByName(queryResponse.getData(), itemName);
+        if (item == null || isBlank(item.getString("itemCode"))) {
+            throw new IllegalStateException("未找到名称为“" + itemName + "”的供应商辅助选项");
+        }
+        String itemCode = item.getString("itemCode").trim();
+
+        String payAccountIds = loadPayAccountIdsByHoldName(itemName);
+        if (isBlank(payAccountIds)) {
+            throw new IllegalStateException("sdzlcard中未找到holdName为“" + itemName + "”的收款账号");
+        }
+
+        // 严格只传接口要求的五个字段,避免覆盖其他供应商辅助选项属性。
+        Map<String, Object> updateBody = new LinkedHashMap<>();
+        updateBody.put("oldItemCode", itemCode);
+        updateBody.put("categoryCode", SUPPLIER_CATEGORY_CODE);
+        updateBody.put("itemCode", itemCode);
+        updateBody.put("itemName", itemName);
+        updateBody.put("payAccountIds", payAccountIds);
+        return yqbApiClient.post(UPDATE_OPTION_PATH, updateBody, "修改供应商收款账号关联");
+    }
+
     /** 查询 07 类别下的全部考核主体。 */
     private YqbApiResponse<Object> queryAssessmentSubjects() {
         Map<String, Object> requestBody = new HashMap<>();
         requestBody.put("categoryCode", ASSESSMENT_SUBJECT_CATEGORY_CODE);
+        requestBody.put("showRelationAuxiliary", "1");
         return yqbApiClient.post(QUERY_OPTION_LIST_PATH, requestBody, "查询考核主体");
     }
 
-    /** 按接口文档中的 data.items[].itemCode 判断考核主体是否存在。 */
-    private boolean containsItemCode(Object data, String itemCode) {
+    /** 按接口文档中的 data.items[].itemCode 查找考核主体,并保留其关联辅助核算数据。 */
+    private JSONObject findItemByCode(Object data, String itemCode) {
         if (data == null) {
-            return false;
+            return null;
         }
         JSONObject dataObject = data instanceof JSONObject
                 ? (JSONObject) data
@@ -169,15 +246,69 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         }
         JSONArray items = dataObject.getJSONArray("items");
         if (items == null) {
-            return false;
+            return null;
         }
         for (int i = 0; i < items.size(); i++) {
             JSONObject item = items.getJSONObject(i);
             if (item != null && itemCode.equals(item.getString("itemCode"))) {
-                return true;
+                return item;
+            }
+        }
+        return null;
+    }
+
+    /** 从辅助选项列表中按名称精确查找一条选项。 */
+    private JSONObject findItemByName(Object data, String itemName) {
+        if (data == null) {
+            return null;
+        }
+        JSONObject dataObject = data instanceof JSONObject
+                ? (JSONObject) data
+                : JSON.parseObject(JSON.toJSONString(data));
+        if (dataObject == null) {
+            throw new IllegalStateException("查询供应商辅助选项接口返回的data格式异常");
+        }
+        JSONArray items = dataObject.getJSONArray("items");
+        if (items == null) {
+            return null;
+        }
+        for (int i = 0; i < items.size(); i++) {
+            JSONObject item = items.getJSONObject(i);
+            if (item != null && itemName.equals(item.getString("itemName"))) {
+                return item;
             }
         }
-        return false;
+        return null;
+    }
+
+    /**
+     * 将当前考核主体 relationAuxiliaryList 中的 itemCode 与本次成本中心编码合并,
+     * 保持原有关联顺序,并使用英文逗号拼接。
+     */
+    private String mergeAuxiliaryCodes(JSONObject assessmentSubjectItem, String costCenterCode) {
+        Set<String> auxiliaryCodes = new LinkedHashSet<>();
+        if (assessmentSubjectItem != null) {
+            Object rawRelations = assessmentSubjectItem.get("relationAuxiliaryList");
+            if (rawRelations != null && !(rawRelations instanceof List)) {
+                throw new IllegalStateException("考核主体的relationAuxiliaryList格式异常");
+            }
+            if (rawRelations instanceof List) {
+                for (Object rawRelation : (List) rawRelations) {
+                    JSONObject relation = rawRelation instanceof JSONObject
+                            ? (JSONObject) rawRelation
+                            : JSON.parseObject(JSON.toJSONString(rawRelation));
+                    if (relation == null) {
+                        continue;
+                    }
+                    String relationItemCode = relation.getString("itemCode");
+                    if (!isBlank(relationItemCode)) {
+                        auxiliaryCodes.add(relationItemCode.trim());
+                    }
+                }
+            }
+        }
+        auxiliaryCodes.add(costCenterCode.trim());
+        return String.join(",", auxiliaryCodes);
     }
 
     @Override
@@ -403,6 +534,20 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         return payAccountIdsByHoldName;
     }
 
+    /** 查询指定户名的全部收款账户 ID,去重后按原查询顺序拼接。 */
+    private String loadPayAccountIdsByHoldName(String holdName) {
+        List<Map<String, Object>> cardRows = jdbcTemplate.queryForList(
+                SELECT_CARD_IDS_BY_HOLD_NAME_SQL, holdName);
+        Set<String> cardIds = new LinkedHashSet<>();
+        for (Map<String, Object> cardRow : cardRows) {
+            String cardId = trimToNull(toStringValue(cardRow.get("cardId")));
+            if (cardId != null) {
+                cardIds.add(cardId);
+            }
+        }
+        return String.join(",", cardIds);
+    }
+
     private String toStringValue(Object value) {
         return value == null ? null : String.valueOf(value);
     }
@@ -572,9 +717,6 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         if (isBlank(request.getAssessmentSubject())) {
             throw new IllegalArgumentException("考核主体不能为空");
         }
-        if (isBlank(request.getAuxiliaryCodes())) {
-            throw new IllegalArgumentException("auxiliaryCodes不能为空");
-        }
         if (isBlank(request.getCostCenterCode())) {
             throw new IllegalArgumentException("成本中心编码不能为空");
         }
@@ -583,6 +725,33 @@ public class YqbOptionPushServiceImpl implements YqbOptionPushService {
         }
     }
 
+    /** 校验修改成本中心所需的原编码和新名称。 */
+    private void validateCostCenterUpdateRequest(YqbCostCenterUpdateRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("请求参数不能为空");
+        }
+        if (isBlank(request.getOldItemCode())) {
+            throw new IllegalArgumentException("成本中心原编码不能为空");
+        }
+        if (isBlank(request.getCategoryCode())) {
+            throw new IllegalArgumentException("辅助类别编码不能为空");
+        }
+        if (isBlank(request.getItemName())) {
+            throw new IllegalArgumentException("成本中心名称不能为空");
+        }
+    }
+
+    /** 校验按名称维护收款账号关联的请求。 */
+    private void validateSupplierPayAccountUpdateRequest(
+            YqbSupplierPayAccountUpdateRequest request) {
+        if (request == null) {
+            throw new IllegalArgumentException("请求参数不能为空");
+        }
+        if (isBlank(request.getItemName())) {
+            throw new IllegalArgumentException("itemName不能为空");
+        }
+    }
+
     /** 判断当前选项是否新增成功或已经存在。 */
     private boolean isSuccessOrAlreadyExists(YqbApiResponse<Object> response) {
         if (response == null) {

+ 73 - 12
mjava-sdzl/src/main/java/com/malk/sdzl/service/Impl/YqbSupplierFormPushServiceImpl.java

@@ -19,10 +19,14 @@ import com.malk.service.aliwork.YDClient;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 
 import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /** 根据宜搭供应商表单创建易企报收款信息、发票抬头和供应商辅助选项。 */
 @Slf4j
@@ -32,6 +36,16 @@ public class YqbSupplierFormPushServiceImpl implements YqbSupplierFormPushServic
     private static final String SUPPLIER_FORM_UUID = "FORM-3648E003C1614C87AD5EC2709874F975TPYM";
     private static final String SUPPLIER_CATEGORY_CODE = "02";
 
+    /** 将易企报基础资料同步到本地配置表。 */
+    private static final String INSERT_CARD_SQL =
+            "INSERT INTO sdzlcard (cardId, holdName) VALUES (?, ?)";
+    private static final String INSERT_INVOICE_SQL =
+            "INSERT INTO sdzlInvoice (taxPayerNo, invoiceId) VALUES (?, ?)";
+    private static final String SELECT_CARD_IDS_SQL =
+            "SELECT cardId FROM sdzlcard WHERE holdName = ? AND cardId IS NOT NULL";
+    private static final String SELECT_INVOICE_IDS_SQL =
+            "SELECT invoiceId FROM sdzlInvoice WHERE taxPayerNo = ? AND invoiceId IS NOT NULL";
+
     /** 宜搭供应商表单字段编码。 */
     private static final String FIELD_SUPPLIER_NAME = "textField_mpfb5ond";
     private static final String FIELD_TAX_PAYER_NO = "textField_mpfb5oo5";
@@ -55,6 +69,9 @@ public class YqbSupplierFormPushServiceImpl implements YqbSupplierFormPushServic
     @Autowired
     private YqbOptionPushService yqbOptionPushService;
 
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
     @Value("${aliwork.appType}")
     private String appType;
 
@@ -85,19 +102,28 @@ public class YqbSupplierFormPushServiceImpl implements YqbSupplierFormPushServic
         YqbApiResponse<Object> receiptResponse = yqbReceiptAccountService.addReceiptAccount(receiptRequest);
         ensureSuccess(receiptResponse, "新增收款信息");
         String payAccountId = extractResponseId(receiptResponse.getData(), "新增收款信息");
+        // 收款信息成功后立即落库,后续供应商关联从数据库汇总全部收款账号。
+        insertCardMapping(receiptRequest.getHoldName(), payAccountId);
 
         YqbApiResponse<Object> invoiceResponse = yqbInvoiceTitleService.addOrUpdateInvoiceTitle(invoiceRequest);
         ensureSuccess(invoiceResponse, "新增发票抬头");
         // 发票抬头新增接口成功时直接返回 data=发票抬头ID,例如 data: 131309。
         String invoiceTitleId = extractDirectResponseId(invoiceResponse.getData(), "新增发票抬头");
+        // 发票抬头成功后落库,后续供应商关联从数据库汇总全部发票抬头。
+        insertInvoiceMapping(invoiceRequest.getTaxPayerNo(), invoiceTitleId);
 
-        supplierRequest.setPayAccountIds(payAccountId);
-        supplierRequest.setInvoiceTitleIds(invoiceTitleId);
+        supplierRequest.setPayAccountIds(loadPayAccountIds(receiptRequest.getHoldName()));
+        supplierRequest.setInvoiceTitleIds(loadInvoiceTitleIds(invoiceRequest.getTaxPayerNo()));
         if (YqbSupplierFormPushRequest.TYPE_CHANGE.equals(type)) {
-            supplierRequest.setOldItemCode(requiredOldItemCode(request));
+            // 变更接口的新旧编码相同,均使用宜搭表单中的供应商编码。
+            supplierRequest.setOldItemCode(supplierRequest.getItemCode());
         }
-        YqbApiResponse<Object> supplierResponse = yqbOptionPushService.pushOne(supplierRequest);
-        ensureSuccess(supplierResponse, "新增供应商");
+        // 新增和变更只在是否传 oldItemCode 上有差异,分别调用对应的易企报接口。
+        YqbApiResponse<Object> supplierResponse = YqbSupplierFormPushRequest.TYPE_CHANGE.equals(type)
+                ? yqbOptionPushService.updateOne(supplierRequest)
+                : yqbOptionPushService.pushOne(supplierRequest);
+        ensureSuccess(supplierResponse,
+                YqbSupplierFormPushRequest.TYPE_CHANGE.equals(type) ? "变更供应商" : "新增供应商");
 
         YqbSupplierFormPushResult result = new YqbSupplierFormPushResult();
         result.setFormInstanceId(formInstanceId);
@@ -111,6 +137,48 @@ public class YqbSupplierFormPushServiceImpl implements YqbSupplierFormPushServic
         return result;
     }
 
+    /** 将新增收款账号写入本地 sdzlcard 配置表。 */
+    private void insertCardMapping(String holdName, String cardId) {
+        jdbcTemplate.update(INSERT_CARD_SQL, cardId, holdName);
+    }
+
+    /** 将新增发票抬头写入本地 sdzlInvoice 配置表。 */
+    private void insertInvoiceMapping(String taxPayerNo, String invoiceId) {
+        jdbcTemplate.update(INSERT_INVOICE_SQL, taxPayerNo, invoiceId);
+    }
+
+    /** 查询供应商全称对应的全部收款账号 ID,去重后使用英文逗号拼接。 */
+    private String loadPayAccountIds(String holdName) {
+        List<Map<String, Object>> rows = jdbcTemplate.queryForList(SELECT_CARD_IDS_SQL, holdName);
+        Set<String> ids = new LinkedHashSet<>();
+        for (Map<String, Object> row : rows) {
+            String id = valueOf(row.get("cardId"));
+            if (!isBlank(id)) {
+                ids.add(id);
+            }
+        }
+        if (ids.isEmpty()) {
+            throw new IllegalStateException("sdzlcard中未找到供应商收款账号,holdName=" + holdName);
+        }
+        return String.join(",", ids);
+    }
+
+    /** 查询统一社会信用代码/身份证对应的全部发票抬头 ID,去重后使用英文逗号拼接。 */
+    private String loadInvoiceTitleIds(String taxPayerNo) {
+        List<Map<String, Object>> rows = jdbcTemplate.queryForList(SELECT_INVOICE_IDS_SQL, taxPayerNo);
+        Set<String> ids = new LinkedHashSet<>();
+        for (Map<String, Object> row : rows) {
+            String id = valueOf(row.get("invoiceId"));
+            if (!isBlank(id)) {
+                ids.add(id);
+            }
+        }
+        if (ids.isEmpty()) {
+            throw new IllegalStateException("sdzlInvoice中未找到发票抬头,taxPayerNo=" + taxPayerNo);
+        }
+        return String.join(",", ids);
+    }
+
     private YqbReceiptAccountRequest buildReceiptAccountRequest(Map<String, Object> formData) {
         YqbReceiptAccountRequest request = new YqbReceiptAccountRequest();
         request.setHoldName(requiredField(formData, FIELD_SUPPLIER_NAME, "供应商全称"));
@@ -241,13 +309,6 @@ public class YqbSupplierFormPushServiceImpl implements YqbSupplierFormPushServic
         return type;
     }
 
-    private String requiredOldItemCode(YqbSupplierFormPushRequest request) {
-        if (isBlank(request.getOldItemCode())) {
-            throw new IllegalArgumentException("type为“变更”时,oldItemCode不能为空");
-        }
-        return request.getOldItemCode().trim();
-    }
-
     private void validateYidaConfig() {
         if (isBlank(appType) || isBlank(systemToken)) {
             throw new IllegalStateException("未配置aliwork.appType或aliwork.systemToken");

+ 12 - 0
mjava-sdzl/src/main/java/com/malk/sdzl/service/YqbOptionPushService.java

@@ -4,9 +4,11 @@ import com.malk.sdzl.entity.dto.YqbApiResponse;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushRequest;
 import com.malk.sdzl.entity.dto.YqbAssessmentCostCenterPushResult;
 import com.malk.sdzl.entity.dto.YqbCategoryCreateRequest;
+import com.malk.sdzl.entity.dto.YqbCostCenterUpdateRequest;
 import com.malk.sdzl.entity.dto.YqbOptionPushRequest;
 import com.malk.sdzl.entity.dto.YqbOptionPushResult;
 import com.malk.sdzl.entity.dto.YqbSupplierDedupResult;
+import com.malk.sdzl.entity.dto.YqbSupplierPayAccountUpdateRequest;
 import org.springframework.web.multipart.MultipartFile;
 
 public interface YqbOptionPushService {
@@ -17,6 +19,9 @@ public interface YqbOptionPushService {
     /** 推送一条辅助选项。 */
     YqbApiResponse<Object> pushOne(YqbOptionPushRequest request);
 
+    /** 修改一条辅助选项。 */
+    YqbApiResponse<Object> updateOne(YqbOptionPushRequest request);
+
     /**
      * 依次新增成本中心和考核主体。
      * 成本中心新增成功或返回已存在时,才继续新增考核主体。
@@ -27,6 +32,13 @@ public interface YqbOptionPushService {
     YqbAssessmentCostCenterPushResult pushAssessmentSubjectAndCostCenter(
             YqbAssessmentCostCenterPushRequest request);
 
+    /** 修改成本中心;categoryCode取请求参数,itemCode固定取oldItemCode。 */
+    YqbApiResponse<Object> updateCostCenter(YqbCostCenterUpdateRequest request);
+
+    /** 按供应商名称查询并更新易企报收款账号关联,categoryCode固定为02。 */
+    YqbApiResponse<Object> updateSupplierPayAccount(
+            YqbSupplierPayAccountUpdateRequest request);
+
     /** 上传 Excel 后按行循环推送辅助选项。 */
     YqbOptionPushResult pushByExcel(MultipartFile file);