[feat] 富文本图片替换_v2
This commit is contained in:
parent
3402971c49
commit
84d6fbd401
@ -31,6 +31,7 @@ import com.xxl.job.core.util.DateUtil;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.apache.commons.collections.CollectionUtils;
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.collections4.Get;
|
||||||
import org.apache.commons.lang.time.DateUtils;
|
import org.apache.commons.lang.time.DateUtils;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -56,6 +57,9 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DataImportNewServiceImpl implements DataImportNewService {
|
public class DataImportNewServiceImpl implements DataImportNewService {
|
||||||
@ -964,7 +968,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
.eq("sync_end_date", endDate)
|
.eq("sync_end_date", endDate)
|
||||||
.set("sf_update_num", targetCount);
|
.set("sf_update_num", targetCount);
|
||||||
dataBatchService.update(updateQw2);
|
dataBatchService.update(updateQw2);
|
||||||
|
|
||||||
log.info("数据更新完成,API: {},总更新数: {},开始时间: {},结束时间: {}", api, targetCount, beginDateStr, endDateStr);
|
log.info("数据更新完成,API: {},总更新数: {},开始时间: {},结束时间: {}", api, targetCount, beginDateStr, endDateStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2413,7 +2417,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
String sql = "";
|
String sql = "";
|
||||||
|
|
||||||
if (!param.getIds().isEmpty()){
|
if (!param.getIds().isEmpty()){
|
||||||
sql = "where Record in (" + param.getIds().stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")) + ")";
|
sql = "where Record in (" + param.getIds().stream().map(id -> "'" + id + "'").collect(Collectors.joining(",")) + ")";
|
||||||
}else {
|
}else {
|
||||||
Date beginDate = param.getBeginCreateDate();
|
Date beginDate = param.getBeginCreateDate();
|
||||||
Date endDate = param.getEndCreateDate();
|
Date endDate = param.getEndCreateDate();
|
||||||
@ -2671,7 +2675,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
qw1.eq("api", api);
|
qw1.eq("api", api);
|
||||||
qw1.eq("isHtmlFormatted", true);
|
qw1.eq("isHtmlFormatted", true);
|
||||||
List<DataField> fields = dataFieldService.list(qw1);
|
List<DataField> fields = dataFieldService.list(qw1);
|
||||||
|
|
||||||
for (DataField dataField : fields) {
|
for (DataField dataField : fields) {
|
||||||
String field = dataField.getField();
|
String field = dataField.getField();
|
||||||
String sqlCondition = "new_id is not null and " + field + " like '%<img%refid%'";
|
String sqlCondition = "new_id is not null and " + field + " like '%<img%refid%'";
|
||||||
@ -2696,7 +2700,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
for (int page = 0; page < totalPages; page++) {
|
for (int page = 0; page < totalPages; page++) {
|
||||||
|
|
||||||
List<Map<String, Object>> records = customMapper.list("Id, new_id, " + field, api,
|
List<Map<String, Object>> records = customMapper.list("Id, new_id, " + field, api,
|
||||||
sqlCondition + " order by Id limit " + (page * pageSize) + "," + pageSize);
|
sqlCondition + " order by Id limit " + (page * pageSize) + "," + pageSize);
|
||||||
|
|
||||||
log.info("处理对象 {} 字段 {} 第 {}/{} 页,本页记录数: {}", api, field, (page + 1), totalPages, records.size());
|
log.info("处理对象 {} 字段 {} 第 {}/{} 页,本页记录数: {}", api, field, (page + 1), totalPages, records.size());
|
||||||
|
|
||||||
@ -2715,7 +2719,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
|
|
||||||
// 处理富文本中的图片
|
// 处理富文本中的图片
|
||||||
String updatedContent = processRichTextImages(richTextContent, sourceId, api, field, sourceConnection, targetConnection);
|
String updatedContent = processRichTextImages(richTextContent, sourceId, api, field, sourceConnection, targetConnection);
|
||||||
|
|
||||||
// 如果内容有变化,则更新目标组织中的记录
|
// 如果内容有变化,则更新目标组织中的记录
|
||||||
if (!richTextContent.equals(updatedContent)) {
|
if (!richTextContent.equals(updatedContent)) {
|
||||||
|
|
||||||
@ -2782,7 +2786,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
/**
|
/**
|
||||||
* 处理富文本中的图片
|
* 处理富文本中的图片
|
||||||
* @param richTextContent 原始富文本内容
|
* @param richTextContent 原始富文本内容
|
||||||
* @param recordId 记录ID
|
* @param sourceRecordId 源记录ID
|
||||||
* @param api 对象API名称
|
* @param api 对象API名称
|
||||||
* @param field 字段名
|
* @param field 字段名
|
||||||
* @param sourceConnection 源组织连接
|
* @param sourceConnection 源组织连接
|
||||||
@ -2790,20 +2794,20 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
* @return 处理后的富文本内容
|
* @return 处理后的富文本内容
|
||||||
* @throws Exception 异常
|
* @throws Exception 异常
|
||||||
*/
|
*/
|
||||||
private String processRichTextImages(String richTextContent, String recordId, String api, String field,
|
private String processRichTextImages(String richTextContent, String sourceRecordId, String api, String field,
|
||||||
PartnerConnection sourceConnection, PartnerConnection targetConnection) throws Exception {
|
PartnerConnection sourceConnection, PartnerConnection targetConnection) throws Exception {
|
||||||
// 匹配富文本中包含refid的img标签
|
// 匹配富文本中包含refid的img标签
|
||||||
Pattern imgPattern = Pattern.compile("<img[^>]*refid=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE);
|
Pattern imgPattern = Pattern.compile("<img[^>]*refid=\"([^\"]+)\"[^>]*/?>", Pattern.CASE_INSENSITIVE);
|
||||||
Matcher matcher = imgPattern.matcher(richTextContent);
|
Matcher matcher = imgPattern.matcher(richTextContent);
|
||||||
|
|
||||||
StringBuffer updatedContent = new StringBuffer();
|
StringBuffer updatedContent = new StringBuffer();
|
||||||
Map<String, String> imageIdMap = new ConcurrentHashMap<>();
|
Map<String, String> imageIdMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
// 遍历所有匹配的img标签
|
// 遍历所有匹配的img标签
|
||||||
while (matcher.find()) {
|
while (matcher.find()) {
|
||||||
String imgTag = matcher.group(0);
|
String imgTag = matcher.group(0);
|
||||||
String refId = matcher.group(1);
|
String refId = matcher.group(1);
|
||||||
|
|
||||||
// 检查是否已经处理过这个refId
|
// 检查是否已经处理过这个refId
|
||||||
if (imageIdMap.containsKey(refId)) {
|
if (imageIdMap.containsKey(refId)) {
|
||||||
String newRefId = imageIdMap.get(refId);
|
String newRefId = imageIdMap.get(refId);
|
||||||
@ -2811,27 +2815,61 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag));
|
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 查询源组织中的ContentDocumentLink记录
|
// 查询源组织中的ContentDocumentLink记录
|
||||||
String cdlQuery = "SELECT Id, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '" + recordId + "' AND ContentDocumentId = '" + refId + "'";
|
String cdlQuery = "SELECT Id, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '" + sourceRecordId + "' AND ContentDocumentId = '" + refId + "'";
|
||||||
QueryResult cdlResult = sourceConnection.query(cdlQuery);
|
QueryResult cdlResult = sourceConnection.query(cdlQuery);
|
||||||
|
|
||||||
if (cdlResult.getRecords() != null && cdlResult.getRecords().length > 0) {
|
if (cdlResult.getRecords() != null && cdlResult.getRecords().length > 0) {
|
||||||
// 获取ContentVersion ID
|
// 构建源图片下载URL (参考Python脚本中的imageUrl构建方式)
|
||||||
String contentVersionId = getContentVersionId(sourceConnection, refId);
|
String downloadUrl = sourceConnection.getConfig().getServiceEndpoint() +
|
||||||
if (contentVersionId != null) {
|
String.format("/services/data/v55.0/sobjects/%s/%s/richTextImageFields/%s/%s",
|
||||||
// 获取目标组织中对应的ContentDocumentId
|
api, sourceRecordId, field, refId);
|
||||||
String newContentDocumentId = getTargetContentDocumentId(targetConnection, contentVersionId);
|
|
||||||
if (newContentDocumentId != null) {
|
// 下载源图片文件
|
||||||
imageIdMap.put(refId, newContentDocumentId);
|
InputStream inputStream = downloadImageFile(sourceConnection, downloadUrl);
|
||||||
String updatedImgTag = imgTag.replace("refid=\"" + refId + "\"", "refid=\"" + newContentDocumentId + "\"");
|
if (inputStream != null) {
|
||||||
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag));
|
// 获取图片的alt属性作为标题
|
||||||
continue;
|
String title = "image";
|
||||||
|
Pattern altPattern = Pattern.compile("alt=\"([^\"]*)\"", Pattern.CASE_INSENSITIVE);
|
||||||
|
Matcher altMatcher = altPattern.matcher(imgTag);
|
||||||
|
if (altMatcher.find()) {
|
||||||
|
title = altMatcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 上传图片到目标组织
|
||||||
|
String newContentVersionId = uploadImageFile(targetConnection, inputStream, title);
|
||||||
|
if (newContentVersionId != null) {
|
||||||
|
// 获取新的ContentDocumentId
|
||||||
|
String newContentDocumentId = getDocumentId(targetConnection, newContentVersionId);
|
||||||
|
if (newContentDocumentId != null) {
|
||||||
|
imageIdMap.put(refId, newContentDocumentId);
|
||||||
|
|
||||||
|
// 构建新的img标签 (参考Python脚本中的richTextUrl构建方式)
|
||||||
|
String newImageUrl = targetConnection.getConfig().getServiceEndpoint() +
|
||||||
|
"/sfc/servlet.shepherd/version/download/" + newContentVersionId;
|
||||||
|
String updatedImgTag = "<img src=\"" + newImageUrl + "\" refid=\"" + newContentDocumentId + "\"/>";
|
||||||
|
|
||||||
|
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag));
|
||||||
|
|
||||||
|
// 记录替换日志
|
||||||
|
richTextLogService.logRichTextImageReplace(
|
||||||
|
api,
|
||||||
|
field,
|
||||||
|
newContentDocumentId,
|
||||||
|
null, // recordOwnerId 可以从记录中获取,但此处简化处理
|
||||||
|
richTextContent,
|
||||||
|
refId, // 原图片链接
|
||||||
|
newImageUrl, // 新图片链接
|
||||||
|
newContentDocumentId // 新文件ID
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果找不到对应关系,保留原始标签
|
// 如果找不到对应关系,保留原始标签
|
||||||
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(imgTag));
|
matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(imgTag));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -2840,10 +2878,94 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
matcher.appendTail(updatedContent);
|
matcher.appendTail(updatedContent);
|
||||||
|
|
||||||
return updatedContent.toString();
|
return updatedContent.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从源组织下载图片文件
|
||||||
|
* @param connection 源组织连接
|
||||||
|
* @param downloadUrl 下载URL
|
||||||
|
* @return 图片文件数据
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
private InputStream downloadImageFile(PartnerConnection connection, String downloadUrl) throws Exception {
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try {
|
||||||
|
String token = connection.getSessionHeader().getSessionId();
|
||||||
|
|
||||||
|
Map<String, String> headers = Maps.newHashMap();
|
||||||
|
headers.put("Authorization", "Bearer " + token);
|
||||||
|
headers.put("connection", "keep-alive");
|
||||||
|
|
||||||
|
Response response = HttpUtil.doGet(downloadUrl, null, headers);
|
||||||
|
assert response.body() != null;
|
||||||
|
inputStream = response.body().byteStream();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("下载图片文件时发生错误,URL: {}", downloadUrl, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传图片文件到目标组织
|
||||||
|
* @param connection 目标组织连接
|
||||||
|
* @param inputStream 文件数据
|
||||||
|
* @param title 文件标题
|
||||||
|
* @return 新的ContentVersion ID
|
||||||
|
* @throws Exception 异常
|
||||||
|
*/
|
||||||
|
private String uploadImageFile(PartnerConnection connection, InputStream inputStream, String title) throws Exception {
|
||||||
|
String newId = null;
|
||||||
|
try {
|
||||||
|
int failCount = 0;
|
||||||
|
|
||||||
|
String token = connection.getSessionHeader().getSessionId();
|
||||||
|
|
||||||
|
String uploadUrl = connection.getConfig().getServiceEndpoint() +
|
||||||
|
"/services/data/v55.0/sobjects/ContentVersion";
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost(uploadUrl);
|
||||||
|
|
||||||
|
Map<String, String> headers = Maps.newHashMap();
|
||||||
|
headers.put("Authorization", "Bearer " + token);
|
||||||
|
headers.put("connection", "keep-alive");
|
||||||
|
|
||||||
|
|
||||||
|
com.alibaba.fastjson.JSONObject credentialsJsonParam = new com.alibaba.fastjson.JSONObject();
|
||||||
|
credentialsJsonParam.put("title", title);
|
||||||
|
credentialsJsonParam.put("pathOnClient", title + ".jpg");
|
||||||
|
|
||||||
|
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||||
|
builder.addTextBody("entity_content", credentialsJsonParam.toJSONString(), ContentType.APPLICATION_JSON);
|
||||||
|
builder.addBinaryBody("VersionData", inputStream, ContentType.APPLICATION_OCTET_STREAM, title + ".jpg");
|
||||||
|
HttpEntity entity = builder.build();
|
||||||
|
httpPost.setEntity(entity);
|
||||||
|
|
||||||
|
CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||||
|
|
||||||
|
CloseableHttpResponse response = null;
|
||||||
|
String respContent = null;
|
||||||
|
while (failCount < Const.MAX_FAIL_COUNT) {
|
||||||
|
response = httpClient.execute(httpPost);
|
||||||
|
if (response != null && response.getStatusLine() != null && response.getStatusLine().getStatusCode() < 400 && response.getEntity() != null) {
|
||||||
|
HttpEntity he = response.getEntity();
|
||||||
|
respContent = EntityUtils.toString(he, "UTF-8");
|
||||||
|
newId = String.valueOf(com.alibaba.fastjson.JSONObject.parseObject(respContent).get("id"));
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
failCount++;
|
||||||
|
log.error("富文本图片替换图片文件上传失败,返回体信息:" + JSON.toJSONString(response));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("上传图片文件时发生错误,标题: {}", title, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return newId;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取ContentVersion ID
|
* 获取ContentVersion ID
|
||||||
* @param connection Salesforce连接
|
* @param connection Salesforce连接
|
||||||
@ -2859,7 +2981,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取目标组织中的ContentDocumentId
|
* 获取目标组织中的ContentDocumentId
|
||||||
* @param connection 目标组织连接
|
* @param connection 目标组织连接
|
||||||
@ -2984,7 +3106,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
JSONObject jsonObject = objects.getJSONObject(i);
|
JSONObject jsonObject = objects.getJSONObject(i);
|
||||||
String id = jsonObject.getString(Const.ID);
|
String id = jsonObject.getString(Const.ID);
|
||||||
List<Map<String, Object>> maps = buildUpdateMaps(api, jsonObject, stringList);
|
List<Map<String, Object>> maps = buildUpdateMaps(api, jsonObject, stringList);
|
||||||
|
|
||||||
if (maps!=null && !maps.isEmpty()) {
|
if (maps!=null && !maps.isEmpty()) {
|
||||||
log.info("准备更新记录,API: {}, ID: {}, 匹配字段信息: {}", api, id, maps);
|
log.info("准备更新记录,API: {}, ID: {}, 匹配字段信息: {}", api, id, maps);
|
||||||
customMapper.updateNewIdByFields(api, maps, id);
|
customMapper.updateNewIdByFields(api, maps, id);
|
||||||
@ -2999,7 +3121,7 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
|
|
||||||
for (String key : stringList) {
|
for (String key : stringList) {
|
||||||
Map<String, Object> paramMap = null;
|
Map<String, Object> paramMap = null;
|
||||||
|
|
||||||
switch (api) {
|
switch (api) {
|
||||||
case "AccountContactRelation":
|
case "AccountContactRelation":
|
||||||
paramMap = handleAccountContactRelation(key, jsonObject);
|
paramMap = handleAccountContactRelation(key, jsonObject);
|
||||||
@ -3036,12 +3158,12 @@ public class DataImportNewServiceImpl implements DataImportNewService {
|
|||||||
log.info("普通字段处理,API: {}, 字段: {}, 值: {}", api, key, jsonObject.getString(key));
|
log.info("普通字段处理,API: {}, 字段: {}, 值: {}", api, key, jsonObject.getString(key));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!paramMap.isEmpty()) {
|
if (!paramMap.isEmpty()) {
|
||||||
maps.add(paramMap);
|
maps.add(paramMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return maps;
|
return maps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user