|
|
@@ -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
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|