客商委托方数据同步

This commit is contained in:
秦鸿展
2026-06-08 15:05:34 +08:00
parent 734be2b825
commit c59fa363ee
23 changed files with 959 additions and 17 deletions
@@ -106,5 +106,8 @@ public class OrganizationPo extends BaseVOEntity implements Serializable {
private Integer isBand;
@ApiModelProperty("是否排队:0-否,1-是")
private Integer isQueue;
@ApiModelProperty("nc编码")
private String ncCode;
}
@@ -0,0 +1,30 @@
package com.mhd.common.core.domain.thirdparty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* MDM 客商主数据查询请求
*/
@Data
public class MdmMerchantQueryDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("对应组织编码(DESC24),即组织NC编码")
private String orgNcCode;
@ApiModelProperty("增量查询:上次最大LASTMODIFYRECORDTIME,格式如 2024-01-01 00:00:00~")
private String lastModifyRecordTime;
@ApiModelProperty("当前页码,从1开始")
private Integer currentPage = 1;
@ApiModelProperty("每页条数")
private Integer countPerPage = 500;
@ApiModelProperty("一级组织ID,用于读取三方接口配置")
private Long topOrganizationId;
}
@@ -0,0 +1,35 @@
package com.mhd.common.core.domain.thirdparty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* MDM 客商主数据查询结果
*/
@Data
public class MdmMerchantQueryResultDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("S-成功 E-失败")
private String result;
@ApiModelProperty("处理结果描述")
private String desc;
@ApiModelProperty("数据列表")
private List<MdmMerchantRecordDTO> dataInfo = new ArrayList<>();
@ApiModelProperty("当前页")
private Integer currentPage;
@ApiModelProperty("总页数")
private Integer totalPages;
@ApiModelProperty("总条数")
private Integer totalNumber;
}
@@ -0,0 +1,69 @@
package com.mhd.common.core.domain.thirdparty;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
/**
* MDM 客商主数据单条记录
*/
@Data
public class MdmMerchantRecordDTO implements Serializable {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主数据编码")
private String code;
@ApiModelProperty("商户全称")
private String desc1;
@ApiModelProperty("商户简称")
private String desc2;
@ApiModelProperty("是否客户")
private String desc3;
@ApiModelProperty("是否供应商")
private String desc4;
@ApiModelProperty("统一社会信用代码")
private String desc7;
@ApiModelProperty("发票抬头名称")
private String desc8;
@ApiModelProperty("单位地址")
private String desc13;
@ApiModelProperty("联系人")
private String desc14;
@ApiModelProperty("联系人电话")
private String desc15;
@ApiModelProperty("电子邮件")
private String desc16;
@ApiModelProperty("身份证号")
private String desc23;
@ApiModelProperty("对应组织编码")
private String desc24;
@ApiModelProperty("对应组织名称")
private String desc25;
@ApiModelProperty("国别地区")
private String desc29;
@ApiModelProperty("启用状态")
private String desc30;
@ApiModelProperty("上次变更时间")
private String lastModifyRecordTime;
@ApiModelProperty("UUID")
private String uuid;
}
@@ -327,4 +327,11 @@ public interface ThirdPartyServiceFeign {
//查询用户详情
@GetMapping("/dingtalkApi/v2/user/get")
AjaxResult userGet(@RequestParam(value = "userId") String userId);
/**
* 查询MDM客商主数据
*/
@PostMapping("/mdmMasterData/queryMerchant")
R<com.mhd.common.core.domain.thirdparty.MdmMerchantQueryResultDTO> queryMdmMerchant(
@RequestBody com.mhd.common.core.domain.thirdparty.MdmMerchantQueryDTO queryDTO);
}
@@ -39,6 +39,7 @@ import com.mhd.user.domain.userAggregate.repository.todo.*;
import com.mhd.user.domain.userAggregate.service.*;
import com.mhd.user.infrastructure.feign.ProductServiceFeign;
import com.mhd.user.infrastructure.feign.SystemServiceFeign;
import com.mhd.common.core.domain.thirdparty.MdmMerchantRecordDTO;
import com.mhd.common.core.domain.dto.SysProvinceCityCountyDTO;
import com.mhd.common.core.domain.po.SysAreaPO;
import com.mhd.common.core.domain.po.UserConfigSwitchPo;
@@ -198,6 +199,11 @@ public class UserShipperApplicationService {
*/
private List<UserShipperPo> processShipperList(List<UserShipperPo> userShipperList) {
for(UserShipperPo userShipperPo : userShipperList){
if (userShipperPo.getDataSource() != null) {
userShipperPo.setDataSourceName(userShipperPo.getDataSource() == 1 ? "接口同步" : "人工新增");
} else {
userShipperPo.setDataSourceName("人工新增");
}
R<TmsShipper> tmsShipper = wlhyServiceFeign.queryBySzwlUserId(userShipperPo.getUserId());
if (tmsShipper.getCode() == R.SUCCESS) {
if(ObjectUtil.isNotNull(tmsShipper.getData())){
@@ -241,6 +247,9 @@ public class UserShipperApplicationService {
if (userShipperDO.getShipperType() == null) {
throw new ServiceException("角色类型不能为空");
}
if (userShipperDO.getDataSource() == null) {
userShipperDO.setDataSource(2);
}
// if (userShipperDO.getStepFlag() == null) {
// throw new ServiceException("认证步骤不能为空");
// }
@@ -1232,4 +1241,109 @@ public class UserShipperApplicationService {
userShipper.setPushByName(pushByName);
return userShipperMapper.updatePushStatus(userShipper);
}
public UserShipperPo findByCustomerNcCodeAndOrganizationId(String customerNcCode, Long organizationId) {
if (StringUtils.isEmpty(customerNcCode) || organizationId == null) {
return null;
}
return userShipperMapper.findByCustomerNcCodeAndOrganizationId(customerNcCode, organizationId);
}
@Transactional(rollbackFor = Exception.class)
public void addEnterpriseShipperFromMasterData(MdmMerchantRecordDTO record, LoginUser loginUser) {
validateMasterDataRecord(record);
UserConfigSwitchPo configSwitch = commonApplicationService.getUserConfigSwitch();
if (ObjectUtil.isNull(configSwitch)) {
throw new ServiceException("获取组织配置失败");
}
UserShipperDO userShipperDO = buildShipperDOFromMasterData(record, loginUser);
userShipperDO.setDataSource(1);
userShipperDO.setShipperType(2);
userShipperDO.setBusinessType(1);
userShipperDO.setDelFlag(1);
userShipperDO.setShipperEnterpriseFill(3);
userShipperDO.setAutomaticCheckUserInfo(1);
userShipperDO.setAuthType(2);
userShipperDO.setShipperAuthStatus(3);
String userAccount = buildMasterDataUserAccount(record.getCode());
checkUserAccountUnique(userAccount, null, configSwitch.getTopOrganizationId());
userShipperDO.setUserAccount(userAccount);
UserDO userDO = new UserDO();
BeanUtils.copyProperties(userShipperDO, userDO);
userDO.setUserRemark(userShipperDO.getUserRemark());
userDO.setRoleCode(RoleEnum.COMPANY.getCode());
userDO.setRoleName(RoleEnum.COMPANY.getName());
UserPo user = userApplicationService.addUser(userDO);
userShipperDO.setUserId(user.getUserId());
userShipperDO.setUpdateBy(loginUser.getUserPo().getUserId());
userShipperDO.setUpdateByName(loginUser.getUserPo().getUserName());
userShipperDO.setUpdateTime(DateUtil.date());
UserShipperEntity userShipperEntity = userShipperDomainService.saveUserShipperByUserId(userShipperDO);
userShipperDO.setShipperId(userShipperEntity.getShipperId());
authShipper(userShipperDO);
UserShipperEntity shipperEntity = userShipperDomainService.findShipperByUserId(user.getUserId());
wlhyDomainService.addShipperUserInfo(shipperEntity);
}
@Transactional(rollbackFor = Exception.class)
public void updateEnterpriseShipperFromMasterData(MdmMerchantRecordDTO record, UserShipperPo existing, LoginUser loginUser) {
validateMasterDataRecord(record);
UserShipperDO userShipperDO = buildShipperDOFromMasterData(record, loginUser);
userShipperDO.setDataSource(1);
userShipperDO.setShipperId(existing.getShipperId());
userShipperDO.setUserId(existing.getUserId());
updateShipper(userShipperDO);
}
private UserShipperDO buildShipperDOFromMasterData(MdmMerchantRecordDTO record, LoginUser loginUser) {
UserShipperDO userShipperDO = new UserShipperDO();
userShipperDO.setCustomerNcCode(record.getCode());
userShipperDO.setUserMemberCode(record.getCode());
userShipperDO.setShipperEnterpriseName(record.getDesc1());
userShipperDO.setUserNameShipper(record.getDesc1());
userShipperDO.setEmergencyContactName(record.getDesc14());
userShipperDO.setEmergencyContactPhone(record.getDesc15());
userShipperDO.setUserEmail(record.getDesc16());
userShipperDO.setUserAreaName(record.getDesc29());
userShipperDO.setUserAreaAddress(record.getDesc13());
userShipperDO.setUnifiedCode(record.getDesc7());
userShipperDO.setShipperEnterpriseSocialCreditCode(record.getDesc7());
userShipperDO.setInvoiceTitle(record.getDesc8());
userShipperDO.setLegalRepresentative(record.getDesc23());
userShipperDO.setShipperCorpIdcardNumber(record.getDesc23());
userShipperDO.setRegisteredAddress(record.getDesc13());
userShipperDO.setRegisteredPhone(record.getDesc15());
userShipperDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
userShipperDO.setOrganizationName(loginUser.getUserPo().getOrganizationName());
userShipperDO.setTopOrganizationId(loginUser.getUserPo().getTopOrganizationId());
return userShipperDO;
}
private void validateMasterDataRecord(MdmMerchantRecordDTO record) {
if (record == null || StringUtils.isEmpty(record.getCode())) {
throw new ServiceException("NC客户编码不能为空");
}
if (StringUtils.isEmpty(record.getDesc1())) {
throw new ServiceException("公司名称不能为空,CODE=" + record.getCode());
}
if (StringUtils.isEmpty(record.getDesc14())) {
throw new ServiceException("联系人不能为空,CODE=" + record.getCode());
}
if (StringUtils.isEmpty(record.getDesc15())) {
throw new ServiceException("联系电话不能为空,CODE=" + record.getCode());
}
if (StringUtils.isEmpty(record.getDesc29())) {
throw new ServiceException("省市区县不能为空,CODE=" + record.getCode());
}
}
private String buildMasterDataUserAccount(String code) {
String account = "MDM_" + code;
return account.length() > 20 ? account.substring(0, 20) : account;
}
}
@@ -0,0 +1,232 @@
package com.mhd.user.application.service;
import cn.hutool.core.util.ObjectUtil;
import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.constant.Constants;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryDTO;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryResultDTO;
import com.mhd.common.core.domain.thirdparty.MdmMerchantRecordDTO;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.feign.ThirdPartyServiceFeign;
import com.mhd.common.core.utils.StringUtils;
import com.mhd.common.redis.service.RedisLock;
import com.mhd.common.security.utils.SecurityUtils;
import com.mhd.system.api.model.LoginUser;
import com.mhd.user.domain.userAggregate.repository.po.UserShipperPo;
import com.mhd.user.domain.userShipperMdmSync.entity.UserShipperMdmSyncEntity;
import com.mhd.user.domain.userShipperMdmSync.mapper.UserShipperMdmSyncMapper;
import com.mhd.user.infrastructure.feign.ProductServiceFeign;
import com.mhd.user.interfaces.vo.ShipperMasterDataSyncResultVo;
import com.mhd.user.interfaces.vo.ShipperMasterDataSyncTimeVo;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class UserShipperMasterDataSyncService {
private static final String LOCK_PREFIX = "shipper:mdm:sync:";
private static final int PAGE_SIZE = 500;
@Resource
private ThirdPartyServiceFeign thirdPartyServiceFeign;
@Resource
private ProductServiceFeign productServiceFeign;
@Resource
private UserShipperApplicationService userShipperApplicationService;
@Resource
private UserShipperMdmSyncMapper userShipperMdmSyncMapper;
@Resource
private RedisLock redisLock;
public ShipperMasterDataSyncTimeVo getLastSyncTime() {
LoginUser loginUser = requireLoginUser();
Long organizationId = loginUser.getUserPo().getOrganizationId();
UserShipperMdmSyncEntity syncEntity = userShipperMdmSyncMapper.selectByOrganizationId(organizationId);
ShipperMasterDataSyncTimeVo vo = new ShipperMasterDataSyncTimeVo();
if (syncEntity != null) {
vo.setLastSyncTime(syncEntity.getLastSyncTime());
vo.setLastModifyRecordTime(syncEntity.getLastModifyRecordTime());
}
return vo;
}
public ShipperMasterDataSyncResultVo syncMasterData() {
LoginUser loginUser = requireLoginUser();
Long organizationId = loginUser.getUserPo().getOrganizationId();
String lockKey = LOCK_PREFIX + organizationId;
boolean locked = redisLock.tryLock(lockKey, 30, TimeUnit.SECONDS);
if (!locked) {
throw new ServiceException("当前组织正在同步主数据,请稍后再试");
}
try {
return doSync(loginUser);
} finally {
redisLock.unlock(lockKey);
}
}
@Transactional(rollbackFor = Exception.class)
protected ShipperMasterDataSyncResultVo doSync(LoginUser loginUser) {
Long organizationId = loginUser.getUserPo().getOrganizationId();
String orgNcCode = resolveOrgNcCode(organizationId);
if (StringUtils.isEmpty(orgNcCode)) {
throw new ServiceException("当前组织未配置NC编码,请先在组织管理中维护");
}
UserShipperMdmSyncEntity syncEntity = userShipperMdmSyncMapper.selectByOrganizationId(organizationId);
String lastModifyRecordTime = syncEntity == null ? null : syncEntity.getLastModifyRecordTime();
ShipperMasterDataSyncResultVo resultVo = new ShipperMasterDataSyncResultVo();
List<MdmMerchantRecordDTO> allRecords = fetchAllRecords(orgNcCode, lastModifyRecordTime,
loginUser.getUserPo().getTopOrganizationId());
String maxModifyTime = lastModifyRecordTime;
for (MdmMerchantRecordDTO record : allRecords) {
if (!belongsToOrg(record, orgNcCode)) {
resultVo.setSkipCount(resultVo.getSkipCount() + 1);
continue;
}
if (isDisabled(record)) {
resultVo.setSkipCount(resultVo.getSkipCount() + 1);
continue;
}
if (StringUtils.isEmpty(record.getCode())) {
resultVo.setSkipCount(resultVo.getSkipCount() + 1);
continue;
}
try {
UserShipperPo existing = userShipperApplicationService.findByCustomerNcCodeAndOrganizationId(
record.getCode(), organizationId);
if (existing == null) {
userShipperApplicationService.addEnterpriseShipperFromMasterData(record, loginUser);
resultVo.setAddCount(resultVo.getAddCount() + 1);
} else {
userShipperApplicationService.updateEnterpriseShipperFromMasterData(record, existing, loginUser);
resultVo.setUpdateCount(resultVo.getUpdateCount() + 1);
}
maxModifyTime = maxTime(maxModifyTime, record.getLastModifyRecordTime());
} catch (Exception e) {
resultVo.setFailCount(resultVo.getFailCount() + 1);
String msg = "CODE=" + record.getCode() + " 同步失败:" + e.getMessage();
resultVo.getErrorMessages().add(msg);
log.error(msg, e);
}
}
Date now = new Date();
resultVo.setLastSyncTime(now);
saveSyncWatermark(organizationId, maxModifyTime, now, resultVo);
return resultVo;
}
private List<MdmMerchantRecordDTO> fetchAllRecords(String orgNcCode, String lastModifyRecordTime, Long topOrganizationId) {
List<MdmMerchantRecordDTO> allRecords = new ArrayList<>();
int currentPage = 1;
Integer totalPages = null;
do {
MdmMerchantQueryDTO queryDTO = new MdmMerchantQueryDTO();
queryDTO.setOrgNcCode(orgNcCode);
if (StringUtils.isNotEmpty(lastModifyRecordTime)) {
queryDTO.setLastModifyRecordTime(lastModifyRecordTime + "~");
}
queryDTO.setCurrentPage(currentPage);
queryDTO.setCountPerPage(PAGE_SIZE);
queryDTO.setTopOrganizationId(topOrganizationId);
R<MdmMerchantQueryResultDTO> response = thirdPartyServiceFeign.queryMdmMerchant(queryDTO);
if (response == null || response.getCode() != Constants.SUCCESS) {
throw new ServiceException("调用MDM客商接口失败:" + (response == null ? "无响应" : response.getMsg()));
}
MdmMerchantQueryResultDTO data = response.getData();
if (data == null) {
break;
}
if (data.getDataInfo() != null) {
allRecords.addAll(data.getDataInfo());
}
totalPages = data.getTotalPages();
if (totalPages == null || totalPages <= 0) {
break;
}
currentPage++;
} while (currentPage <= totalPages);
return allRecords;
}
private void saveSyncWatermark(Long organizationId, String maxModifyTime, Date syncTime,
ShipperMasterDataSyncResultVo resultVo) {
UserShipperMdmSyncEntity entity = userShipperMdmSyncMapper.selectByOrganizationId(organizationId);
if (entity == null) {
entity = new UserShipperMdmSyncEntity();
entity.setOrganizationId(organizationId);
entity.setCreateTime(syncTime);
}
if (StringUtils.isNotEmpty(maxModifyTime)) {
entity.setLastModifyRecordTime(maxModifyTime);
}
entity.setLastSyncTime(syncTime);
entity.setSyncStatus(resultVo.getFailCount() > 0 ? 2 : 1);
entity.setSyncMessage(String.format("新增%d,更新%d,跳过%d,失败%d",
resultVo.getAddCount(), resultVo.getUpdateCount(), resultVo.getSkipCount(), resultVo.getFailCount()));
entity.setUpdateTime(syncTime);
if (entity.getId() == null) {
userShipperMdmSyncMapper.insert(entity);
} else {
userShipperMdmSyncMapper.updateById(entity);
}
}
private String resolveOrgNcCode(Long organizationId) {
com.mhd.common.core.web.domain.AjaxResult organizationInfo = productServiceFeign.getOrganizationInfo(organizationId);
if (organizationInfo == null || organizationInfo.get("code") == null
|| !"200".equals(String.valueOf(organizationInfo.get("code")))) {
throw new ServiceException("获取组织信息失败");
}
Object data = organizationInfo.get("data");
if (data == null) {
throw new ServiceException("组织信息不存在");
}
// 仅读取 ncCode,避免 product 组织对象字段多于 common OrganizationPo 导致 toBean 失败
JSONObject orgJson = JSONObject.fromObject(data);
return orgJson.has("ncCode") ? orgJson.getString("ncCode") : null;
}
private boolean belongsToOrg(MdmMerchantRecordDTO record, String orgNcCode) {
return StringUtils.isEmpty(record.getDesc24()) || orgNcCode.equals(record.getDesc24());
}
private boolean isDisabled(MdmMerchantRecordDTO record) {
if (StringUtils.isEmpty(record.getDesc30())) {
return false;
}
String status = record.getDesc30().trim();
return "".equals(status) || "停用".equals(status) || "0".equals(status);
}
private String maxTime(String currentMax, String candidate) {
if (StringUtils.isEmpty(candidate)) {
return currentMax;
}
if (StringUtils.isEmpty(currentMax)) {
return candidate;
}
return candidate.compareTo(currentMax) > 0 ? candidate : currentMax;
}
private LoginUser requireLoginUser() {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (ObjectUtil.isNull(loginUser) || loginUser.getUserPo() == null) {
throw new ServiceException("未获取到登录用户信息");
}
return loginUser;
}
}
@@ -192,6 +192,9 @@ public class UserShipperEntity extends BaseVOEntity {
private String remark;
@ApiModelProperty(name = "数据来源:1-接口同步 2-人工新增")
private Integer dataSource;
@ApiModelProperty(name = "客户nc编码")
private String customerNcCode;
@@ -40,4 +40,7 @@ public interface UserShipperMapper extends BaseMapper<UserShipperEntity> {
@Select("select customer_nc_code from user_shipper where user_id = #{shipperId}")
String getCustomerNcCode(@Param("shipperId") Long shipperId);
UserShipperPo findByCustomerNcCodeAndOrganizationId(@Param("customerNcCode") String customerNcCode,
@Param("organizationId") Long organizationId);
}
@@ -259,6 +259,12 @@ public class UserShipperPo implements Serializable {
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(name = "数据来源:1-接口同步 2-人工新增")
private Integer dataSource;
@ApiModelProperty(name = "数据来源名称")
private String dataSourceName;
@ApiModelProperty(name = "客户nc编码")
private String customerNcCode;
@@ -226,6 +226,9 @@ public class UserShipperDO extends UserDO {
private String remark;
@ApiModelProperty(name = "数据来源:1-接口同步 2-人工新增")
private Integer dataSource;
@ApiModelProperty(name = "客户nc编码")
private String customerNcCode;
@@ -0,0 +1,39 @@
package com.mhd.user.domain.userShipperMdmSync.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@TableName("user_shipper_mdm_sync")
public class UserShipperMdmSyncEntity {
@TableId(type = IdType.AUTO)
private Long id;
@ApiModelProperty("组织ID")
private Long organizationId;
@ApiModelProperty("MDM最大LASTMODIFYRECORDTIME")
private String lastModifyRecordTime;
@ApiModelProperty("上次同步时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastSyncTime;
@ApiModelProperty("同步状态:1-成功 2-失败")
private Integer syncStatus;
@ApiModelProperty("同步结果摘要")
private String syncMessage;
private Date createTime;
private Date updateTime;
}
@@ -0,0 +1,10 @@
package com.mhd.user.domain.userShipperMdmSync.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mhd.user.domain.userShipperMdmSync.entity.UserShipperMdmSyncEntity;
import org.apache.ibatis.annotations.Param;
public interface UserShipperMdmSyncMapper extends BaseMapper<UserShipperMdmSyncEntity> {
UserShipperMdmSyncEntity selectByOrganizationId(@Param("organizationId") Long organizationId);
}
@@ -191,6 +191,9 @@ public class UserShipperDTO extends UserDTO {
@ApiModelProperty(name = "备注信息")
private String remark;
@ApiModelProperty(name = "数据来源:1-接口同步 2-人工新增")
private Integer dataSource;
@ApiModelProperty("客户nc编码")
private String customerNcCode;
@@ -16,8 +16,11 @@ import com.mhd.common.core.web.domain.AjaxResult;
import com.mhd.common.core.web.page.TableDataInfo;
import com.mhd.common.security.annotation.RepeatSubmit;
import com.mhd.common.security.service.TokenService;
import com.mhd.user.application.service.UserShipperMasterDataSyncService;
import com.mhd.user.interfaces.dto.updateDTO.SettlementInfoUpdateDTO;
import com.mhd.user.interfaces.vo.SettlementInfoQueryVo;
import com.mhd.user.interfaces.vo.ShipperMasterDataSyncResultVo;
import com.mhd.user.interfaces.vo.ShipperMasterDataSyncTimeVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -37,6 +40,8 @@ public class UserShipperAPI extends BaseController {
@Resource
private UserShipperApplicationService userShipperApplicationService;
@Resource
private UserShipperMasterDataSyncService userShipperMasterDataSyncService;
@Resource
private UserApplicationService userApplicationService;
@Resource
private UserShipperAssember userShipperAssember;
@@ -277,6 +282,31 @@ public class UserShipperAPI extends BaseController {
return getDataTable(list);
}
@Log(title = "托运人管理-同步主数据", description = "从MDM同步客商主数据到委托方列表", businessType = BusinessType.UPDATE)
@ApiOperation("同步主数据-客商(权限码:user:shipper:syncMasterData")
@PostMapping("/syncMasterData")
public AjaxResult syncMasterData() {
try {
ShipperMasterDataSyncResultVo result = userShipperMasterDataSyncService.syncMasterData();
return AjaxResult.success("同步完成", result);
} catch (Exception e) {
log.error("同步主数据失败", e);
return AjaxResult.error("同步主数据失败:" + e.getMessage());
}
}
@ApiOperation("查询主数据上次同步时间")
@GetMapping("/getMasterDataLastSyncTime")
public AjaxResult getMasterDataLastSyncTime() {
try {
ShipperMasterDataSyncTimeVo result = userShipperMasterDataSyncService.getLastSyncTime();
return AjaxResult.success(result);
} catch (Exception e) {
log.error("查询主数据同步时间失败", e);
return AjaxResult.error("查询失败:" + e.getMessage());
}
}
@PostMapping("/getCustomerNcCodeBatch")
public AjaxResult getCustomerNcCodeBatch(@RequestBody List<Long> shipperIdList){
Map<Long, String> resultMap = new HashMap<>();
@@ -0,0 +1,34 @@
package com.mhd.user.interfaces.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Data
public class ShipperMasterDataSyncResultVo {
@ApiModelProperty("新增条数")
private int addCount;
@ApiModelProperty("更新条数")
private int updateCount;
@ApiModelProperty("跳过条数")
private int skipCount;
@ApiModelProperty("失败条数")
private int failCount;
@ApiModelProperty("本次同步时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastSyncTime;
@ApiModelProperty("失败明细")
private List<String> errorMessages = new ArrayList<>();
}
@@ -0,0 +1,20 @@
package com.mhd.user.interfaces.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
public class ShipperMasterDataSyncTimeVo {
@ApiModelProperty("上次同步时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date lastSyncTime;
@ApiModelProperty("MDM增量水位时间")
private String lastModifyRecordTime;
}
@@ -20,17 +20,17 @@ spring:
discovery:
# server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
# server-addr: 10.33.0.129:6010
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
server-addr: 10.33.0.129:6010
# server-addr: 10.102.192.31:6848
# username: nacos
# password: manhuoda@2023
config:
# server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
# server-addr: 10.33.0.129:6010
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
server-addr: 10.33.0.129:6010
# server-addr: 10.102.192.31:6848
# username: nacos
# password: manhuoda@2023
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置
file-extension: yml #默认properties
# 共享配置
@@ -165,7 +165,8 @@
b.push_status,
b.push_time,
b.push_by,
b.push_by_name
b.push_by_name,
b.data_source
FROM "USER" a
LEFT JOIN user_shipper b ON a.user_id = b.user_id
left join (select user_id, LISTAGG(DISTINCT r.role_name, '&amp;') WITHIN GROUP(ORDER BY r.role_name) AS roleName
@@ -435,5 +436,25 @@
WHERE shipper_id = #{shipperId}
</update>
<select id="findByCustomerNcCodeAndOrganizationId" resultType="com.mhd.user.domain.userAggregate.repository.po.UserShipperPo">
SELECT b.shipper_id,
b.user_id,
b.customer_nc_code,
b.data_source,
a.organization_id AS organizationId,
a.organization_name AS organizationName,
b.shipper_enterprise_name,
b.user_name_shipper,
b.emergency_contact_name,
b.emergency_contact_phone
FROM "USER" a
INNER JOIN user_shipper b ON a.user_id = b.user_id
WHERE a.del_flag = 1
AND b.del_flag = 1
AND b.customer_nc_code = #{customerNcCode}
AND a.organization_id = #{organizationId}
LIMIT 1
</select>
</mapper>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mhd.user.domain.userShipperMdmSync.mapper.UserShipperMdmSyncMapper">
<select id="selectByOrganizationId" resultType="com.mhd.user.domain.userShipperMdmSync.entity.UserShipperMdmSyncEntity">
SELECT id, organization_id, last_modify_record_time, last_sync_time, sync_status, sync_message, create_time, update_time
FROM user_shipper_mdm_sync
WHERE organization_id = #{organizationId}
LIMIT 1
</select>
</mapper>
@@ -0,0 +1,232 @@
package com.linke.thirdParty.application.server;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.linke.thirdParty.domain.departInterfaceInfo.entity.SysDepartInterfaceInfo;
import com.linke.thirdParty.domain.departInterfaceInfo.service.SysDepartInterfaceInfoDomainService;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryDTO;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryResultDTO;
import com.mhd.common.core.domain.thirdparty.MdmMerchantRecordDTO;
import com.mhd.common.core.exception.ServiceException;
import com.mhd.common.core.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 北京三维天地 MDM 客商主数据查询
*/
@Service
@Slf4j
public class MdmMasterDataApplicationService {
private static final String INTERFACE_TYPE = "mdm_merchant";
private static final String DEFAULT_URL = "http://10.30.70.2/esbmule/services/query/mdm_nkks_query";
@Autowired
private SysDepartInterfaceInfoDomainService sysDepartInterfaceInfoDomainService;
public MdmMerchantQueryResultDTO queryMerchant(MdmMerchantQueryDTO queryDTO) {
MdmConfig config = loadConfig();
String requestBody = buildRequestBody(queryDTO);
log.info("MDM客商查询请求 orgNcCode={}, page={}, lastTime={}",
queryDTO.getOrgNcCode(), queryDTO.getCurrentPage(), queryDTO.getLastModifyRecordTime());
String responseBody = doPost(config.url, config.usercode, config.password, requestBody);
return parseResponse(responseBody);
}
private MdmConfig loadConfig() {
MdmConfig config = new MdmConfig();
config.url = DEFAULT_URL;
List<SysDepartInterfaceInfo> list = sysDepartInterfaceInfoDomainService.queryListByInterfaceType(INTERFACE_TYPE);
for (SysDepartInterfaceInfo info : list) {
if (info == null || StringUtils.isEmpty(info.getInterfaceTypeKey())) {
continue;
}
switch (info.getInterfaceTypeKey()) {
case "url":
config.url = info.getInterfaceTypeValue();
break;
case "usercode":
config.usercode = info.getInterfaceTypeValue();
break;
case "password":
config.password = info.getInterfaceTypeValue();
break;
default:
break;
}
}
if (StringUtils.isEmpty(config.usercode) || StringUtils.isEmpty(config.password)) {
throw new ServiceException("MDM客商接口未配置 usercode/password,请在三方接口配置中维护 interfaceType=mdm_merchant");
}
return config;
}
private String buildRequestBody(MdmMerchantQueryDTO queryDTO) {
JSONObject dataInfoItem = new JSONObject();
if (StringUtils.isNotEmpty(queryDTO.getOrgNcCode())) {
dataInfoItem.put("DESC24", queryDTO.getOrgNcCode());
}
if (StringUtils.isNotEmpty(queryDTO.getLastModifyRecordTime())) {
dataInfoItem.put("LASTMODIFYRECORDTIME", queryDTO.getLastModifyRecordTime());
}
JSONArray dataInfoArray = new JSONArray();
dataInfoArray.add(dataInfoItem);
JSONObject dataInfos = new JSONObject();
dataInfos.put("PUUID", UUID.randomUUID().toString().replace("-", ""));
dataInfos.put("DATAINFO", dataInfoArray);
JSONObject splitPage = new JSONObject();
splitPage.put("COUNTPERPAGE", String.valueOf(queryDTO.getCountPerPage() == null ? 500 : queryDTO.getCountPerPage()));
splitPage.put("CURRENTPAGE", String.valueOf(queryDTO.getCurrentPage() == null ? 1 : queryDTO.getCurrentPage()));
JSONObject data = new JSONObject();
data.put("DATAINFOS", dataInfos);
data.put("SPLITPAGE", splitPage);
JSONObject esb = new JSONObject();
esb.put("DATA", data);
JSONObject root = new JSONObject();
root.put("ESB", esb);
return root.toJSONString();
}
private String doPost(String url, String usercode, String password, String body) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(30000);
conn.setReadTimeout(60000);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");
conn.setRequestProperty("usercode", usercode);
conn.setRequestProperty("password", password);
try (OutputStream os = conn.getOutputStream()) {
os.write(body.getBytes(StandardCharsets.UTF_8));
os.flush();
}
int code = conn.getResponseCode();
BufferedReader reader = new BufferedReader(new InputStreamReader(
code >= 400 ? conn.getErrorStream() : conn.getInputStream(), StandardCharsets.UTF_8));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
reader.close();
if (code >= 400) {
throw new ServiceException("MDM客商接口HTTP异常,status=" + code + ", body=" + sb);
}
return sb.toString();
} catch (ServiceException e) {
throw e;
} catch (Exception e) {
log.error("MDM客商接口调用失败", e);
throw new ServiceException("MDM客商接口调用失败:" + e.getMessage());
} finally {
if (conn != null) {
conn.disconnect();
}
}
}
private MdmMerchantQueryResultDTO parseResponse(String responseBody) {
if (StringUtils.isEmpty(responseBody)) {
throw new ServiceException("MDM客商接口返回为空");
}
JSONObject root = JSON.parseObject(responseBody);
JSONObject esb = root.getJSONObject("ESB");
if (esb == null) {
throw new ServiceException("MDM客商接口返回格式异常:缺少ESB节点");
}
MdmMerchantQueryResultDTO resultDTO = new MdmMerchantQueryResultDTO();
resultDTO.setResult(esb.getString("RESULT"));
resultDTO.setDesc(esb.getString("DESC"));
if (!"S".equalsIgnoreCase(resultDTO.getResult())) {
throw new ServiceException("MDM客商接口返回失败:" + resultDTO.getDesc());
}
JSONObject data = esb.getJSONObject("DATA");
if (data == null) {
return resultDTO;
}
JSONObject splitPage = data.getJSONObject("SPLITPAGE");
if (splitPage != null) {
resultDTO.setCurrentPage(parseInt(splitPage.getString("CURRENTPAGE")));
resultDTO.setTotalPages(parseInt(splitPage.getString("TOTALPAGES")));
resultDTO.setTotalNumber(parseInt(splitPage.getString("TOTALNUMBER")));
}
JSONObject dataInfos = data.getJSONObject("DATAINFOS");
if (dataInfos == null) {
return resultDTO;
}
Object dataInfoObj = dataInfos.get("DATAINFO");
List<MdmMerchantRecordDTO> records = new ArrayList<>();
if (dataInfoObj instanceof JSONArray) {
JSONArray array = (JSONArray) dataInfoObj;
for (int i = 0; i < array.size(); i++) {
records.add(mapRecord(array.getJSONObject(i)));
}
} else if (dataInfoObj instanceof JSONObject) {
records.add(mapRecord((JSONObject) dataInfoObj));
}
resultDTO.setDataInfo(records);
return resultDTO;
}
private MdmMerchantRecordDTO mapRecord(JSONObject item) {
MdmMerchantRecordDTO record = new MdmMerchantRecordDTO();
record.setCode(item.getString("CODE"));
record.setDesc1(item.getString("DESC1"));
record.setDesc2(item.getString("DESC2"));
record.setDesc3(item.getString("DESC3"));
record.setDesc4(item.getString("DESC4"));
record.setDesc7(item.getString("DESC7"));
record.setDesc8(item.getString("DESC8"));
record.setDesc13(item.getString("DESC13"));
record.setDesc14(item.getString("DESC14"));
record.setDesc15(item.getString("DESC15"));
record.setDesc16(item.getString("DESC16"));
record.setDesc23(item.getString("DESC23"));
record.setDesc24(item.getString("DESC24"));
record.setDesc25(item.getString("DESC25"));
record.setDesc29(item.getString("DESC29"));
record.setDesc30(item.getString("DESC30"));
record.setLastModifyRecordTime(item.getString("LASTMODIFYRECORDTIME"));
record.setUuid(item.getString("UUID"));
return record;
}
private Integer parseInt(String value) {
if (StringUtils.isEmpty(value)) {
return null;
}
try {
return Integer.parseInt(value.trim());
} catch (NumberFormatException e) {
return null;
}
}
private static class MdmConfig {
private String url;
private String usercode;
private String password;
}
}
@@ -0,0 +1,36 @@
package com.linke.thirdParty.interfaces.facade;
import com.linke.thirdParty.application.server.MdmMasterDataApplicationService;
import com.mhd.common.core.domain.entity.R;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryDTO;
import com.mhd.common.core.domain.thirdparty.MdmMerchantQueryResultDTO;
import com.mhd.common.core.web.controller.BaseController;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "MDM主数据")
@RestController
@RequestMapping("/mdmMasterData")
@Slf4j
public class MdmMasterDataApi extends BaseController {
@Autowired
private MdmMasterDataApplicationService mdmMasterDataApplicationService;
@ApiOperation("查询客商主数据")
@PostMapping("/queryMerchant")
public R<MdmMerchantQueryResultDTO> queryMerchant(@RequestBody MdmMerchantQueryDTO queryDTO) {
try {
return R.ok(mdmMasterDataApplicationService.queryMerchant(queryDTO));
} catch (Exception e) {
log.error("查询MDM客商主数据失败", e);
return R.fail(e.getMessage());
}
}
}
@@ -20,17 +20,17 @@ spring:
discovery:
# server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
#server-addr: 10.33.0.129:6010
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
server-addr: 10.33.0.129:6010
# server-addr: 10.102.192.5:6848
# username: nacos
# password: manhuoda@2023
config:
# server-addr: 127.0.0.1:8848
# 线上测试环境配置 容器名+端口号
#server-addr: 10.33.0.129:6010
server-addr: 10.102.192.31:6848
username: nacos
password: manhuoda@2023
server-addr: 10.33.0.129:6010
# server-addr: 10.102.192.5:6848
# username: nacos
# password: manhuoda@2023
#线上正式环境
# server-addr: 10.102.192.105:6848
group: DEFAULT_GROUP # 默认分组就是DEFAULT_GROUP,如果使用默认分组可以不配置