[feat] 富文本图片替换_v2

This commit is contained in:
Kris 2025-10-10 16:26:42 +08:00
parent 3402971c49
commit 84d6fbd401

View File

@ -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 {
@ -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();
@ -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());
@ -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,10 +2794,10 @@ 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();
@ -2814,20 +2818,54 @@ public class DataImportNewServiceImpl implements DataImportNewService {
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;
}
} }
} }
} }
@ -2844,6 +2882,90 @@ public class DataImportNewServiceImpl implements DataImportNewService {
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连接