✨ 新增客商档案同步金蝶功能(A902-1查询回填+A902-2审批推送)
This commit is contained in:
+9
@@ -158,4 +158,13 @@ public class CustomerArchive extends TenantEntity {
|
|||||||
@Schema(description = "审核通过时间")
|
@Schema(description = "审核通过时间")
|
||||||
private LocalDateTime approvedTime;
|
private LocalDateTime approvedTime;
|
||||||
|
|
||||||
|
@Schema(description = "金蝶协同状态:none未推送/exist金蝶已有/synced已推送/failed推送失败")
|
||||||
|
private String kingdeeStatus;
|
||||||
|
|
||||||
|
@Schema(description = "金蝶推送失败原因")
|
||||||
|
private String kingdeeFailReason;
|
||||||
|
|
||||||
|
@Schema(description = "金蝶推送时间")
|
||||||
|
private LocalDateTime kingdeeSyncTime;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+69
@@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* 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.transport.constant;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客商档案金蝶(星瀚)协同常量
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
public class CustomerBfiConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送金蝶固定值:业务职能全选(1销售,2结算,3付款,4收货)
|
||||||
|
*/
|
||||||
|
public static final String BIZ_FUNCTION_ALL = "1,2,3,4";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送金蝶固定值:伙伴类型兜底编码(民营企业),客商性质未命中映射时使用
|
||||||
|
*/
|
||||||
|
public static final String DEFAULT_PARTNER_TYPE = "6";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 营业期限类型:长期(TMS 表单文本)
|
||||||
|
*/
|
||||||
|
public static final String BUSINESS_TERM_LONG = "长期";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客商性质(nature_of_client 字典 dictKey)→ 金蝶伙伴类型编码
|
||||||
|
* 键名以数据库字典 nature_of_client 实际 dictKey 为准核对;未命中走 DEFAULT_PARTNER_TYPE
|
||||||
|
*/
|
||||||
|
public static final Map<String, String> NATURE_TYPE_MAPPING = Map.of(
|
||||||
|
"企业法人", "1",
|
||||||
|
"个人", "4",
|
||||||
|
"个体工商户", "5",
|
||||||
|
"民营企业", "6",
|
||||||
|
"国有企业", "8",
|
||||||
|
"政府事业单位", "9",
|
||||||
|
"其他", "12"
|
||||||
|
);
|
||||||
|
|
||||||
|
private CustomerBfiConstants() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* 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.transport.controller;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springblade.core.boot.ctrl.BladeController;
|
||||||
|
import org.springblade.core.tool.api.R;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiCustomerVO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
|
||||||
|
import org.springblade.thirdparty.bfi.service.IBfiService;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 业财融合(BFI)客商查询控制器
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RequestMapping("/bfi")
|
||||||
|
@Tag(name = "BFI客商", description = "业财融合客商查询接口")
|
||||||
|
public class BfiCustomerController extends BladeController {
|
||||||
|
|
||||||
|
private final IBfiService bfiService;
|
||||||
|
|
||||||
|
@GetMapping("/customer/query-by-credit-code")
|
||||||
|
@Operation(summary = "按统一社会信用代码查询金蝶客商", description = "新增客商时查询金蝶是否已有该客商,已审核则回填工商信息")
|
||||||
|
public R<BfiCustomerVO> queryByCreditCode(
|
||||||
|
@Parameter(description = "统一社会信用代码", required = true) @RequestParam String creditCode) {
|
||||||
|
if (StringUtil.isBlank(creditCode)) {
|
||||||
|
return R.fail("统一社会信用代码不能为空");
|
||||||
|
}
|
||||||
|
BfiCustomerQueryDTO param = new BfiCustomerQueryDTO();
|
||||||
|
param.setPageNo(1);
|
||||||
|
param.setPageSize(1);
|
||||||
|
param.setSocietycreditcode(List.of(creditCode.trim().toUpperCase()));
|
||||||
|
BfiPageVO<BfiCustomerVO> page = bfiService.queryCustomer(param);
|
||||||
|
List<BfiCustomerVO> rows = page == null ? null : page.getRows();
|
||||||
|
return R.data(rows == null || rows.isEmpty() ? null : rows.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+22
-2
@@ -31,6 +31,7 @@ import io.swagger.v3.oas.annotations.Parameter;
|
|||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.boot.ctrl.BladeController;
|
import org.springblade.core.boot.ctrl.BladeController;
|
||||||
import org.springblade.core.excel.util.ExcelUtil;
|
import org.springblade.core.excel.util.ExcelUtil;
|
||||||
import org.springblade.core.mp.support.Condition;
|
import org.springblade.core.mp.support.Condition;
|
||||||
@@ -60,6 +61,7 @@ import java.util.List;
|
|||||||
*
|
*
|
||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
|
@Slf4j
|
||||||
@RestController
|
@RestController
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@PreAuth(menu = "customer_archive")
|
@PreAuth(menu = "customer_archive")
|
||||||
@@ -133,13 +135,21 @@ public class CustomerArchiveController extends BladeController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 审核通过
|
* 审核通过(通过后自动推送金蝶,推送失败不影响审批结果)
|
||||||
*/
|
*/
|
||||||
@PostMapping("/approve")
|
@PostMapping("/approve")
|
||||||
@ApiOperationSupport(order = 6)
|
@ApiOperationSupport(order = 6)
|
||||||
@Operation(summary = "审核通过", description = "传入id")
|
@Operation(summary = "审核通过", description = "传入id")
|
||||||
public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||||
return R.status(customerArchiveService.approve(id));
|
boolean result = customerArchiveService.approve(id);
|
||||||
|
if (result) {
|
||||||
|
try {
|
||||||
|
customerArchiveService.syncKingdee(id);
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.error("客商审批通过后推送金蝶异常,id={}", id, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return R.status(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -173,6 +183,16 @@ public class CustomerArchiveController extends BladeController {
|
|||||||
return R.status(customerArchiveService.removeDraft(ids));
|
return R.status(customerArchiveService.removeDraft(ids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送金蝶(审批通过后失败重推入口)
|
||||||
|
*/
|
||||||
|
@PostMapping("/sync-kingdee")
|
||||||
|
@ApiOperationSupport(order = 12)
|
||||||
|
@Operation(summary = "推送金蝶", description = "传入id,仅审批通过的客商可推送,已推送/金蝶已有自动幂等跳过")
|
||||||
|
public R<CustomerArchive> syncKingdee(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||||
|
return R.data(customerArchiveService.syncKingdee(id));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 评分明细模板
|
* 评分明细模板
|
||||||
*/
|
*/
|
||||||
|
|||||||
+3
@@ -105,4 +105,7 @@ public class CustomerArchiveExcel implements Serializable {
|
|||||||
@ExcelProperty("审核通过时间")
|
@ExcelProperty("审核通过时间")
|
||||||
private LocalDateTime approvedTime;
|
private LocalDateTime approvedTime;
|
||||||
|
|
||||||
|
@ExcelProperty("金蝶同步状态")
|
||||||
|
private String kingdeeStatusName;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -46,6 +46,9 @@
|
|||||||
<result column="current_node" property="currentNode"/>
|
<result column="current_node" property="currentNode"/>
|
||||||
<result column="current_processor" property="currentProcessor"/>
|
<result column="current_processor" property="currentProcessor"/>
|
||||||
<result column="approved_time" property="approvedTime"/>
|
<result column="approved_time" property="approvedTime"/>
|
||||||
|
<result column="kingdee_status" property="kingdeeStatus"/>
|
||||||
|
<result column="kingdee_fail_reason" property="kingdeeFailReason"/>
|
||||||
|
<result column="kingdee_sync_time" property="kingdeeSyncTime"/>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<select id="selectCustomerArchivePage" resultMap="customerArchiveResultMap">
|
<select id="selectCustomerArchivePage" resultMap="customerArchiveResultMap">
|
||||||
@@ -92,7 +95,10 @@
|
|||||||
approval_status,
|
approval_status,
|
||||||
current_node,
|
current_node,
|
||||||
current_processor,
|
current_processor,
|
||||||
approved_time
|
approved_time,
|
||||||
|
kingdee_status,
|
||||||
|
kingdee_fail_reason,
|
||||||
|
kingdee_sync_time
|
||||||
FROM blade_customer_archive
|
FROM blade_customer_archive
|
||||||
WHERE is_deleted = 0
|
WHERE is_deleted = 0
|
||||||
<if test="userId != null">
|
<if test="userId != null">
|
||||||
|
|||||||
+8
@@ -124,6 +124,14 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
|
|||||||
*/
|
*/
|
||||||
boolean removeDraft(String ids);
|
boolean removeDraft(String ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推送金蝶(A902-1 复查 + A902-2 保存),幂等:已推送/金蝶已有直接返回
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return 更新后的客商档案
|
||||||
|
*/
|
||||||
|
CustomerArchive syncKingdee(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 基于启用评分量化表生成评分记录
|
* 基于启用评分量化表生成评分记录
|
||||||
*
|
*
|
||||||
|
|||||||
+138
@@ -29,12 +29,22 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
import org.springblade.core.tool.jackson.JsonUtil;
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
import org.springblade.core.tool.utils.BeanUtil;
|
import org.springblade.core.tool.utils.BeanUtil;
|
||||||
import org.springblade.core.tool.utils.Func;
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.thirdparty.bfi.constant.BfiConstant;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultErrorVO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiCustomerVO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
|
||||||
|
import org.springblade.thirdparty.bfi.service.IBfiService;
|
||||||
import org.springblade.transport.excel.CustomerArchiveExcel;
|
import org.springblade.transport.excel.CustomerArchiveExcel;
|
||||||
import org.springblade.transport.mapper.CreditRatingStandardMapper;
|
import org.springblade.transport.mapper.CreditRatingStandardMapper;
|
||||||
import org.springblade.transport.mapper.CreditScoreCategoryMapper;
|
import org.springblade.transport.mapper.CreditScoreCategoryMapper;
|
||||||
@@ -101,6 +111,7 @@ import java.util.stream.Collectors;
|
|||||||
* @author Chill
|
* @author Chill
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveMapper, CustomerArchive> implements ICustomerArchiveService {
|
public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveMapper, CustomerArchive> implements ICustomerArchiveService {
|
||||||
|
|
||||||
@@ -112,6 +123,10 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
private static final String APPROVAL_REVIEWING = "reviewing";
|
private static final String APPROVAL_REVIEWING = "reviewing";
|
||||||
private static final String APPROVAL_APPROVED = "approved";
|
private static final String APPROVAL_APPROVED = "approved";
|
||||||
private static final String APPROVAL_REJECTED = "rejected";
|
private static final String APPROVAL_REJECTED = "rejected";
|
||||||
|
private static final String KINGDEE_STATUS_NONE = "none";
|
||||||
|
private static final String KINGDEE_STATUS_EXIST = "exist";
|
||||||
|
private static final String KINGDEE_STATUS_SYNCED = "synced";
|
||||||
|
private static final String KINGDEE_STATUS_FAILED = "failed";
|
||||||
private static final String CUSTOMER_CODE_PREFIX = "KS";
|
private static final String CUSTOMER_CODE_PREFIX = "KS";
|
||||||
/**
|
/**
|
||||||
* 变更快照忽略字段:明细每次保存会重建主键/审计字段,不能作为“是否有修改”的依据
|
* 变更快照忽略字段:明细每次保存会重建主键/审计字段,不能作为“是否有修改”的依据
|
||||||
@@ -137,6 +152,8 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
private final CreditRatingStandardMapper standardMapper;
|
private final CreditRatingStandardMapper standardMapper;
|
||||||
private final UserCustomerScopeMapper userCustomerScopeMapper;
|
private final UserCustomerScopeMapper userCustomerScopeMapper;
|
||||||
private final PaymentApplicationMapper paymentApplicationMapper;
|
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||||
|
private final IBfiService bfiService;
|
||||||
|
private final CustomerBfiConverter customerBfiConverter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IPage<CustomerArchiveVO> selectCustomerArchivePage(IPage<CustomerArchiveVO> page, CustomerArchiveVO customer) {
|
public IPage<CustomerArchiveVO> selectCustomerArchivePage(IPage<CustomerArchiveVO> page, CustomerArchiveVO customer) {
|
||||||
@@ -268,6 +285,112 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
return updateById(after);
|
return updateById(after);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public CustomerArchive syncKingdee(Long id) {
|
||||||
|
CustomerArchive customer = ensureCustomerAccessible(id);
|
||||||
|
if (!Objects.equals(customer.getApprovalStatus(), APPROVAL_APPROVED)) {
|
||||||
|
throw new ServiceException("仅审批通过的客商允许推送金蝶");
|
||||||
|
}
|
||||||
|
if (List.of(KINGDEE_STATUS_SYNCED, KINGDEE_STATUS_EXIST).contains(customer.getKingdeeStatus())) {
|
||||||
|
// 幂等:已推送或金蝶已有档案,直接返回
|
||||||
|
return customer;
|
||||||
|
}
|
||||||
|
CustomerArchive after = copyCustomer(customer);
|
||||||
|
after.setKingdeeSyncTime(LocalDateTime.now());
|
||||||
|
BfiCustomerVO kingdeeCustomer = queryKingdeeCustomer(customer.getUnifiedCreditCode());
|
||||||
|
if (kingdeeCustomer != null && BfiConstant.BILL_STATUS_AUDITED.equals(kingdeeCustomer.getStatus())) {
|
||||||
|
// 金蝶已有已审核档案:跳过推送
|
||||||
|
after.setKingdeeStatus(KINGDEE_STATUS_EXIST);
|
||||||
|
after.setKingdeeFailReason(null);
|
||||||
|
addChangeRecord(id, "金蝶已存在该客商档案,跳过推送", customer, after);
|
||||||
|
updateById(after);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
if (kingdeeCustomer != null) {
|
||||||
|
// 金蝶侧暂存/已提交:审核中,置失败待重推
|
||||||
|
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||||
|
after.setKingdeeFailReason("金蝶侧该客商审核中,请稍后重推");
|
||||||
|
addChangeRecord(id, "推送金蝶失败:金蝶侧客商审核中", customer, after);
|
||||||
|
updateById(after);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
pushCustomerToKingdee(id, customer, after);
|
||||||
|
updateById(after);
|
||||||
|
return after;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装 A902-2 报文推送金蝶并回写协同状态
|
||||||
|
*/
|
||||||
|
private void pushCustomerToKingdee(Long id, CustomerArchive customer, CustomerArchive after) {
|
||||||
|
try {
|
||||||
|
BfiBillSaveResultVO result = bfiService.saveCustomer(customerBfiConverter.buildSaveDTO(detail(id)));
|
||||||
|
BfiBillSaveResultItemVO item = firstSaveItem(result);
|
||||||
|
if (item != null && Boolean.TRUE.equals(item.getBillStatus())) {
|
||||||
|
after.setKingdeeStatus(KINGDEE_STATUS_SYNCED);
|
||||||
|
after.setKingdeeFailReason(null);
|
||||||
|
addChangeRecord(id, "推送金蝶成功,金蝶客户编码与客商编号一致:" + customer.getCustomerCode(), customer, after);
|
||||||
|
} else {
|
||||||
|
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||||
|
after.setKingdeeFailReason(buildSaveFailReason(result, item));
|
||||||
|
addChangeRecord(id, "推送金蝶失败:" + after.getKingdeeFailReason(), customer, after);
|
||||||
|
}
|
||||||
|
} catch (Exception exception) {
|
||||||
|
log.error("客商推送金蝶失败,id={},unifiedCreditCode={}", id, customer.getUnifiedCreditCode(), exception);
|
||||||
|
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||||
|
after.setKingdeeFailReason(truncateText(exception.getMessage() == null ? "金蝶接口调用异常" : exception.getMessage()));
|
||||||
|
addChangeRecord(id, "推送金蝶失败:" + after.getKingdeeFailReason(), customer, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A902-1 按统一社会信用代码查询金蝶客商,无数据返回 null
|
||||||
|
*/
|
||||||
|
private BfiCustomerVO queryKingdeeCustomer(String unifiedCreditCode) {
|
||||||
|
BfiCustomerQueryDTO param = new BfiCustomerQueryDTO();
|
||||||
|
param.setPageNo(1);
|
||||||
|
param.setPageSize(1);
|
||||||
|
param.setSocietycreditcode(List.of(unifiedCreditCode));
|
||||||
|
BfiPageVO<BfiCustomerVO> page = bfiService.queryCustomer(param);
|
||||||
|
List<BfiCustomerVO> rows = page == null ? null : page.getRows();
|
||||||
|
return rows == null || rows.isEmpty() ? null : rows.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BfiBillSaveResultItemVO firstSaveItem(BfiBillSaveResultVO result) {
|
||||||
|
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return result.getResult().get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildSaveFailReason(BfiBillSaveResultVO result, BfiBillSaveResultItemVO item) {
|
||||||
|
if (item != null && Func.isNotEmpty(item.getErrors())) {
|
||||||
|
String messages = item.getErrors().stream()
|
||||||
|
.map(BfiBillSaveResultErrorVO::getRowMsg)
|
||||||
|
.filter(Func::isNotEmpty)
|
||||||
|
.flatMap(List::stream)
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
if (Func.isEmpty(messages)) {
|
||||||
|
messages = item.getErrors().stream()
|
||||||
|
.map(error -> String.valueOf(error.getEntityKey()))
|
||||||
|
.collect(Collectors.joining(";"));
|
||||||
|
}
|
||||||
|
return truncateText(messages);
|
||||||
|
}
|
||||||
|
if (result != null && Func.isNotEmpty(result.getFailCount())) {
|
||||||
|
return truncateText("金蝶返回失败数量:" + result.getFailCount());
|
||||||
|
}
|
||||||
|
return "金蝶返回结果为空";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String truncateText(String text) {
|
||||||
|
if (text == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return text.length() > 500 ? text.substring(0, 500) : text;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public boolean changeStatus(Long id, Integer status) {
|
public boolean changeStatus(Long id, Integer status) {
|
||||||
@@ -343,6 +466,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
customer.setDeptName(trimToEmpty(customer.getDeptName()));
|
customer.setDeptName(trimToEmpty(customer.getDeptName()));
|
||||||
customer.setAccessType(Func.isEmpty(customer.getAccessType()) ? ACCESS_TEMPORARY : customer.getAccessType());
|
customer.setAccessType(Func.isEmpty(customer.getAccessType()) ? ACCESS_TEMPORARY : customer.getAccessType());
|
||||||
customer.setApprovalStatus(Func.isEmpty(customer.getApprovalStatus()) ? APPROVAL_DRAFT : customer.getApprovalStatus());
|
customer.setApprovalStatus(Func.isEmpty(customer.getApprovalStatus()) ? APPROVAL_DRAFT : customer.getApprovalStatus());
|
||||||
|
customer.setKingdeeStatus(Func.isEmpty(customer.getKingdeeStatus()) ? KINGDEE_STATUS_NONE : customer.getKingdeeStatus());
|
||||||
if (Func.isEmpty(customer.getStatus())) {
|
if (Func.isEmpty(customer.getStatus())) {
|
||||||
customer.setStatus(STATUS_ENABLED);
|
customer.setStatus(STATUS_ENABLED);
|
||||||
}
|
}
|
||||||
@@ -1266,6 +1390,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
excel.setContactPhone(customer.getContactPhone());
|
excel.setContactPhone(customer.getContactPhone());
|
||||||
excel.setLegalPerson(customer.getLegalPerson());
|
excel.setLegalPerson(customer.getLegalPerson());
|
||||||
excel.setCreateTime(customer.getCreateTime());
|
excel.setCreateTime(customer.getCreateTime());
|
||||||
|
excel.setKingdeeStatusName(kingdeeStatusName(customer.getKingdeeStatus()));
|
||||||
return excel;
|
return excel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1286,6 +1411,19 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
|||||||
return "草稿";
|
return "草稿";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String kingdeeStatusName(String kingdeeStatus) {
|
||||||
|
if (Objects.equals(kingdeeStatus, KINGDEE_STATUS_SYNCED)) {
|
||||||
|
return "已推送";
|
||||||
|
}
|
||||||
|
if (Objects.equals(kingdeeStatus, KINGDEE_STATUS_EXIST)) {
|
||||||
|
return "金蝶已有";
|
||||||
|
}
|
||||||
|
if (Objects.equals(kingdeeStatus, KINGDEE_STATUS_FAILED)) {
|
||||||
|
return "推送失败";
|
||||||
|
}
|
||||||
|
return "未推送";
|
||||||
|
}
|
||||||
|
|
||||||
private void validateLength(String value, int maxLength, String message) {
|
private void validateLength(String value, int maxLength, String message) {
|
||||||
if (Func.isNotEmpty(value) && value.length() > maxLength) {
|
if (Func.isNotEmpty(value) && value.length() > maxLength) {
|
||||||
throw new ServiceException(message);
|
throw new ServiceException(message);
|
||||||
|
|||||||
+147
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* 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.transport.service.impl;
|
||||||
|
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springblade.core.tool.utils.Func;
|
||||||
|
import org.springblade.core.tool.utils.StringUtil;
|
||||||
|
import org.springblade.system.cache.DictBizCache;
|
||||||
|
import org.springblade.system.pojo.entity.DictBiz;
|
||||||
|
import org.springblade.thirdparty.bfi.constant.BfiConstant;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerGroupEntryDTO;
|
||||||
|
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerSaveDTO;
|
||||||
|
import org.springblade.transport.constant.CustomerBfiConstants;
|
||||||
|
import org.springblade.transport.pojo.vo.CustomerArchiveVO;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客商档案金蝶(星瀚)报文转换器
|
||||||
|
*
|
||||||
|
* @author Chill
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomerBfiConverter {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据来源系统编码,默认 WLTMS(运输TMS)
|
||||||
|
*/
|
||||||
|
@Value("${thirdParty.bfi.customer.srcSys:WLTMS}")
|
||||||
|
private String srcSys;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建组织.编码,默认 WLTMS,需与财务确认
|
||||||
|
*/
|
||||||
|
@Value("${thirdParty.bfi.customer.createOrg:WLTMS}")
|
||||||
|
private String createOrg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组装 A902-2 客户保存参数
|
||||||
|
*
|
||||||
|
* @param customer 客商档案
|
||||||
|
* @return 客户保存参数
|
||||||
|
*/
|
||||||
|
public BfiCustomerSaveDTO buildSaveDTO(CustomerArchiveVO customer) {
|
||||||
|
BfiCustomerSaveDTO param = new BfiCustomerSaveDTO();
|
||||||
|
// 金蝶客户编码与 TMS 客商编号保持一致
|
||||||
|
param.setNumber(customer.getCustomerCode());
|
||||||
|
param.setStatus(BfiConstant.BILL_STATUS_SUBMITTED);
|
||||||
|
param.setName(customer.getFullName());
|
||||||
|
param.setSocietycreditcode(customer.getUnifiedCreditCode());
|
||||||
|
param.setTxRegisterNo(customer.getUnifiedCreditCode());
|
||||||
|
param.setArtificialperson(customer.getLegalPerson());
|
||||||
|
param.setType(resolvePartnerType(customer.getCustomerNature()));
|
||||||
|
param.setBizpartnerAddress(customer.getRegisteredAddress());
|
||||||
|
param.setBusinessscope(buildBusinessScopeText(customer.getBusinessScope()));
|
||||||
|
param.setBusinessterm(buildBusinessTerm(customer.getBusinessTermType(), customer.getBusinessEndDate()));
|
||||||
|
param.setBizpartnerPhone(customer.getContactPhone());
|
||||||
|
param.setFcusbeizhu(customer.getRemark());
|
||||||
|
param.setBizfunction(CustomerBfiConstants.BIZ_FUNCTION_ALL);
|
||||||
|
param.setAwrpDatasourcesys(srcSys);
|
||||||
|
param.setAwrpCusfortrade(BfiConstant.CUS_FOR_TRADE_YES);
|
||||||
|
param.setCreateorgNumber(createOrg);
|
||||||
|
BfiCustomerGroupEntryDTO groupEntry = new BfiCustomerGroupEntryDTO();
|
||||||
|
groupEntry.setStandardidNumber(BfiConstant.GROUP_STANDARD_BASIC);
|
||||||
|
groupEntry.setGroupidNumber(BfiConstant.CUSTOMER_GROUP_EXTERNAL);
|
||||||
|
param.setEntryGroupstandard(List.of(groupEntry));
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客商性质(字典 dictKey)→ 金蝶伙伴类型编码,未命中走兜底值
|
||||||
|
*
|
||||||
|
* @param customerNature 客商性质字典值
|
||||||
|
* @return 金蝶伙伴类型编码
|
||||||
|
*/
|
||||||
|
public String resolvePartnerType(String customerNature) {
|
||||||
|
if (Func.isNotEmpty(customerNature)) {
|
||||||
|
String partnerType = CustomerBfiConstants.NATURE_TYPE_MAPPING.get(customerNature.trim());
|
||||||
|
if (StringUtil.isNotBlank(partnerType)) {
|
||||||
|
return partnerType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CustomerBfiConstants.DEFAULT_PARTNER_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 经营范围多选编码 → 字典文本「、」拼接,字典缺失时回退原编码
|
||||||
|
*
|
||||||
|
* @param businessScope 逗号拼接的经营范围编码
|
||||||
|
* @return 金蝶经营范围文本
|
||||||
|
*/
|
||||||
|
public String buildBusinessScopeText(String businessScope) {
|
||||||
|
if (Func.isEmpty(businessScope)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
List<DictBiz> dictList = DictBizCache.getList("scope_of_business");
|
||||||
|
Map<String, String> scopeNames = dictList == null ? Map.of() : dictList.stream()
|
||||||
|
.collect(Collectors.toMap(DictBiz::getDictKey, DictBiz::getDictValue, (first, second) -> first));
|
||||||
|
return Func.toStrList(businessScope.replace(",", ",")).stream()
|
||||||
|
.map(code -> scopeNames.getOrDefault(code, code))
|
||||||
|
.collect(Collectors.joining("、"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 营业期限:长期传「长期」;固定期限传「至 yyyy-MM-dd」(TMS 未采集起始日)
|
||||||
|
*
|
||||||
|
* @param businessTermType 营业期限类型
|
||||||
|
* @param businessEndDate 营业期限截止日
|
||||||
|
* @return 金蝶营业期限文本
|
||||||
|
*/
|
||||||
|
public String buildBusinessTerm(String businessTermType, LocalDate businessEndDate) {
|
||||||
|
if (CustomerBfiConstants.BUSINESS_TERM_LONG.equals(businessTermType)) {
|
||||||
|
return CustomerBfiConstants.BUSINESS_TERM_LONG;
|
||||||
|
}
|
||||||
|
return businessEndDate == null ? "" : "至 " + businessEndDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
-- 客商档案:金蝶(星瀚)协同字段
|
||||||
|
-- kingdee_status:none未推送 / exist金蝶已有档案(A902-1查到已审核,跳过推送) / synced已推送成功 / failed推送失败或金蝶审核中
|
||||||
|
-- 金蝶客户编码与 TMS 客商编号(customer_code)一致(A902-2 number 传入客商编号),无需单独存储
|
||||||
|
|
||||||
|
ALTER TABLE `blade_customer_archive`
|
||||||
|
ADD COLUMN `kingdee_status` varchar(20) NOT NULL DEFAULT 'none' COMMENT '金蝶协同状态:none未推送/exist金蝶已有/synced已推送/failed推送失败' AFTER `approved_time`,
|
||||||
|
ADD COLUMN `kingdee_fail_reason` varchar(500) NULL DEFAULT NULL COMMENT '金蝶推送失败原因' AFTER `kingdee_status`,
|
||||||
|
ADD COLUMN `kingdee_sync_time` datetime NULL DEFAULT NULL COMMENT '金蝶推送时间' AFTER `kingdee_fail_reason`;
|
||||||
|
|
||||||
|
CREATE INDEX idx_kingdee_status ON blade_customer_archive (kingdee_status);
|
||||||
|
|
||||||
|
-- 列表「同步金蝶」按钮权限(审批通过后推送失败的重推入口;parent_id 通过菜单 code 关联,避免写死 id)
|
||||||
|
INSERT INTO blade_menu (id, parent_id, code, name, alias, path, source, sort, category, action, is_open, component, remark, is_deleted)
|
||||||
|
SELECT 2075431800000000001, m.id, 'customer_archive_sync', '同步金蝶', 'customer_archive_sync', '', '', 1, 2, 0, 1, '', '', 0
|
||||||
|
FROM blade_menu m
|
||||||
|
WHERE m.code = 'customer_archive' AND m.is_deleted = 0;
|
||||||
Reference in New Issue
Block a user