[feat] 富文本图片替换初步实现
This commit is contained in:
parent
33d23213dd
commit
2457665b14
@ -104,6 +104,13 @@ public class DataField implements Serializable {
|
||||
@ApiModelProperty(value = "是否更新")
|
||||
private Boolean isUpdateable;
|
||||
|
||||
/**
|
||||
* 是否富文本字段
|
||||
*/
|
||||
@TableField("isHtmlFormatted")
|
||||
@ApiModelProperty(value = "是否富文本字段")
|
||||
private Boolean isHtmlFormatted;
|
||||
|
||||
/**
|
||||
* 是否为空(目标库)
|
||||
*/
|
||||
|
||||
68
src/main/java/com/celnet/datadump/entity/RichTextLog.java
Normal file
68
src/main/java/com/celnet/datadump/entity/RichTextLog.java
Normal file
@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 富文本替换记录表
|
||||
* </p>
|
||||
*
|
||||
* @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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 富文本替换记录表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author Lingma
|
||||
* @since 2025-10-10
|
||||
*/
|
||||
@Mapper
|
||||
public interface RichTextLogMapper extends BaseMapper<RichTextLog> {
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.celnet.datadump.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.celnet.datadump.entity.RichTextLog;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 富文本替换记录表 服务类
|
||||
* </p>
|
||||
*
|
||||
* @author Lingma
|
||||
* @since 2025-10-10
|
||||
*/
|
||||
public interface RichTextLogService extends IService<RichTextLog> {
|
||||
|
||||
/**
|
||||
* 记录富文本图片替换日志
|
||||
* @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);
|
||||
|
||||
}
|
||||
@ -979,10 +979,7 @@ public class CommonServiceImpl implements CommonService {
|
||||
String blobField = null;
|
||||
List<DataPicklist> 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<String, Object> 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<DataPicklist> 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<String, Object> 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 = "";
|
||||
|
||||
@ -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<String> 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<DataObject> qw = new QueryWrapper<>();
|
||||
if (StringUtils.isNotBlank(param.getApi())) {
|
||||
List<String> apis = DataUtil.toIdList(param.getApi());
|
||||
qw.in("name", apis);
|
||||
}
|
||||
qw.eq("data_work", true);
|
||||
List<DataObject> objectList = dataObjectService.list(qw);
|
||||
|
||||
if (StringUtils.isBlank(field)) {
|
||||
return new ReturnT<>(500, "field不能为空!");
|
||||
}
|
||||
for (DataObject dataObject : objectList) {
|
||||
String api = dataObject.getName();
|
||||
QueryWrapper<DataField> qw1 = new QueryWrapper<>();
|
||||
qw1.eq("api", api);
|
||||
qw1.eq("isHtmlFormatted", true);
|
||||
List<DataField> fields = dataFieldService.list(qw1);
|
||||
|
||||
for (DataField dataField : fields) {
|
||||
String field = dataField.getField();
|
||||
String sqlCondition = "new_id is not null and " + field + " like '%<img%refid%'";
|
||||
|
||||
String beginDateStr = null;
|
||||
String endDateStr = null;
|
||||
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");
|
||||
}
|
||||
if (StringUtils.isNotEmpty(beginDateStr) && StringUtils.isNotEmpty(endDateStr)) {
|
||||
sqlCondition += " and CreatedDate >= '" + beginDateStr + "' and CreatedDate < '" + endDateStr + "'";
|
||||
}
|
||||
|
||||
String sqlCondition = "new_id is not null and " + field + " like '%<img%refid%'";
|
||||
if (StringUtils.isNotEmpty(beginDateStr) && StringUtils.isNotEmpty(endDateStr)) {
|
||||
sqlCondition += " and CreatedDate >= '" + beginDateStr + "' and CreatedDate < '" + endDateStr + "'";
|
||||
}
|
||||
// 获取记录总数
|
||||
Integer count = customMapper.countBySQL(api, "where " + sqlCondition);
|
||||
log.info("对象 {} 字段 {} 需要处理的富文本记录总数: {}", api, field, count);
|
||||
|
||||
// 获取记录总数
|
||||
Integer count = customMapper.countBySQL(api, "where " + sqlCondition);
|
||||
log.info("需要处理的富文本记录总数: {}", count);
|
||||
if (count == 0) {
|
||||
log.info("对象 {} 字段 {} 没有需要处理的富文本记录", api, field);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count == 0) {
|
||||
log.info("没有需要处理的富文本记录");
|
||||
return ReturnT.SUCCESS;
|
||||
}
|
||||
// 分批处理,每批200条记录
|
||||
int pageSize = 200;
|
||||
int totalPages = (count + pageSize - 1) / pageSize;
|
||||
|
||||
// 分批处理,每批200条记录
|
||||
int pageSize = 200;
|
||||
int totalPages = (count + pageSize - 1) / pageSize;
|
||||
for (int page = 0; page < totalPages; page++) {
|
||||
List<Map<String, Object>> records = customMapper.list("Id, new_id, " + field, api,
|
||||
sqlCondition + " order by Id limit " + (page * pageSize) + "," + pageSize);
|
||||
|
||||
for (int page = 0; page < totalPages; page++) {
|
||||
List<Map<String, Object>> 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<String, Object> record : records) {
|
||||
try {
|
||||
String sourceId = (String) record.get("Id");
|
||||
String targetId = (String) record.get("new_id");
|
||||
String richTextContent = (String) record.get(field);
|
||||
|
||||
for (Map<String, Object> 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 {
|
||||
// // 使用正则表达式匹配<img>标签
|
||||
// Pattern imgPattern = Pattern.compile("<img[^>]*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<String, String> 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<String, String> 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 {
|
||||
// 使用正则表达式匹配<img>标签
|
||||
Pattern imgPattern = Pattern.compile("<img[^>]*refid=\\\"([^\"]*)\\\"[^>]*/?>", Pattern.CASE_INSENSITIVE);
|
||||
// 匹配富文本中包含refid的img标签
|
||||
Pattern imgPattern = Pattern.compile("<img[^>]*refid=\"([^\"]+)\"[^>]*>", Pattern.CASE_INSENSITIVE);
|
||||
Matcher matcher = imgPattern.matcher(richTextContent);
|
||||
|
||||
StringBuffer result = new StringBuffer();
|
||||
boolean hasReplacements = false;
|
||||
|
||||
|
||||
StringBuffer updatedContent = new StringBuffer();
|
||||
Map<String, String> 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<String, String> 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<String, String> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 富文本替换记录表 服务实现类
|
||||
* </p>
|
||||
*
|
||||
* @author Lingma
|
||||
* @since 2025-10-10
|
||||
*/
|
||||
@Service
|
||||
public class RichTextLogServiceImpl extends ServiceImpl<RichTextLogMapper, RichTextLog> 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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user