Browse Source

增加U9客户地址同步,和定时任务同步

bai 3 weeks ago
parent
commit
17fa416954

+ 46 - 0
blade-service-api/blade-u9cloud-api/src/main/java/org/springblade/u9cloud/entity/ZcrmViewCustomerAddressSel.java

@@ -0,0 +1,46 @@
+package org.springblade.u9cloud.entity;
+
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import org.springblade.core.mp.base.BaseEntity;
+
+/**
+ * <p>
+ * 客户地址档案表
+ * </p>
+ *
+ * @author your-name
+ * @since 2025-07-30
+ */
+@Data
+@TableName("zcrm_view_customeraddress_sel")
+public class ZcrmViewCustomerAddressSel extends BaseEntity {
+
+	private static final long serialVersionUID = 1L;
+
+	/* ========== 通用字段 ========== */
+
+	@JsonProperty("Customer_ID")
+	@TableField("Customer_ID")
+	private Long customerId;
+
+	@JsonProperty("Customer_CODE")
+	@TableField("Customer_CODE")
+	private String customerCode;
+
+	@JsonProperty("Contacts")
+	@TableField("Contacts")
+	private String contacts;
+
+	@JsonProperty("Phone")
+	@TableField("Phone")
+	private String phone;
+
+	@JsonProperty("CustAddress")
+	@TableField("CustAddress")
+	private String custAddress;
+
+}

+ 42 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/controller/ZcrmViewCustomerAddressSelController.java

@@ -0,0 +1,42 @@
+package org.springblade.u9cloud.controller;
+
+
+import lombok.AllArgsConstructor;
+import org.springblade.core.tool.api.R;
+import org.springblade.u9cloud.service.ZcrmViewCustomerAddressSelService;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ * 客户地址档案 前端控制器
+ * </p>
+ *
+ * @author your-name
+ * @since 2025-07-30
+ */
+@RestController
+@RequestMapping("/api/viewCustomerAddressSel")
+@AllArgsConstructor
+public class ZcrmViewCustomerAddressSelController {
+
+
+	public final ZcrmViewCustomerAddressSelService zcrmViewCustomerAddressSelService;
+
+	/**
+	 * 执行同步
+	 * @return
+	 */
+	@GetMapping("/executionSynchronization")
+	public R<String> executionSynchronization()
+	{
+		boolean res = zcrmViewCustomerAddressSelService.insertBatchCustomers();
+		if (res) {
+			return R.data("执行成功");
+		} else {
+			return R.data("执行失败");
+		}
+	}
+
+}

+ 29 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/job/U9CloudTaskJob.java

@@ -56,6 +56,9 @@ public class U9CloudTaskJob {
 	@Autowired
 	private final ZcrmViewARBillHeadSelService zcrmViewARBillHeadSelService;
 
+	@Autowired
+	private final ZcrmViewCustomerAddressSelService zcrmViewCustomerAddressSelService;
+
 
 
 	/**
@@ -262,6 +265,26 @@ public class U9CloudTaskJob {
 	}
 
 	/**
+	 * 同步客户地址档案数据(单独任务)
+	 */
+	@XxlJob("syncCustomerAddressDataHandler")
+	public ReturnT<String> syncCustomerAddressData(String param) throws Exception {
+		log.info("XXL-JOB: 开始执行客户地址档案数据同步任务,参数: {}", param);
+		try {
+			boolean res10 = zcrmViewCustomerAddressSelService.insertBatchCustomers();
+			if (!res10) {
+				return ReturnT.FAIL;
+			}
+			log.info("XXL-JOB: 客户地址档案数据同步任务执行完成");
+			return ReturnT.SUCCESS;
+		} catch (Exception e)
+		{
+			log.error("XXL-JOB: U9 客户地址档案数据完整同步失败", e);
+			return ReturnT.FAIL;
+		}
+	}
+
+	/**
 	 * 同步所有U9数据(完整同步)
 	 */
 	@XxlJob("syncAllDataHandler")
@@ -330,6 +353,12 @@ public class U9CloudTaskJob {
 				return ReturnT.FAIL;
 			}
 
+			boolean res11 = zcrmViewCustomerAddressSelService.insertBatchCustomers();
+			if (!res11) {
+				System.out.println("客户地址档案数据批量插入失败");
+				return ReturnT.FAIL;
+			}
+
 			return ReturnT.SUCCESS;
 		} catch (Exception e) {
 			String errorMsg = "XXL-JOB: U9数据完整同步失败:" + e.getMessage();

+ 16 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/mapper/ZcrmViewCustomerAddressSelMapper.java

@@ -0,0 +1,16 @@
+package org.springblade.u9cloud.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.springblade.u9cloud.entity.ZcrmViewCustomerAddressSel;
+import org.springblade.u9cloud.entity.ZcrmViewCustomerSel;
+
+/**
+ * <p>
+ * 客户地址档案 Mapper 接口
+ * </p>
+ *
+ * @author your-name
+ * @since 2025-07-30
+ */
+public interface ZcrmViewCustomerAddressSelMapper extends BaseMapper<ZcrmViewCustomerAddressSel> {
+}

+ 5 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/mapper/xml/ZcrmViewCustomerAddressSelMapper.xml

@@ -0,0 +1,5 @@
+<?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="org.springblade.u9cloud.mapper.ZcrmViewCustomerAddressSelMapper">
+
+</mapper>

+ 16 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/service/ZcrmViewCustomerAddressSelService.java

@@ -0,0 +1,16 @@
+package org.springblade.u9cloud.service;
+
+import org.springblade.core.mp.base.BaseService;
+import org.springblade.u9cloud.entity.ZcrmViewCustomerAddressSel;
+import org.springblade.u9cloud.entity.ZcrmViewCustomerSel;
+
+public interface ZcrmViewCustomerAddressSelService  extends BaseService<ZcrmViewCustomerAddressSel> {
+
+	/**
+	 * 批量新增客户地址信息
+	 * @param
+	 * @return 是否成功
+	 */
+	boolean insertBatchCustomers();
+
+}

+ 294 - 0
blade-service/blade-u9cloud/src/main/java/org/springblade/u9cloud/service/impl/ZcrmViewCustomerAddressSelServiceImpl.java

@@ -0,0 +1,294 @@
+package org.springblade.u9cloud.service.impl;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.AllArgsConstructor;
+import org.springblade.core.mp.base.BaseServiceImpl;
+import org.springblade.u9cloud.common.U9cloudGetTokenUtil;
+import org.springblade.u9cloud.config.U9cloudConfig;
+import org.springblade.u9cloud.entity.ViewApiResponse;
+import org.springblade.u9cloud.entity.ZcrmViewCustomerAddressSel;
+import org.springblade.u9cloud.mapper.ZcrmViewCustomerAddressSelMapper;
+import org.springblade.u9cloud.service.ZcrmViewCustomerAddressSelService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.*;
+import org.springframework.jdbc.core.BeanPropertyRowMapper;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.util.UriComponentsBuilder;
+
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+/**
+ * <p>
+ * 客户地址档案 服务实现类
+ * </p>
+ *
+ * @author your-name
+ * @since 2025-07-30
+ */
+@Service
+@AllArgsConstructor
+public class ZcrmViewCustomerAddressSelServiceImpl extends BaseServiceImpl<ZcrmViewCustomerAddressSelMapper, ZcrmViewCustomerAddressSel>
+	implements ZcrmViewCustomerAddressSelService {
+
+
+	private final U9cloudGetTokenUtil u9cloudGetTokenUtil;
+
+	@Autowired
+	private JdbcTemplate jdbcTemplate;
+
+
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public boolean insertBatchCustomers() {
+		// 1. 构建URL和请求参数(不变)
+		String url = UriComponentsBuilder.fromHttpUrl(U9cloudConfig.BASE_URL + U9cloudConfig.PATH_API)
+			.encode(StandardCharsets.UTF_8)
+			.toUriString();
+
+		Map<String, String> requestBody = new HashMap<>();
+		requestBody.put("SqlString", "select * from ZCRM_View_CustomerAddress_Sel");
+
+		HttpHeaders headers = new HttpHeaders();
+		headers.setContentType(MediaType.APPLICATION_JSON);
+		String token = u9cloudGetTokenUtil.getToken();
+		headers.set("token", token);
+		HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
+
+		// 2. 发送请求:先接收为 String(关键修改)
+		RestTemplate restTemplate = new RestTemplate();
+		ResponseEntity<String> responseEntity = restTemplate.exchange(
+			url, HttpMethod.POST, requestEntity, String.class
+		);
+		String responseStr = responseEntity.getBody();
+
+		// ========== 打印原始完整响应 ==========
+		System.out.println("=====================================");
+		System.out.println("接口返回原始完整响应:");
+		System.out.println(responseStr);
+		System.out.println("=====================================");
+
+		if (responseStr == null || responseStr.trim().isEmpty()) {
+			System.err.println("接口返回空响应");
+			return false;
+		}
+
+		// 3. 手动解析 JSON(适配接口实际返回格式:ResCode/Success/ResMsg/Data 大写)
+		ObjectMapper objectMapper = new ObjectMapper();
+		JsonNode rootNode;
+		List<ZcrmViewCustomerAddressSel> newItems = new ArrayList<>();
+		try {
+			rootNode = objectMapper.readTree(responseStr);
+
+			// 打印接口实际返回的字段(大写)
+			System.out.println("接口响应 ResCode:" + (rootNode.has("ResCode") ? rootNode.get("ResCode").asInt() : "无"));
+			System.out.println("接口响应 Success:" + (rootNode.has("Success") ? rootNode.get("Success").asBoolean() : "无"));
+			System.out.println("接口响应 ResMsg:" + (rootNode.has("ResMsg") ? rootNode.get("ResMsg").asText() : "无"));
+
+			// 核心修正:接口返回的是大写 Data,不是小写 data!
+			JsonNode dataNode = rootNode.get("Data");
+			System.out.println("接口响应 Data 字段类型:" + (dataNode == null ? "null" : dataNode.getNodeType()));
+			System.out.println("接口响应 Data 字段长度:" + (dataNode != null && dataNode.isArray() ? dataNode.size() + " 条" : "无"));
+			System.out.println("=====================================");
+
+			// 校验接口是否成功(适配大写 Success 字段)
+			boolean success = rootNode.has("Success") ? rootNode.get("Success").asBoolean() : false;
+			if (!success) {
+				String errorMsg = rootNode.has("ResMsg") ? rootNode.get("ResMsg").asText() : "接口返回失败";
+				System.err.println("接口调用失败:" + errorMsg);
+				return false;
+			}
+
+			// 处理 Data 字段(接口返回的是数组)
+			if (dataNode == null || dataNode.isNull()) {
+				System.err.println("接口返回 Data 为空");
+				return false;
+			}
+
+			if (dataNode.isTextual()) {
+				String dataStr = dataNode.asText();
+				System.err.println("接口返回 Data 为字符串:" + dataStr);
+				return false;
+			} else if (dataNode.isArray()) {
+				// 解析数组数据(接口实际返回数组)
+				newItems = objectMapper.readValue(
+					dataNode.toString(),
+					objectMapper.getTypeFactory().constructCollectionType(List.class, ZcrmViewCustomerAddressSel.class)
+				);
+				System.out.println("成功解析数组数据,共 " + newItems.size() + " 条记录");
+			} else if (dataNode.isObject()) {
+				// 兼容单个对象场景
+				ZcrmViewCustomerAddressSel item = objectMapper.readValue(
+					dataNode.toString(),
+					ZcrmViewCustomerAddressSel.class
+				);
+				newItems.add(item);
+				System.out.println("成功解析单个对象数据,共 1 条记录");
+			} else {
+				System.err.println("接口返回 Data 格式不支持(非字符串/数组/对象)");
+				return false;
+			}
+		} catch (Exception e) {
+			System.err.println("解析接口响应失败,详细异常信息:");
+			e.printStackTrace();
+			throw new RuntimeException("解析接口响应失败", e);
+		}
+
+		// 4. 校验解析后的数据非空
+		if (newItems.isEmpty()) {
+			System.err.println("解析后的数据集合为空");
+			return false;
+		}
+
+		// 打印解析后的前3条数据(验证字段映射是否正确)
+		System.out.println("解析后的原始数据示例(前3条):");
+		for (int i = 0; i < Math.min(newItems.size(), 3); i++) {
+			ZcrmViewCustomerAddressSel item = newItems.get(i);
+			System.out.printf("第 %d 条:Customer_ID=%d, Customer_CODE=%s, Contacts=%s, Phone=%s, CustAddress=%s%n",
+				i+1, item.getCustomerId(), item.getCustomerCode(), item.getContacts(), item.getPhone(), item.getCustAddress());
+		}
+		System.out.println("=====================================");
+
+		// 5. 过滤关键字段为空的记录(修正:允许 CustAddress 为空字符串)
+		List<ZcrmViewCustomerAddressSel> validItems = newItems.stream()
+			.filter(item -> item.getCustomerId() != null)
+			.filter(item -> item.getCustomerCode() != null && !item.getCustomerCode().trim().isEmpty())
+			.filter(item -> item.getContacts() != null && !item.getContacts().trim().isEmpty())
+			.filter(item -> item.getPhone() != null && !item.getPhone().trim().isEmpty())
+			.filter(item -> item.getCustAddress() != null) // 仅过滤 null,允许空字符串
+			.collect(Collectors.toList());
+
+		System.out.println("过滤关键字段为空后,有效数据共 " + validItems.size() + " 条");
+		if (validItems.isEmpty()) {
+			System.err.println("所有记录都存在关键字段为空的情况,跳过处理");
+			return false;
+		}
+
+		// 6. 用JdbcTemplate查询已存在记录(优化:字段 trim() 匹配,避免空格差异)
+		List<ZcrmViewCustomerAddressSel> existingItems = new ArrayList<>();
+		try {
+			List<Object> params = new ArrayList<>();
+			StringBuilder sql = new StringBuilder("SELECT * FROM ZCRM_View_CustomerAddress_Sel WHERE ");
+
+			List<String> conditionParts = new ArrayList<>();
+			for (ZcrmViewCustomerAddressSel item : validItems) {
+				// 优化:数据库查询时对字段进行 TRIM(),匹配处理后的数据
+				conditionParts.add("(Customer_ID = ? " +
+					"AND TRIM(Customer_CODE) = ? " +
+					"AND TRIM(Contacts) = ? " +
+					"AND TRIM(Phone) = ? " +
+					"AND TRIM(CustAddress) = ?)");
+				params.add(item.getCustomerId());
+				params.add(item.getCustomerCode().trim());
+				params.add(item.getContacts().trim());
+				params.add(item.getPhone().trim());
+				params.add(item.getCustAddress().trim());
+			}
+
+			sql.append(String.join(" OR ", conditionParts));
+			System.out.println("查询已存在记录的SQL:" + sql);
+			System.out.println("查询参数总数:" + params.size());
+
+			existingItems = jdbcTemplate.query(
+				sql.toString(),
+				params.toArray(),
+				new BeanPropertyRowMapper<>(ZcrmViewCustomerAddressSel.class)
+			);
+			System.out.println("数据库中已存在的记录数:" + existingItems.size());
+		} catch (Exception e) {
+			System.err.println("查询已存在客户地址记录失败,详细异常信息:");
+			e.printStackTrace();
+			throw new RuntimeException("查询已存在客户地址记录失败", e);
+		}
+
+		// 7. 构建已存在记录的映射表(优化:增强唯一键处理)
+		Map<String, ZcrmViewCustomerAddressSel> existingMap = new HashMap<>();
+		for (ZcrmViewCustomerAddressSel existing : existingItems) {
+			String key = buildUniqueKey(existing);
+			existingMap.put(key, existing);
+		}
+
+		// 8. 分离新增和更新列表(打印新增记录详情,方便排查重复)
+		List<ZcrmViewCustomerAddressSel> insertList = new ArrayList<>();
+		List<ZcrmViewCustomerAddressSel> updateList = new ArrayList<>();
+
+		for (ZcrmViewCustomerAddressSel newItem : validItems) {
+			String key = buildUniqueKey(newItem);
+
+			if (existingMap.containsKey(key)) {
+				// 已存在:拷贝主键ID用于更新
+				ZcrmViewCustomerAddressSel existing = existingMap.get(key);
+				newItem.setId(existing.getId());
+				updateList.add(newItem);
+			} else {
+				// 不存在:新增,打印详情
+				insertList.add(newItem);
+				System.out.printf("新增记录:Customer_ID=%d, Contacts=%s, Customer_CODE=%s%n",
+					newItem.getCustomerId(), newItem.getContacts(), newItem.getCustomerCode());
+			}
+		}
+
+		System.out.println("需要新增的记录数:" + insertList.size());
+		System.out.println("需要更新的记录数:" + updateList.size());
+
+		// 9. 执行批量操作(优化批次大小,添加日志)
+		int batchSize = 200;
+		boolean insertSuccess = true;
+		if (!insertList.isEmpty()) {
+			insertSuccess = this.saveBatch(insertList, batchSize);
+			System.out.println("批量新增" + (insertSuccess ? "成功" : "失败") + ",共 " + insertList.size() + " 条记录");
+		}
+
+		boolean updateSuccess = true;
+		if (!updateList.isEmpty()) {
+			updateSuccess = this.updateBatchById(updateList, batchSize);
+			System.out.println("批量更新" + (updateSuccess ? "成功" : "失败") + ",共 " + updateList.size() + " 条记录");
+		}
+
+		System.out.println("=====================================");
+		System.out.println("批量操作完成,整体结果:" + (insertSuccess && updateSuccess ? "成功" : "失败"));
+		System.out.println("=====================================");
+
+		return insertSuccess && updateSuccess;
+	}
+
+	/**
+	 * 优化唯一键构建:去除全角空格、换行符、制表符,增强兼容性
+	 */
+	private String buildUniqueKey(ZcrmViewCustomerAddressSel item) {
+		// 处理规则:
+		// 1. trim() 去除前后空格
+		// 2. 替换全角空格为半角空格
+		// 3. 去除换行符、制表符等不可见字符
+		String customerCode = item.getCustomerCode().trim()
+			.replaceAll(" ", " ") // 全角空格转半角
+			.replaceAll("[\r\n\t]", ""); // 去除换行/制表符
+		String contacts = item.getContacts().trim()
+			.replaceAll(" ", " ")
+			.replaceAll("[\r\n\t]", "");
+		String phone = item.getPhone().trim()
+			.replaceAll(" ", " ")
+			.replaceAll("[\r\n\t]", "")
+			.replaceAll("-", "") // 去除手机号中的分隔符(如138-1234-5678)
+			.replaceAll(" ", ""); // 去除手机号中的空格(如138 1234 5678)
+		String custAddress = item.getCustAddress().trim()
+			.replaceAll(" ", " ")
+			.replaceAll("[\r\n\t]", "");
+
+		return String.format(
+			"%d-%s-%s-%s-%s",
+			item.getCustomerId(),
+			customerCode,
+			contacts,
+			phone,
+			custAddress
+		);
+	}
+}