|
|
@@ -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) {
|