From 2457665b145e7b7107aca68c4866b3a6f47bbd8a Mon Sep 17 00:00:00 2001 From: Kris <2893855659@qq.com> Date: Fri, 10 Oct 2025 11:21:43 +0800 Subject: [PATCH] =?UTF-8?q?[feat]=20=20=E5=AF=8C=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=9B=BF=E6=8D=A2=E5=88=9D=E6=AD=A5=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/celnet/datadump/entity/DataField.java | 7 + .../celnet/datadump/entity/RichTextLog.java | 68 ++++ .../datadump/mapper/RichTextLogMapper.java | 18 + .../datadump/service/RichTextLogService.java | 31 ++ .../service/impl/CommonServiceImpl.java | 88 ++-- .../impl/DataImportNewServiceImpl.java | 381 +++++++----------- .../service/impl/RichTextLogServiceImpl.java | 35 ++ 7 files changed, 355 insertions(+), 273 deletions(-) create mode 100644 src/main/java/com/celnet/datadump/entity/RichTextLog.java create mode 100644 src/main/java/com/celnet/datadump/mapper/RichTextLogMapper.java create mode 100644 src/main/java/com/celnet/datadump/service/RichTextLogService.java create mode 100644 src/main/java/com/celnet/datadump/service/impl/RichTextLogServiceImpl.java diff --git a/src/main/java/com/celnet/datadump/entity/DataField.java b/src/main/java/com/celnet/datadump/entity/DataField.java index 03bb633..d5ca2a6 100644 --- a/src/main/java/com/celnet/datadump/entity/DataField.java +++ b/src/main/java/com/celnet/datadump/entity/DataField.java @@ -104,6 +104,13 @@ public class DataField implements Serializable { @ApiModelProperty(value = "是否更新") private Boolean isUpdateable; + /** + * 是否富文本字段 + */ + @TableField("isHtmlFormatted") + @ApiModelProperty(value = "是否富文本字段") + private Boolean isHtmlFormatted; + /** * 是否为空(目标库) */ diff --git a/src/main/java/com/celnet/datadump/entity/RichTextLog.java b/src/main/java/com/celnet/datadump/entity/RichTextLog.java new file mode 100644 index 0000000..b32346a --- /dev/null +++ b/src/main/java/com/celnet/datadump/entity/RichTextLog.java @@ -0,0 +1,68 @@ +package com.celnet.datadump.entity; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 富文本替换记录表 + *

+ * + * @author Lingma + * @since 2025-10-10 + */ +@Getter +@Setter +@TableName("richText_log") +@ApiModel(value = "RichTextLog对象", description = "富文本替换记录表") +public class RichTextLog implements Serializable { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + @TableId(value = "ID", type = IdType.AUTO) + private Long id; + + @ApiModelProperty("对象API名称") + @TableField("api") + private String api; + + @ApiModelProperty("字段API名称") + @TableField("field") + private String field; + + @ApiModelProperty("记录ID") + @TableField("record_id") + private String recordId; + + @ApiModelProperty("记录OwnerID") + @TableField("record_owner_id") + private String recordOwnerId; + + @ApiModelProperty("原始富文本内容") + @TableField("original_rich_text") + private String originalRichText; + + @ApiModelProperty("原图片链接") + @TableField("original_image_url") + private String originalImageUrl; + + @ApiModelProperty("新图片链接") + @TableField("new_image_url") + private String newImageUrl; + + @ApiModelProperty("新文件ID(ContentDocumentId)") + @TableField("new_file_id") + private String newFileId; + + @ApiModelProperty("创建时间") + @TableField(value = "created_time", fill = FieldFill.INSERT) + private Date createdTime; + +} \ No newline at end of file diff --git a/src/main/java/com/celnet/datadump/mapper/RichTextLogMapper.java b/src/main/java/com/celnet/datadump/mapper/RichTextLogMapper.java new file mode 100644 index 0000000..5010e01 --- /dev/null +++ b/src/main/java/com/celnet/datadump/mapper/RichTextLogMapper.java @@ -0,0 +1,18 @@ +package com.celnet.datadump.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.celnet.datadump.entity.RichTextLog; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 富文本替换记录表 Mapper 接口 + *

+ * + * @author Lingma + * @since 2025-10-10 + */ +@Mapper +public interface RichTextLogMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/src/main/java/com/celnet/datadump/service/RichTextLogService.java b/src/main/java/com/celnet/datadump/service/RichTextLogService.java new file mode 100644 index 0000000..0beadca --- /dev/null +++ b/src/main/java/com/celnet/datadump/service/RichTextLogService.java @@ -0,0 +1,31 @@ +package com.celnet.datadump.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.celnet.datadump.entity.RichTextLog; + +/** + *

+ * 富文本替换记录表 服务类 + *

+ * + * @author Lingma + * @since 2025-10-10 + */ +public interface RichTextLogService extends IService { + + /** + * 记录富文本图片替换日志 + * @param api 对象API名称 + * @param field 字段API名称 + * @param recordId 记录ID + * @param recordOwnerId 记录OwnerID + * @param originalRichText 原始富文本内容 + * @param originalImageUrl 原图片链接 + * @param newImageUrl 新图片链接 + * @param newFileId 新文件ID(ContentDocumentId) + */ + void logRichTextImageReplace(String api, String field, String recordId, String recordOwnerId, + String originalRichText, String originalImageUrl, + String newImageUrl, String newFileId); + +} \ No newline at end of file diff --git a/src/main/java/com/celnet/datadump/service/impl/CommonServiceImpl.java b/src/main/java/com/celnet/datadump/service/impl/CommonServiceImpl.java index bd427ea..5b47add 100644 --- a/src/main/java/com/celnet/datadump/service/impl/CommonServiceImpl.java +++ b/src/main/java/com/celnet/datadump/service/impl/CommonServiceImpl.java @@ -979,10 +979,7 @@ public class CommonServiceImpl implements CommonService { String blobField = null; List dataPicklists = Lists.newArrayList(); for (Field field : dsr.getFields()) { - if ("textarea".equalsIgnoreCase(field.getType().toString()) && field.isHtmlFormatted()) { - log.info("检测到富文本字段: {},API: {}", field.getName(), apiName); - } // 过滤字段 if (Const.TABLE_FILTERS.contains(field.getName())) { continue; @@ -1016,6 +1013,15 @@ public class CommonServiceImpl implements CommonService { } else { log.info("referenceTo too long set null, api:{}, field:{}, reference to:{}", apiName, field.getName(), StringUtils.join(field.getReferenceTo(), ",")); } + if ("textarea".equalsIgnoreCase(field.getType().toString()) && field.isHtmlFormatted()) { + log.info("检测到富文本字段: {},API: {}", field.getName(), apiName); + dataField.setIsHtmlFormatted(true); + Map htmlMap = Maps.newHashMap(); + htmlMap.put("type", "text"); + htmlMap.put("comment", field.getLabel()+ "备份字段"); + htmlMap.put("name", field.getName()+ "_back"); + list.add(htmlMap); + } if (field.getReferenceTo().length > 1){ if (field.getName().contains("Id") && !"OwnerId".equals(field.getName()) && !"UserOrGroupId".equals(field.getName())){ LinkConfig linkConfig = new LinkConfig(); @@ -1337,6 +1343,7 @@ public class CommonServiceImpl implements CommonService { String blobField = null; List dataPicklists = Lists.newArrayList(); for (Field field : dsr.getFields()) { + // 过滤字段 if (Const.TABLE_FILTERS.contains(field.getName())) { continue; @@ -1356,48 +1363,47 @@ public class CommonServiceImpl implements CommonService { list.add(map); DataField dataField = new DataField(); dataField.setApi(apiName); - dataField.setSfType(sfType); dataField.setField(field.getName()); dataField.setName(field.getLabel()); - dataField.setIsCreateable(field.getCreateable()); - dataField.setIsUpdateable(field.getUpdateable()); - dataField.setIsNillable(field.getNillable()); - dataField.setIsDefaultedOnCreate(field.getDefaultedOnCreate()); - String join = null; - // 会有非常多映射关系的字段在 这里置空不管 - if (field.getReferenceTo().length <= 3) { - join = StringUtils.join(field.getReferenceTo(), ","); - } else { - log.info("referenceTo too long set null, api:{}, field:{}, reference to:{}", apiName, field.getName(), StringUtils.join(field.getReferenceTo(), ",")); - } - if (field.getReferenceTo().length > 1){ - if (field.getName().contains("Id") && !"OwnerId".equals(field.getName()) && !"UserOrGroupId".equals(field.getName())){ - LinkConfig linkConfig = new LinkConfig(); - linkConfig.setApi(apiName); - linkConfig.setLabel(label); - linkConfig.setField(field.getName()); - linkConfig.setLinkField(StringUtils.replace(field.getName(), "Id", "_Type")); - linkConfigService.save(linkConfig); - } - } - // picklist保存到picklist表 - if ("picklist".equalsIgnoreCase(sfType)) { - join = "data_picklist"; - PicklistEntry[] picklistValues = field.getPicklistValues(); - if (ArrayUtils.isNotEmpty(picklistValues)) { - for (PicklistEntry picklistValue : picklistValues) { - DataPicklist dataPicklist = new DataPicklist(); - dataPicklist.setApi(apiName); - dataPicklist.setField(field.getName()); - dataPicklist.setLabel(picklistValue.getLabel()); - dataPicklist.setValue(picklistValue.getValue()); - dataPicklists.add(dataPicklist); - } - } - } - dataField.setReferenceTo(join); + dataField.setReferenceTo(field.getReferenceTo() != null && field.getReferenceTo().length > 0 ? field.getReferenceTo()[0] : null); + dataField.setLeftIndex(field.getLeftSideReferenceTo() != null && field.getLeftSideReferenceTo().length > 0 ? field.getLeftSideReferenceTo()[0] : null); + dataField.setRightIndex(field.getRightSideReferenceTo() != null && field.getRightSideReferenceTo().length > 0 ? field.getRightSideReferenceTo()[0] : null); + dataField.setOnList(field.isListVisible()); + dataField.setSfType(sfType); + dataField.setCreateable(field.isCreateable()); + dataField.setUpdateable(field.isUpdateable()); + dataField.setHtmlFormatted(field.isHtmlFormatted()); + dataField.setNillable(field.isNillable()); + dataField.setDefaultedOnCreate(field.isDefaultedOnCreate()); fieldList.add(dataField); fields.add(field.getName()); + + // 如果是富文本字段,添加备份字段 + if (field.isHtmlFormatted()) { + String backupFieldName = field.getName() + "_back"; + Map backupMap = Maps.newHashMap(); + backupMap.put("type", DataUtil.fieldTypeToMysql(field)); + backupMap.put("comment", field.getLabel() + " 备份"); + backupMap.put("name", backupFieldName); + list.add(backupMap); + + DataField backupDataField = new DataField(); + backupDataField.setApi(apiName); + backupDataField.setField(backupFieldName); + backupDataField.setName(field.getLabel() + " 备份"); + backupDataField.setReferenceTo(field.getReferenceTo() != null && field.getReferenceTo().length > 0 ? field.getReferenceTo()[0] : null); + backupDataField.setLeftIndex(field.getLeftSideReferenceTo() != null && field.getLeftSideReferenceTo().length > 0 ? field.getLeftSideReferenceTo()[0] : null); + backupDataField.setRightIndex(field.getRightSideReferenceTo() != null && field.getRightSideReferenceTo().length > 0 ? field.getRightSideReferenceTo()[0] : null); + backupDataField.setOnList(field.isListVisible()); + backupDataField.setSfType(sfType); + backupDataField.setCreateable(field.isCreateable()); + backupDataField.setUpdateable(field.isUpdateable()); + backupDataField.setHtmlFormatted(field.isHtmlFormatted()); + backupDataField.setNillable(field.isNillable()); + backupDataField.setDefaultedOnCreate(field.isDefaultedOnCreate()); + fieldList.add(backupDataField); + fields.add(backupFieldName); + } } // 存在ownerId字段 String extraField = ""; diff --git a/src/main/java/com/celnet/datadump/service/impl/DataImportNewServiceImpl.java b/src/main/java/com/celnet/datadump/service/impl/DataImportNewServiceImpl.java index 33f8c7e..7c973e6 100644 --- a/src/main/java/com/celnet/datadump/service/impl/DataImportNewServiceImpl.java +++ b/src/main/java/com/celnet/datadump/service/impl/DataImportNewServiceImpl.java @@ -93,6 +93,9 @@ public class DataImportNewServiceImpl implements DataImportNewService { @Autowired private DataLogService dataLogService; + @Autowired + private RichTextLogService richTextLogService; + private static final String TEMP_FILE_PATH = "verify/"; static { @@ -2637,85 +2640,99 @@ public class DataImportNewServiceImpl implements DataImportNewService { @Override public ReturnT richTextImageReplace(SalesforceParam param) throws Exception { log.info("richTextImageReplace execute start .................."); + String beginDateStr = null; + String endDateStr = null; + if (param.getType() != 1){ + Date beginDate = param.getBeginCreateDate(); + Date endDate = param.getEndCreateDate(); + if (param.getBeginCreateDate() != null && param.getEndCreateDate() != null){ + beginDateStr = DateUtil.format(beginDate, "yyyy-MM-dd HH:mm:ss"); + endDateStr = DateUtil.format(endDate, "yyyy-MM-dd HH:mm:ss"); + } + } // 获取连接 PartnerConnection sourceConnection = salesforceConnect.createConnect(); PartnerConnection targetConnection = salesforceTargetConnect.createConnect(); - String api = param.getApi(); - String field = null; - - if (StringUtils.isBlank(api)) { - return new ReturnT<>(500, "api不能为空!"); + QueryWrapper qw = new QueryWrapper<>(); + if (StringUtils.isNotBlank(param.getApi())) { + List apis = DataUtil.toIdList(param.getApi()); + qw.in("name", apis); } + qw.eq("data_work", true); + List objectList = dataObjectService.list(qw); - if (StringUtils.isBlank(field)) { - return new ReturnT<>(500, "field不能为空!"); - } + for (DataObject dataObject : objectList) { + String api = dataObject.getName(); + QueryWrapper qw1 = new QueryWrapper<>(); + qw1.eq("api", api); + qw1.eq("isHtmlFormatted", true); + List fields = dataFieldService.list(qw1); + + for (DataField dataField : fields) { + String field = dataField.getField(); + String sqlCondition = "new_id is not null and " + field + " like '%> records = customMapper.list("Id, new_id, " + field, api, + sqlCondition + " order by Id limit " + (page * pageSize) + "," + pageSize); - for (int page = 0; page < totalPages; page++) { - List> records = customMapper.list("Id, new_id, " + field, api, - sqlCondition + " order by Id limit " + (page * pageSize) + "," + pageSize); + log.info("处理对象 {} 字段 {} 第 {}/{} 页,本页记录数: {}", api, field, (page + 1), totalPages, records.size()); - log.info("处理第 {}/{} 页,本页记录数: {}", (page + 1), totalPages, records.size()); + for (Map record : records) { + try { + String sourceId = (String) record.get("Id"); + String targetId = (String) record.get("new_id"); + String richTextContent = (String) record.get(field); - for (Map record : records) { - try { - String sourceId = (String) record.get("Id"); - String targetId = (String) record.get("new_id"); - String richTextContent = (String) record.get(field); + if (StringUtils.isBlank(richTextContent) || !richTextContent.contains("refid")) { + continue; + } - if (StringUtils.isBlank(richTextContent) || !richTextContent.contains("refid")) { - continue; - } + // 处理富文本中的图片 + String updatedContent = processRichTextImages(richTextContent, sourceId, api, field, sourceConnection, targetConnection); + + // 如果内容有变化,则更新目标组织中的记录 + if (!richTextContent.equals(updatedContent)) { + // 更新目标组织中的记录 + SObject sObject = new SObject(); + sObject.setType(api); + sObject.setId(targetId); + sObject.setField(field, updatedContent); + + // 同时更新备份字段 + sObject.setField(field + "_back", richTextContent); - // 处理富文本中的图片 - String updatedContent = null; - // 如果内容有变化,则更新目标组织中的记录 - if (!richTextContent.equals(updatedContent)) { - // 更新目标组织中的记录 - SObject sObject = new SObject(); - sObject.setType(api); - sObject.setId(targetId); - sObject.setField(field, updatedContent); - - SaveResult[] results = targetConnection.update(new SObject[]{sObject}); - if (results != null && results.length > 0 && results[0].getSuccess()) { - log.info("成功更新记录 {} 的富文本内容", targetId); - } else { - log.error("更新记录 {} 的富文本内容失败: {}", targetId, - results != null && results.length > 0 ? JSON.toJSONString(results[0].getErrors()) : "未知错误"); + SaveResult[] results = targetConnection.update(new SObject[]{sObject}); + if (results != null && results.length > 0 && results[0].getSuccess()) { + log.info("成功更新记录 {} 的富文本内容", targetId); + } else { + log.error("更新记录 {} 的富文本内容失败: {}", targetId, + results != null && results.length > 0 ? JSON.toJSONString(results[0].getErrors()) : "未知错误"); + } + } + } catch (Exception e) { + log.error("处理记录 {} 的富文本内容时发生错误: {}", record.get("Id"), e.getMessage(), e); } } - } catch (Exception e) { - log.error("处理记录 {} 的富文本内容时发生错误: {}", record.get("Id"), e.getMessage(), e); } } } @@ -2724,105 +2741,6 @@ public class DataImportNewServiceImpl implements DataImportNewService { return ReturnT.SUCCESS; } - /** - * 处理富文本中的图片 - * @param richTextContent 原始富文本内容 - * @param recordId 记录ID - * @param api 对象API名称 - * @param field 字段名 - * @param sourceConnection 源组织连接 - * @param targetConnection 目标组织连接 - * @return 处理后的富文本内容 - * @throws Exception 异常 - */ -// private String processRichTextImages(String richTextContent, String recordId, String api, String field, -// PartnerConnection sourceConnection, PartnerConnection targetConnection) throws Exception { -// // 使用正则表达式匹配标签 -// Pattern imgPattern = Pattern.compile("]*refid=\\\"([^\"]*)\\\"[^>]*/?>", Pattern.CASE_INSENSITIVE); -// Matcher matcher = imgPattern.matcher(richTextContent); -// -// StringBuffer result = new StringBuffer(); -// boolean hasReplacements = false; -// -// while (matcher.find()) { -// String imgTag = matcher.group(0); -// String refId = matcher.group(1); -// -// try { -// // 从源组织下载图片 -// String sourceSessionId = sourceConnection.getSessionHeader().getSessionId(); -// String sourceInstanceUrl = sourceConnection.getConfig().getServiceEndpoint().replace("/services/Soap/u/50.0", ""); -// -// String imageUrl = sourceInstanceUrl + String.format(Const.SF_RICH_TEXT_FILE_URL, api, recordId, field, refId); -// -// // 构建请求头 -// Map headers = new HashMap<>(); -// headers.put("Authorization", "Bearer " + sourceSessionId); -// -// // 下载图片 -// Response response = HttpUtil.doGet(imageUrl, null, headers); -// if (response.code() == 200 && response.body() != null) { -// // 将图片转换为Base64 -// byte[] imageBytes = response.body().bytes(); -// String base64Image = Base64.getEncoder().encodeToString(imageBytes); -// -// // 在目标组织中上传图片 -// String targetSessionId = targetConnection.getSessionHeader().getSessionId(); -// String targetInstanceUrl = targetConnection.getConfig().getServiceEndpoint().replace("/services/Soap/u/50.0", ""); -// -// // 创建ContentVersion来存储图片 -// JSONObject contentVersion = new JSONObject(); -// contentVersion.put("Title", "RichTextImage_" + refId); -// contentVersion.put("PathOnClient", "image.jpg"); -// contentVersion.put("VersionData", base64Image); -// -// // 上传到目标组织 -// String uploadUrl = targetInstanceUrl + "/services/data/v55.0/sobjects/ContentVersion/"; -// Map targetHeaders = new HashMap<>(); -// targetHeaders.put("Authorization", "Bearer " + targetSessionId); -// targetHeaders.put("Content-Type", "application/json"); -// -// Response uploadResponse = HttpUtil.doPost(uploadUrl, contentVersion.toJSONString(), targetHeaders); -// if (uploadResponse.code() == 201 || uploadResponse.code() == 200) { -// // 获取上传后的ContentVersion ID -// String responseContent = uploadResponse.body().string(); -// JSONObject jsonResponse = JSON.parseObject(responseContent); -// String contentVersionId = jsonResponse.getString("id"); -// -// // 获取ContentDocumentId -// String contentDocumentId = getDocumentId(targetConnection, contentVersionId); -// -// // 生成新的图片URL -// String newImgUrl = targetInstanceUrl + "/sfc/servlet.shepherd/document/download/" + contentDocumentId; -// String newImgTag = imgTag.replaceFirst("src\\s*=\\s*\"[^"]*\"", "src=\"" + newImgUrl + "\"") -// .replaceFirst("refid\\s*=\\s*\"[^"]*\"", ""); -// -// matcher.appendReplacement(result, Matcher.quoteReplacement(newImgTag)); -// hasReplacements = true; -// -// log.info("成功处理图片 refId: {}", refId); -// } else { -// log.error("上传图片到目标组织失败,refId: {}, 状态码: {}, 响应: {}", -// refId, uploadResponse.code(), uploadResponse.message()); -// matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); -// } -// } else { -// log.error("从源组织下载图片失败,refId: {}, 状态码: {}", refId, response.code()); -// matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); -// } -// } catch (Exception e) { -// log.error("处理图片 refId: {} 时发生错误: {}", refId, e.getMessage(), e); -// matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); -// } -// } -// -// matcher.appendTail(result); -// -// // 如果有替换,则返回新内容,否则返回原始内容 -// return hasReplacements ? result.toString() : richTextContent; -// } -// - /** * 处理富文本中的图片 * @param richTextContent 原始富文本内容 @@ -2836,89 +2754,88 @@ public class DataImportNewServiceImpl implements DataImportNewService { */ private String processRichTextImages(String richTextContent, String recordId, String api, String field, PartnerConnection sourceConnection, PartnerConnection targetConnection) throws Exception { - // 使用正则表达式匹配标签 - Pattern imgPattern = Pattern.compile("]*refid=\\\"([^\"]*)\\\"[^>]*/?>", Pattern.CASE_INSENSITIVE); + // 匹配富文本中包含refid的img标签 + Pattern imgPattern = Pattern.compile("]*refid=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE); Matcher matcher = imgPattern.matcher(richTextContent); - - StringBuffer result = new StringBuffer(); - boolean hasReplacements = false; - + + StringBuffer updatedContent = new StringBuffer(); + Map imageIdMap = new ConcurrentHashMap<>(); + + // 遍历所有匹配的img标签 while (matcher.find()) { String imgTag = matcher.group(0); String refId = matcher.group(1); - + + // 检查是否已经处理过这个refId + if (imageIdMap.containsKey(refId)) { + String newRefId = imageIdMap.get(refId); + String updatedImgTag = imgTag.replace("refid=\"" + refId + "\"", "refid=\"" + newRefId + "\""); + matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag)); + continue; + } + try { - // 从源组织下载图片 - String sourceSessionId = sourceConnection.getSessionHeader().getSessionId(); - String sourceInstanceUrl = sourceConnection.getConfig().getServiceEndpoint().replace("/services/Soap/u/50.0", ""); - - String imageUrl = sourceInstanceUrl + String.format(Const.SF_RICH_TEXT_FILE_URL, api, recordId, field, refId); - - // 构建请求头 - Map headers = new HashMap<>(); - headers.put("Authorization", "Bearer " + sourceSessionId); - - // 下载图片 - Response response = HttpUtil.doGet(imageUrl, null, headers); - if (response.code() == 200 && response.body() != null) { - // 将图片转换为Base64 - byte[] imageBytes = response.body().bytes(); - String base64Image = Base64.getEncoder().encodeToString(imageBytes); - - // 在目标组织中上传图片 - String targetSessionId = targetConnection.getSessionHeader().getSessionId(); - String targetInstanceUrl = targetConnection.getConfig().getServiceEndpoint().replace("/services/Soap/u/50.0", ""); - - // 创建ContentVersion来存储图片 - JSONObject contentVersion = new JSONObject(); - contentVersion.put("Title", "RichTextImage_" + refId); - contentVersion.put("PathOnClient", "image.jpg"); - contentVersion.put("VersionData", base64Image); - - // 上传到目标组织 - String uploadUrl = targetInstanceUrl + "/services/data/v55.0/sobjects/ContentVersion/"; - Map targetHeaders = new HashMap<>(); - targetHeaders.put("Authorization", "Bearer " + targetSessionId); - targetHeaders.put("Content-Type", "application/json"); - - Response uploadResponse = HttpUtil.doPost(uploadUrl, contentVersion.toJSONString(), targetHeaders); - if (uploadResponse.code() == 201 || uploadResponse.code() == 200) { - // 获取上传后的ContentVersion ID - String responseContent = uploadResponse.body().string(); - JSONObject jsonResponse = null; - String contentVersionId = jsonResponse.getString("id"); - - // 获取ContentDocumentId - String contentDocumentId = getDocumentId(targetConnection, contentVersionId); - - // 生成新的图片URL - String newImgUrl = targetInstanceUrl + "/sfc/servlet.shepherd/document/download/" + contentDocumentId; - String newImgTag = imgTag.replaceFirst("src\\s*=\\s*\"[^\"]*\"", "src=\"" + newImgUrl + "\"") - .replaceFirst("refid\\s*=\\s*\"[^\"]*\"", ""); - - matcher.appendReplacement(result, Matcher.quoteReplacement(newImgTag)); - hasReplacements = true; - - log.info("成功处理图片 refId: {}", refId); - } else { - log.error("上传图片到目标组织失败,refId: {}, 状态码: {}, 响应: {}", - refId, uploadResponse.code(), uploadResponse.message()); - matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); + // 查询源组织中的ContentDocumentLink记录 + String cdlQuery = "SELECT Id, ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = '" + recordId + "' AND ContentDocumentId = '" + refId + "'"; + QueryResult cdlResult = sourceConnection.query(cdlQuery); + + if (cdlResult.getRecords() != null && cdlResult.getRecords().length > 0) { + // 获取ContentVersion ID + String contentVersionId = getContentVersionId(sourceConnection, refId); + if (contentVersionId != null) { + // 获取目标组织中对应的ContentDocumentId + String newContentDocumentId = getTargetContentDocumentId(targetConnection, contentVersionId); + if (newContentDocumentId != null) { + imageIdMap.put(refId, newContentDocumentId); + String updatedImgTag = imgTag.replace("refid=\"" + refId + "\"", "refid=\"" + newContentDocumentId + "\""); + matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(updatedImgTag)); + continue; + } } - } else { - log.error("从源组织下载图片失败,refId: {}, 状态码: {}", refId, response.code()); - matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); } + + // 如果找不到对应关系,保留原始标签 + matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(imgTag)); } catch (Exception e) { - log.error("处理图片 refId: {} 时发生错误: {}", refId, e.getMessage(), e); - matcher.appendReplacement(result, Matcher.quoteReplacement(imgTag)); + log.error("处理富文本图片时发生错误,refId: {}", refId, e); + matcher.appendReplacement(updatedContent, Matcher.quoteReplacement(imgTag)); } } - - matcher.appendTail(result); - - // 如果有替换,则返回新内容,否则返回原始内容 - return hasReplacements ? result.toString() : richTextContent; + matcher.appendTail(updatedContent); + + return updatedContent.toString(); + } + + /** + * 获取ContentVersion ID + * @param connection Salesforce连接 + * @param contentDocumentId ContentDocument ID + * @return ContentVersion ID + * @throws Exception 异常 + */ + private String getContentVersionId(PartnerConnection connection, String contentDocumentId) throws Exception { + String query = "SELECT Id FROM ContentVersion WHERE ContentDocumentId = '" + contentDocumentId + "' ORDER BY CreatedDate DESC LIMIT 1"; + QueryResult result = connection.query(query); + if (result.getRecords() != null && result.getRecords().length > 0) { + return (String) result.getRecords()[0].getField("Id"); + } + return null; + } + + /** + * 获取目标组织中的ContentDocumentId + * @param connection 目标组织连接 + * @param contentVersionId ContentVersion ID + * @return 目标组织中的ContentDocumentId + * @throws Exception 异常 + */ + private String getTargetContentDocumentId(PartnerConnection connection, String contentVersionId) throws Exception { + String query = "SELECT ContentDocumentId FROM ContentVersion WHERE Id = '" + contentVersionId + "'"; + QueryResult result = connection.query(query); + if (result.getRecords() != null && result.getRecords().length > 0) { + return (String) result.getRecords()[0].getField("ContentDocumentId"); + } + return null; } /** diff --git a/src/main/java/com/celnet/datadump/service/impl/RichTextLogServiceImpl.java b/src/main/java/com/celnet/datadump/service/impl/RichTextLogServiceImpl.java new file mode 100644 index 0000000..5a5a4d0 --- /dev/null +++ b/src/main/java/com/celnet/datadump/service/impl/RichTextLogServiceImpl.java @@ -0,0 +1,35 @@ +package com.celnet.datadump.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.celnet.datadump.entity.RichTextLog; +import com.celnet.datadump.mapper.RichTextLogMapper; +import com.celnet.datadump.service.RichTextLogService; +import org.springframework.stereotype.Service; + +/** + *

+ * 富文本替换记录表 服务实现类 + *

+ * + * @author Lingma + * @since 2025-10-10 + */ +@Service +public class RichTextLogServiceImpl extends ServiceImpl implements RichTextLogService { + + @Override + public void logRichTextImageReplace(String api, String field, String recordId, String recordOwnerId, + String originalRichText, String originalImageUrl, + String newImageUrl, String newFileId) { + RichTextLog log = new RichTextLog(); + log.setApi(api); + log.setField(field); + log.setRecordId(recordId); + log.setRecordOwnerId(recordOwnerId); + log.setOriginalRichText(originalRichText); + log.setOriginalImageUrl(originalImageUrl); + log.setNewImageUrl(newImageUrl); + log.setNewFileId(newFileId); + this.save(log); + } +} \ No newline at end of file