:feat: 新增项目金蝶同步结果回调并回填金蝶ID
- 新增 /hcs/projectLegal/result 免登回调端点(D902),按项目编号定位回填金蝶项目ID/编号,原文留痕 blade_hcs_check_log - 合同 contractLegal/result 回调成功分支回填金蝶合同ID - 项目/合同金蝶回调成功与失败均追加变更记录(changeRecordJson) - 项目立项/合同列表支持按是否同步金蝶、是否同步合规筛选 - 新增增量SQL:blade_project_apply.kingdee_id/kingdee_number、blade_contract_manage.kingdee_id
This commit is contained in:
+3
@@ -262,4 +262,7 @@ public class ContractManage extends TenantEntity {
|
||||
@Schema(description = "特批申请材料+内部审批附件JSON(含C103-2 合规附件ID)")
|
||||
private String hcsSpecFilesJson;
|
||||
|
||||
@Schema(description = "金蝶合同ID(contractLegal/result 合同特批金蝶同步结果回调回填)")
|
||||
private String kingdeeId;
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -215,4 +215,10 @@ public class ProjectApply extends TenantEntity {
|
||||
@TableField(insertStrategy = FieldStrategy.ALWAYS, updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String hcsSyncMsg;
|
||||
|
||||
@Schema(description = "金蝶项目ID(D902 项目金蝶同步结果回调回填)")
|
||||
private String kingdeeId;
|
||||
|
||||
@Schema(description = "金蝶项目编号(D902 项目金蝶同步结果回调回填)")
|
||||
private String kingdeeNumber;
|
||||
|
||||
}
|
||||
|
||||
+8
@@ -55,6 +55,14 @@ public class ContractManageVO extends ContractManage {
|
||||
@Schema(description = "到期快捷筛选")
|
||||
private String expireScope;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否已同步金蝶(金蝶合同ID是否已回填)")
|
||||
private Boolean kingdeeSynced;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否已同步合规(合规同步状态是否为 synced)")
|
||||
private Boolean hcsSynced;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "生效类型筛选")
|
||||
private String effectiveType;
|
||||
|
||||
+8
@@ -86,6 +86,14 @@ public class ProjectApplyVO extends ProjectApply {
|
||||
@Schema(description = "是否仅查询可用于合同选择的项目(含临时项目与正式审批通过项目)")
|
||||
private Boolean contractSelectable;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否已同步金蝶(金蝶项目ID是否已回填)")
|
||||
private Boolean kingdeeSynced;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "是否已同步合规(合规同步状态是否为 synced)")
|
||||
private Boolean hcsSynced;
|
||||
|
||||
@TableField(exist = false)
|
||||
@Schema(description = "资金使用风险等级:high、medium、none")
|
||||
private String fundUseRisk;
|
||||
|
||||
+5
@@ -89,6 +89,11 @@ public final class HcsSyncConstant {
|
||||
*/
|
||||
public static final String CHECK_TYPE_SPEC = "spec";
|
||||
|
||||
/**
|
||||
* 校验类型:项目金蝶同步结果回调(D902,projectLegal/result 推送留痕)
|
||||
*/
|
||||
public static final String CHECK_TYPE_KINGDEE = "kingdee";
|
||||
|
||||
/**
|
||||
* 校验结果:通过
|
||||
*/
|
||||
|
||||
+28
@@ -36,11 +36,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.hcs.dto.HcsAttachmentRefDTO;
|
||||
import org.springblade.hcs.dto.HcsContractLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsCurrencyVO;
|
||||
import org.springblade.hcs.dto.HcsLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsTemplateVO;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.pojo.vo.CustomerRiskVO;
|
||||
@@ -223,6 +225,17 @@ public class HcsSyncController {
|
||||
return R.data(hcsCreditCheckSupport.queryCustomerRisk(customerCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 客商风险批量查询(C101-1,客商列表页展示用,前端接入设计 §3.3):
|
||||
* 按客商编号集合逐个查询(口径同 /risk),展示用不作为安全边界,单个失败跳过不阻断整页
|
||||
*/
|
||||
@GetMapping("/risk/batch")
|
||||
@ApiOperationSupport(order = 13)
|
||||
@Operation(summary = "客商风险批量查询", description = "C101-1 按客商编号集合批量查询风险等级与黑名单状态(展示用),口径同单个查询,单个失败跳过不阻断")
|
||||
public R<List<CustomerRiskVO>> riskBatch(@Parameter(description = "客商编号集合,逗号分隔", required = true) @RequestParam String customerCodes) {
|
||||
return R.data(hcsCreditCheckSupport.queryCustomerRiskBatch(Func.toStrList(customerCodes)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件上传到合同系统(C103-2 代理,前端接入设计 §3.2),返回合规附件ID
|
||||
*/
|
||||
@@ -293,4 +306,19 @@ public class HcsSyncController {
|
||||
public R<String> paymentLegalResult(@RequestBody(required = false) HcsLegalResultDTO param) {
|
||||
return R.data(hcsPaymentSyncSupport.handleLegalResult(param));
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目金蝶同步结果回传接收(合规系统 projectLegal/result 推送,D902,原文留痕)
|
||||
* <p>
|
||||
* 免登免授权:blade.secure skip-url + auth permitAll 放行(doc/nacos/blade.yaml);
|
||||
* iPaaS 网关注册映射:/gwzh/HTHG/projectLegal/result → /hcs/projectLegal/result;
|
||||
* 报文 {sourceSystem, projectCode, requestId, success, kingdeeId, projectNumber, reason},
|
||||
* 按 projectCode=TMS 项目编号定位,success=true 回填金蝶项目ID/编号,success=false 记录失败原因。
|
||||
*/
|
||||
@PostMapping("/projectLegal/result")
|
||||
@ApiOperationSupport(order = 18)
|
||||
@Operation(summary = "项目金蝶同步结果回传接收", description = "合规系统推送项目金蝶同步结果(D902),按项目编号定位并回填金蝶ID,原文留痕")
|
||||
public R<String> projectLegalResult(@RequestBody(required = false) HcsProjectLegalResultDTO param) {
|
||||
return R.data(hcsProjectSyncSupport.handleLegalResult(param));
|
||||
}
|
||||
}
|
||||
|
||||
+20
@@ -482,9 +482,29 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
||||
.orderByDesc(ContractManage::getCreateTime);
|
||||
applyEffectiveType(queryWrapper, contractManage.getEffectiveType());
|
||||
applyExpireScope(queryWrapper, contractManage.getExpireScope());
|
||||
// 是否同步金蝶(kingdee_id 是否已回填)/ 是否同步合规(hcs_sync_status 是否 synced)
|
||||
applySyncFlagFilter(queryWrapper, contractManage.getKingdeeSynced(), contractManage.getHcsSynced());
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 金蝶/合规同步与否筛选(列表搜索:kingdeeSynced 按 kingdee_id 空值判断,hcsSynced 按 hcs_sync_status=synced 判断)
|
||||
*/
|
||||
private void applySyncFlagFilter(LambdaQueryWrapper<ContractManage> queryWrapper, Boolean kingdeeSynced, Boolean hcsSynced) {
|
||||
if (Boolean.TRUE.equals(kingdeeSynced)) {
|
||||
queryWrapper.isNotNull(ContractManage::getKingdeeId).ne(ContractManage::getKingdeeId, "");
|
||||
} else if (Boolean.FALSE.equals(kingdeeSynced)) {
|
||||
queryWrapper.and(wrapper -> wrapper.isNull(ContractManage::getKingdeeId)
|
||||
.or().eq(ContractManage::getKingdeeId, ""));
|
||||
}
|
||||
if (Boolean.TRUE.equals(hcsSynced)) {
|
||||
queryWrapper.eq(ContractManage::getHcsSyncStatus, HcsSyncConstant.HCS_STATUS_SYNCED);
|
||||
} else if (Boolean.FALSE.equals(hcsSynced)) {
|
||||
queryWrapper.and(wrapper -> wrapper.isNull(ContractManage::getHcsSyncStatus)
|
||||
.or().ne(ContractManage::getHcsSyncStatus, HcsSyncConstant.HCS_STATUS_SYNCED));
|
||||
}
|
||||
}
|
||||
|
||||
private void applyEffectiveType(LambdaQueryWrapper<ContractManage> queryWrapper, String effectiveType) {
|
||||
if (Func.isEmpty(effectiveType)) {
|
||||
return;
|
||||
|
||||
+20
@@ -406,9 +406,29 @@ public class ProjectApplyServiceImpl extends BaseServiceImpl<ProjectApplyMapper,
|
||||
.toList();
|
||||
queryWrapper.in(ProjectApply::getId, matchedIds.isEmpty() ? List.of(-1L) : matchedIds);
|
||||
}
|
||||
// 是否同步金蝶(kingdee_id 是否已回填)/ 是否同步合规(hcs_sync_status 是否 synced)
|
||||
applySyncFlagFilter(queryWrapper, projectApply.getKingdeeSynced(), projectApply.getHcsSynced());
|
||||
return queryWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 金蝶/合规同步与否筛选(列表搜索:kingdeeSynced 按 kingdee_id 空值判断,hcsSynced 按 hcs_sync_status=synced 判断)
|
||||
*/
|
||||
private void applySyncFlagFilter(LambdaQueryWrapper<ProjectApply> queryWrapper, Boolean kingdeeSynced, Boolean hcsSynced) {
|
||||
if (Boolean.TRUE.equals(kingdeeSynced)) {
|
||||
queryWrapper.isNotNull(ProjectApply::getKingdeeId).ne(ProjectApply::getKingdeeId, "");
|
||||
} else if (Boolean.FALSE.equals(kingdeeSynced)) {
|
||||
queryWrapper.and(wrapper -> wrapper.isNull(ProjectApply::getKingdeeId)
|
||||
.or().eq(ProjectApply::getKingdeeId, ""));
|
||||
}
|
||||
if (Boolean.TRUE.equals(hcsSynced)) {
|
||||
queryWrapper.eq(ProjectApply::getHcsSyncStatus, HcsSyncConstant.HCS_STATUS_SYNCED);
|
||||
} else if (Boolean.FALSE.equals(hcsSynced)) {
|
||||
queryWrapper.and(wrapper -> wrapper.isNull(ProjectApply::getHcsSyncStatus)
|
||||
.or().ne(ProjectApply::getHcsSyncStatus, HcsSyncConstant.HCS_STATUS_SYNCED));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillFundUseRisk(List<ProjectApplyVO> records) {
|
||||
if (records == null || records.isEmpty()) {
|
||||
return;
|
||||
|
||||
+45
-2
@@ -45,8 +45,12 @@ import org.springblade.transport.pojo.entity.HcsContractCheckLog;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HCS 合同特批同步支撑组件(合规特批结果接收:contractLegal/result 推送 + C103-4 定时查询兜底 + 人工确认)。
|
||||
@@ -169,7 +173,7 @@ public class HcsContractSpecSyncSupport {
|
||||
* 合同特批结果接收处理(合规 contractLegal/result 推送,2026-10-13 按合规方最新字段表修正报文结构):
|
||||
* 特批完成后合规侧同步金蝶并回调,报文 {contractType, contractCode, requestId, success, reason, kingdeeId, sourceSystem=WCCK};
|
||||
* sourceSystem 须为 WCCK(不符抛"来源系统标识错误"异常);contractCode=TMS 合同编号定位(requestId 仅请求头回显不可定位);
|
||||
* success=true 联动恢复"审批通过"(kingdeeId 随留痕保存);success=false 联动合同审批驳回走重新提交闭环(reason 随原文留痕)
|
||||
* success=true 联动恢复"审批通过"(金蝶合同ID回填 kingdee_id);success=false 联动合同审批驳回走重新提交闭环(reason 随原文留痕)
|
||||
*
|
||||
* @param param 合规推送报文
|
||||
* @return 处理结果描述
|
||||
@@ -193,9 +197,11 @@ public class HcsContractSpecSyncSupport {
|
||||
return "已留痕:success 字段缺失(contractNo=" + contractManage.getContractNo() + ")";
|
||||
}
|
||||
boolean success = param.getSuccess();
|
||||
// 成功:特批完成且金蝶同步成功,恢复"审批通过";kingdeeId 随 hcs_spec_msg/留痕保存
|
||||
// 成功:特批完成且金蝶同步成功,恢复"审批通过";金蝶合同ID回填 kingdee_id 留痕保存
|
||||
if (success) {
|
||||
syncContractOnSpecResult(contractManage, true);
|
||||
contractManage.setKingdeeId(param.getKingdeeId());
|
||||
appendKingdeeResultRecord(contractManage, "金蝶同步成功:金蝶合同ID=" + param.getKingdeeId());
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_APPROVED,
|
||||
"合同特批结果推送:金蝶同步成功,kingdeeId=" + param.getKingdeeId());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_PASS, "合同特批结果推送:" + raw);
|
||||
@@ -204,12 +210,49 @@ public class HcsContractSpecSyncSupport {
|
||||
}
|
||||
// 失败:特批流程已完成但金蝶同步失败,联动合同审批驳回,走重新编辑→提交→审批→重新特批闭环(失败单独重试机制待联调确认)
|
||||
syncContractOnSpecResult(contractManage, false);
|
||||
appendKingdeeResultRecord(contractManage, "金蝶同步失败:" + param.getReason());
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_REJECTED, "合同特批结果推送:金蝶同步失败:" + param.getReason());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_FAIL, "合同特批结果推送:" + raw);
|
||||
log.info("合同特批结果推送处理完成:contractNo={},结果=rejected,reason={}", contractManage.getContractNo(), param.getReason());
|
||||
return "特批结果已处理:rejected";
|
||||
}
|
||||
|
||||
/**
|
||||
* 金蝶同步结果回填变更记录(changeRecordJson 追加一条;系统回调无登录用户,操作人记来源标识)
|
||||
*/
|
||||
private void appendKingdeeResultRecord(ContractManage contractManage, String changeContent) {
|
||||
List<Object> records = StringUtil.isBlank(contractManage.getChangeRecordJson())
|
||||
? new ArrayList<>() : new ArrayList<>(JSONUtil.toList(contractManage.getChangeRecordJson(), Object.class));
|
||||
Map<String, Object> record = new LinkedHashMap<>();
|
||||
record.put("changeDate", LocalDate.now().toString());
|
||||
record.put("handlerUserName", "合规系统回调");
|
||||
record.put("changeType", "金蝶同步结果");
|
||||
record.put("changeContent", changeContent);
|
||||
record.put("status", contractManage.getApprovalStatus());
|
||||
record.put("statusName", approvalStatusName(contractManage.getApprovalStatus()));
|
||||
records.add(record);
|
||||
contractManage.setChangeRecordJson(JSONUtil.toJsonStr(records));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批状态中文名(变更记录展示用,未匹配回退原值)
|
||||
*/
|
||||
private String approvalStatusName(String approvalStatus) {
|
||||
if (StringUtil.isBlank(approvalStatus)) {
|
||||
return null;
|
||||
}
|
||||
return switch (approvalStatus) {
|
||||
case "draft" -> "草稿";
|
||||
case "reviewing" -> "审批中";
|
||||
case "withdrawn" -> "已撤回";
|
||||
case "approved" -> "审批通过";
|
||||
case "rejected" -> "已驳回";
|
||||
case "change_reviewing" -> "变更审批中";
|
||||
case "change_approved" -> "变更审批通过";
|
||||
default -> approvalStatus;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 ERP 合同编号定位合同(合规推送 contractCode,合同编号唯一)
|
||||
*/
|
||||
|
||||
+133
@@ -25,6 +25,8 @@
|
||||
*/
|
||||
package org.springblade.transport.support.hcs;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
@@ -36,6 +38,7 @@ import org.springblade.hcs.dto.HcsPageQueryDTO;
|
||||
import org.springblade.hcs.dto.HcsPageVO;
|
||||
import org.springblade.hcs.dto.HcsProjectAddDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectFormDataDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectPropsDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectUpdateDTO;
|
||||
import org.springblade.hcs.dto.HcsProjectVO;
|
||||
@@ -47,12 +50,17 @@ import org.springblade.hcs.exception.HcsException;
|
||||
import org.springblade.hcs.feign.IHcsClient;
|
||||
import org.springblade.transport.constant.HcsFieldConstant;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.mapper.HcsContractCheckLogMapper;
|
||||
import org.springblade.transport.mapper.ProjectApplyMapper;
|
||||
import org.springblade.transport.pojo.entity.HcsContractCheckLog;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -76,9 +84,15 @@ public class HcsProjectSyncSupport {
|
||||
*/
|
||||
private static final BigDecimal WAN_TO_YUAN = BigDecimal.valueOf(10000);
|
||||
|
||||
/**
|
||||
* 项目金蝶同步结果回传来源系统标识(合规方接口文档约定固定传 WCCK)
|
||||
*/
|
||||
private static final String LEGAL_CALLBACK_SRC_SYSTEM = "WCCK";
|
||||
|
||||
private final IHcsClient hcsClient;
|
||||
private final HcsProperties hcsProperties;
|
||||
private final ProjectApplyMapper projectApplyMapper;
|
||||
private final HcsContractCheckLogMapper hcsContractCheckLogMapper;
|
||||
|
||||
/**
|
||||
* 推送项目立项到 HCS:判重 → 新建(C102-1)/ 更新(C102-2)→ 回填合规 fdId 置 synced
|
||||
@@ -257,6 +271,125 @@ public class HcsProjectSyncSupport {
|
||||
return page.getContent().get(0).getFdId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目金蝶同步结果接收处理(合规 projectLegal/result 推送,D902):
|
||||
* sourceSystem 须为 WCCK(不符抛"来源系统标识错误"异常);projectCode=TMS 项目编号定位(requestId 仅 X-ERP-Request-Id 回显不可定位);
|
||||
* success=true 回填金蝶项目ID/编号(kingdeeId/kingdeeNumber);success=false 记录失败原因;原文留痕 blade_hcs_check_log
|
||||
*
|
||||
* @param param 合规推送报文
|
||||
* @return 处理结果描述
|
||||
*/
|
||||
public String handleLegalResult(HcsProjectLegalResultDTO param) {
|
||||
// 来源系统校验:仅接受合规方约定的 WCCK,防伪造回传
|
||||
if (!LEGAL_CALLBACK_SRC_SYSTEM.equalsIgnoreCase(param == null ? null : param.getSourceSystem())) {
|
||||
log.warn("项目金蝶同步结果推送来源系统标识错误:sourceSystem={}", param == null ? null : param.getSourceSystem());
|
||||
throw new ServiceException("来源系统标识错误");
|
||||
}
|
||||
String raw = JSONUtil.toJsonStr(param);
|
||||
ProjectApply projectApply = locateProjectByCode(param.getProjectCode());
|
||||
if (projectApply == null) {
|
||||
log.warn("项目金蝶同步结果推送未定位到项目:projectCode={},报文已留痕", param.getProjectCode());
|
||||
writeCallbackLog(null, HcsSyncConstant.CHECK_RESULT_SKIP, "项目金蝶同步结果推送未定位到项目:" + raw);
|
||||
return "已留痕:未定位到项目(projectCode=" + param.getProjectCode() + ")";
|
||||
}
|
||||
if (param.getSuccess() == null) {
|
||||
log.warn("项目金蝶同步结果推送 success 字段缺失:projectCode={},报文已留痕", projectApply.getProjectCode());
|
||||
writeCallbackLog(projectApply, HcsSyncConstant.CHECK_RESULT_SKIP, "项目金蝶同步结果推送 success 字段缺失:" + raw);
|
||||
return "已留痕:success 字段缺失(projectCode=" + projectApply.getProjectCode() + ")";
|
||||
}
|
||||
if (param.getSuccess()) {
|
||||
// 成功:回填金蝶项目ID/编号,同步结果信息随 hcs_sync_msg 留痕
|
||||
projectApply.setKingdeeId(param.getKingdeeId());
|
||||
projectApply.setKingdeeNumber(param.getProjectNumber());
|
||||
projectApply.setHcsSyncMsg(HcsSyncConstant.truncateMsg("项目金蝶同步结果回调:金蝶同步成功,kingdeeId=" + param.getKingdeeId()
|
||||
+ ",projectNumber=" + param.getProjectNumber()));
|
||||
appendKingdeeResultRecord(projectApply, "金蝶同步成功:金蝶项目ID=" + param.getKingdeeId()
|
||||
+ ",金蝶项目编号=" + param.getProjectNumber());
|
||||
projectApplyMapper.updateById(projectApply);
|
||||
writeCallbackLog(projectApply, HcsSyncConstant.CHECK_RESULT_PASS, "项目金蝶同步结果推送:" + raw);
|
||||
log.info("项目金蝶同步结果推送处理完成:projectCode={},结果=success,kingdeeId={}", projectApply.getProjectCode(), param.getKingdeeId());
|
||||
return "金蝶同步结果已处理:success";
|
||||
}
|
||||
// 失败:记录失败原因(金蝶同步在合规侧完成,TMS 不重推,失败重试由合规侧负责)
|
||||
projectApply.setHcsSyncMsg(HcsSyncConstant.truncateMsg("项目金蝶同步结果回调:金蝶同步失败:" + param.getReason()));
|
||||
appendKingdeeResultRecord(projectApply, "金蝶同步失败:" + param.getReason());
|
||||
projectApplyMapper.updateById(projectApply);
|
||||
writeCallbackLog(projectApply, HcsSyncConstant.CHECK_RESULT_FAIL, "项目金蝶同步结果推送:" + raw);
|
||||
log.info("项目金蝶同步结果推送处理完成:projectCode={},结果=fail,reason={}", projectApply.getProjectCode(), param.getReason());
|
||||
return "金蝶同步结果已处理:fail";
|
||||
}
|
||||
|
||||
/**
|
||||
* 金蝶同步结果回填变更记录(changeRecordJson 追加一条;系统回调无登录用户,操作人记来源标识)
|
||||
*/
|
||||
private void appendKingdeeResultRecord(ProjectApply projectApply, String changeContent) {
|
||||
List<Object> records = StringUtil.isBlank(projectApply.getChangeRecordJson())
|
||||
? new ArrayList<>() : new ArrayList<>(JSONUtil.toList(projectApply.getChangeRecordJson(), Object.class));
|
||||
Map<String, Object> record = new LinkedHashMap<>();
|
||||
record.put("changeDate", LocalDate.now().toString());
|
||||
record.put("handlerUserName", "合规系统回调");
|
||||
record.put("changeType", "金蝶同步结果");
|
||||
record.put("changeContent", changeContent);
|
||||
record.put("status", projectApply.getApprovalStatus());
|
||||
record.put("statusName", approvalStatusName(projectApply.getApprovalStatus()));
|
||||
records.add(record);
|
||||
projectApply.setChangeRecordJson(JSONUtil.toJsonStr(records));
|
||||
}
|
||||
|
||||
/**
|
||||
* 审批状态中文名(变更记录展示用,未匹配回退原值)
|
||||
*/
|
||||
private String approvalStatusName(String approvalStatus) {
|
||||
if (StringUtil.isBlank(approvalStatus)) {
|
||||
return null;
|
||||
}
|
||||
return switch (approvalStatus) {
|
||||
case "draft" -> "草稿";
|
||||
case "reviewing" -> "审批中";
|
||||
case "withdrawn" -> "已撤回";
|
||||
case "approved" -> "审批通过";
|
||||
case "rejected" -> "已驳回";
|
||||
case "change_reviewing" -> "变更审批中";
|
||||
case "change_rejected" -> "变更驳回";
|
||||
case "change_approved" -> "变更审批通过";
|
||||
case "voided" -> "已作废";
|
||||
default -> approvalStatus;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 ERP 项目编号定位项目(合规推送 projectCode,对应 C102 fd_num)
|
||||
*/
|
||||
private ProjectApply locateProjectByCode(String projectCode) {
|
||||
if (StringUtil.isBlank(projectCode)) {
|
||||
return null;
|
||||
}
|
||||
return projectApplyMapper.selectOne(Wrappers.<ProjectApply>lambdaQuery()
|
||||
.eq(ProjectApply::getProjectCode, projectCode.trim())
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目金蝶同步结果推送留痕(blade_hcs_check_log,biz_type=project,项目未定位到时 bizId 为空,异常不影响主流程)
|
||||
*/
|
||||
private void writeCallbackLog(ProjectApply projectApply, String result, String msg) {
|
||||
try {
|
||||
HcsContractCheckLog checkLog = new HcsContractCheckLog();
|
||||
if (projectApply != null) {
|
||||
checkLog.setTenantId(projectApply.getTenantId());
|
||||
checkLog.setBizId(projectApply.getId());
|
||||
}
|
||||
checkLog.setBizType(HcsSyncConstant.BIZ_TYPE_PROJECT);
|
||||
checkLog.setCheckType(HcsSyncConstant.CHECK_TYPE_KINGDEE);
|
||||
checkLog.setResult(result);
|
||||
checkLog.setMsg(StringUtil.left(msg, HcsSyncConstant.SYNC_MSG_MAX_LENGTH));
|
||||
checkLog.setCreateTime(LocalDateTime.now());
|
||||
hcsContractCheckLogMapper.insert(checkLog);
|
||||
} catch (Exception exception) {
|
||||
log.error("项目金蝶同步结果推送留痕写入失败:projectCode={}", projectApply == null ? null : projectApply.getProjectCode(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条手动重推:重置为待同步并清零重试计数后立即推送(页面"推送合规/重推"按钮入口)
|
||||
*
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.hcs.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 项目金蝶同步完成后合规系统回传 TMS 的结果报文(D902,POST /hcs/projectLegal/result,iPaaS 网关 /gwzh/HTHG/projectLegal/result)
|
||||
* <p>
|
||||
* 定位项目用 projectCode(ERP 项目编号,对应 C102 请求中的 fd_num),
|
||||
* requestId 仅是 C102 请求头 X-ERP-Request-Id 原样回传、不能用于定位;
|
||||
* 未知或过期的 requestId 合规侧返回失败。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class HcsProjectLegalResultDTO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* ERP 项目编号(TMS blade_project_apply.project_code,对应 C102 请求中的 fd_num,定位项目用)
|
||||
*/
|
||||
private String projectCode;
|
||||
/**
|
||||
* C102 请求头 X-ERP-Request-Id 的值,原样返回(不用于定位)
|
||||
*/
|
||||
private String requestId;
|
||||
/**
|
||||
* 金蝶同步成功传 true,失败传 false
|
||||
*/
|
||||
private Boolean success;
|
||||
/**
|
||||
* 金蝶项目 ID;成功时用于回填保存
|
||||
*/
|
||||
private String kingdeeId;
|
||||
/**
|
||||
* 金蝶项目编号;成功时用于回填保存
|
||||
*/
|
||||
private String projectNumber;
|
||||
/**
|
||||
* 金蝶同步失败原因;失败时建议填写
|
||||
*/
|
||||
private String reason;
|
||||
/**
|
||||
* 来源系统标识,约定传 WCCK(最长 64 字符)
|
||||
*/
|
||||
private String sourceSystem;
|
||||
}
|
||||
+10
-2
@@ -237,11 +237,13 @@ blade:
|
||||
- /feign/client/businessProcess/getProcessInfo
|
||||
- /feign/client/customerArchive/syncProcessNode
|
||||
- /feign/client/mkProcess/**
|
||||
# HCS 合同/付款特批结果回传:合规系统经 iPaaS 网关无令牌调用,免登免授权
|
||||
# HCS 合同/付款特批结果回传 + 项目金蝶同步结果回传:合规系统经 iPaaS 网关无令牌调用,免登免授权
|
||||
- /blade-transport/hcs/contractLegal/result
|
||||
- /hcs/contractLegal/result
|
||||
- /blade-transport/hcs/paymentLegal/result
|
||||
- /hcs/paymentLegal/result
|
||||
- /blade-transport/hcs/projectLegal/result
|
||||
- /hcs/projectLegal/result
|
||||
#授权认证配置
|
||||
auth:
|
||||
- method: ALL
|
||||
@@ -269,7 +271,7 @@ blade:
|
||||
- method: POST
|
||||
pattern: /kingdee-bfi/**
|
||||
expression: "permitAll()"
|
||||
# HCS 合同/付款特批结果回传:合规系统经 iPaaS 网关无令牌调用,免登免授权
|
||||
# HCS 合同/付款特批结果回传 + 项目金蝶同步结果回传:合规系统经 iPaaS 网关无令牌调用,免登免授权
|
||||
- method: POST
|
||||
pattern: /blade-transport/hcs/contractLegal/result
|
||||
expression: "permitAll()"
|
||||
@@ -282,6 +284,12 @@ blade:
|
||||
- method: POST
|
||||
pattern: /hcs/paymentLegal/result
|
||||
expression: "permitAll()"
|
||||
- method: POST
|
||||
pattern: /blade-transport/hcs/projectLegal/result
|
||||
expression: "permitAll()"
|
||||
- method: POST
|
||||
pattern: /hcs/projectLegal/result
|
||||
expression: "permitAll()"
|
||||
#基础认证配置
|
||||
basic:
|
||||
- method: ALL
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
-- HCS 金蝶同步结果回调回填字段:
|
||||
-- blade_project_apply 追加金蝶项目ID/编号(D902 项目金蝶同步结果回调 projectLegal/result 回填);
|
||||
-- blade_contract_manage 追加金蝶合同ID(合同特批金蝶同步结果回调 contractLegal/result 回填)。
|
||||
-- 通过 information_schema 判断字段是否存在,可重复执行。
|
||||
SET @db_name = DATABASE();
|
||||
|
||||
-- ==================== blade_project_apply(D902 项目金蝶同步结果回填) ====================
|
||||
|
||||
SET @sql = IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = @db_name AND table_name = 'blade_project_apply' AND column_name = 'kingdee_id'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE blade_project_apply ADD COLUMN kingdee_id varchar(64) DEFAULT NULL COMMENT ''金蝶项目ID(D902 回调回填)'''
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @sql = IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = @db_name AND table_name = 'blade_project_apply' AND column_name = 'kingdee_number'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE blade_project_apply ADD COLUMN kingdee_number varchar(64) DEFAULT NULL COMMENT ''金蝶项目编号(D902 回调回填)'''
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- ==================== blade_contract_manage(contractLegal/result 金蝶同步结果回填) ====================
|
||||
|
||||
SET @sql = IF(
|
||||
EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = @db_name AND table_name = 'blade_contract_manage' AND column_name = 'kingdee_id'),
|
||||
'SELECT 1',
|
||||
'ALTER TABLE blade_contract_manage ADD COLUMN kingdee_id varchar(64) DEFAULT NULL COMMENT ''金蝶合同ID(contractLegal/result 回调回填)'''
|
||||
);
|
||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
Reference in New Issue
Block a user