『博客开发日记-后台』之更新通知接口的实现

本文最后更新于 2026年5月29日 下午

更新通知接口的实现


更新通知接口的需求

其实通知和写文章差不多

检查通知是否存在

处理通知内容中的图片

提取旧内容中的图片URL

处理新内容中的临时图片

提取新内容中的图片URL

先更新通知,避免先删 OSS 再更新数据库

如果标题发生变化,同步更新文件表里的文件来源

数据库更新成功后,再删除不再使用的内容图片


代码实现

在 NoticeController 中添加接口

1
2
3
4
5
6
7
8
9
10
 @PutMapping("/{id}")
@PreAuthorize("@ps.hasPermission('sys:notice:update')")
@SystemLog(businessName = "修改通知")
@ApiOperation(value = "修改通知接口", notes = "修改通知", response = String.class)
@ApiImplicitParam(name = "id", value = "通知ID", dataType = "long", paramType = "path", required = true)
public ResponseResult updateNotice(@PathVariable Long id, @Valid @RequestBody UpdateNoticeDto updateNoticeDto)
{
return sysNoticeService.updateNotice(id, updateNoticeDto);
}


创建 UpdateNoticeDto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 更新通知请求DTO
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(description = "更新通知请求对象")
public class UpdateNoticeDto
{
@NotBlank(message = "通知标题不能为空")
@ApiModelProperty(value = "通知标题", required = true, example = "测试通知标题")
private String title;

@NotBlank(message = "通知内容不能为空")
@ApiModelProperty(value = "通知内容(HTML格式/md格式)", required = true, example = "你好")
private String content;

@NotNull(message = "通知类型不能为空")
@ApiModelProperty(value = "通知类型(关联字典编码:notice_type)", required = true, example = "1")
private Integer type;

@NotNull(message = "通知等级不能为空")
@ApiModelProperty(value = "通知等级(LOW/MEDIUM/HIGH)(字典code:notice_level)", example = "H")
private String level;

@Pattern(regexp = "^$|^[012]$", message = "状态只能为0,1或2")
@ApiModelProperty(value = "状态(0: 未发布, 1: 已发布, 2: 已撤回)", example = "1")
private String status;

@ApiModelProperty(value = "目标人ID集合(多个使用英文逗号,分割)", example = "['1','2']")
private List<Long> targetUserIds;

@NotNull(message = "目标类型")
@Pattern(regexp = "^$|^[12]$", message = "目标类型只能为1或2")
@ApiModelProperty(value = "目标类型(1: 全体, 2: 指定)", example = "1")
private String targetType;
}

在 NoticeServiceImpl 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//更新通知
@Override
@Transactional
public ResponseResult updateNotice(Long id, UpdateNoticeDto updateNoticeDto)
{
//检查通知是否存在
SysNotice oldNotice = sysNoticeService.getById(id);
if (oldNotice == null) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "该通知不存在!");
}

//处理通知内容中的图片
String content = updateNoticeDto.getContent();
List<String> oldContentImages = new ArrayList<>();
List<String> newContentImages = new ArrayList<>();

//提取旧内容中的图片URL
if (StringUtils.hasText(oldNotice.getContent())) {
oldContentImages = ImageUrlUtils.extractImageUrls(oldNotice.getContent());
}

//处理新内容中的临时图片
if (ImageUrlUtils.containsTempImages(content)) {
content = processContentImagesAndSaveRecords(content, updateNoticeDto.getTitle());
updateNoticeDto.setContent(content);
}

//提取新内容中的图片URL
if (StringUtils.hasText(content)) {
newContentImages = ImageUrlUtils.extractImageUrls(content);
}

//先更新通知,避免先删 OSS 再更新数据库
SysNotice notice = BeanCopyUtils.copyBean(updateNoticeDto, SysNotice.class);
notice.setId(id);
boolean updated = sysNoticeService.updateById(notice);
if (!updated) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "更新通知失败");
}

//如果标题发生变化,同步更新文件表里的文件来源
if (StringUtils.hasText(oldNotice.getTitle()) && StringUtils.hasText(updateNoticeDto.getTitle())
&& !oldNotice.getTitle().equals(updateNoticeDto.getTitle())) {
syncFileSourceByTitle(oldNotice.getTitle(), updateNoticeDto.getTitle());
}

//数据库更新成功后,再删除不再使用的内容图片
for (String oldImageUrl : oldContentImages) {
if (!newContentImages.contains(oldImageUrl)) {
ossFileService.deleteFileByUrl(oldImageUrl);
deleteFileRecordByUrl(oldImageUrl);
log.info("删除旧的内容图片并同步文件表: {}", oldImageUrl);
}
}

return ResponseResult.okResult();
}


//处理通知内容中的图片并将其写入文件表
private String processContentImagesAndSaveRecords(String content, String postTitle)
{
List<String> tempImageUrls = ImageUrlUtils.extractTempImageUrls(content);
String result = content;

//因为同一篇通知里可能重复引用同一张临时图片,所一这里先去重,避免重复转正和重复删除临时文件
List<String> distinctTempImageUrls = tempImageUrls.stream().distinct().toList();

for (String tempUrl : distinctTempImageUrls) {
UploadFileMetaVo uploadFileMetaVo = ossFileService.moveTempToFormal(tempUrl);

//判断图片是否保存(从oss中的 临时 > 正式)成功
if (uploadFileMetaVo == null || !StringUtils.hasText(uploadFileMetaVo.getUrl())) {
throw new RuntimeException("图片保存失败,无法保存通知内容中的图片记录: " + tempUrl);
}

result = ImageUrlUtils.replaceImageUrl(result, tempUrl, uploadFileMetaVo.getUrl());
if (StringUtils.hasText(uploadFileMetaVo.getFilePath())) {
String fileSource = "通知《" + postTitle + "》中的内容图片";
saveFileRecord(uploadFileMetaVo.getName(),
uploadFileMetaVo.getFilePath(),
uploadFileMetaVo.getUrl(),
uploadFileMetaVo.getSize(),
uploadFileMetaVo.getMimeType(),
fileSource);
}
}

return result;
}

//处理删除文件
private void deleteFileRecordByUrl(String url)
{
if (!StringUtils.hasText(url)) {
return;
}

LambdaQueryWrapper<SysFile> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysFile::getUrl, url);
sysFileService.remove(queryWrapper);
}

//标题变更时同步更新文件来源
private void syncFileSourceByTitle(String oldTitle, String newTitle)
{
String oldPostPrefix = "通知《" + oldTitle + "》";
String newPostPrefix = "通知《" + newTitle + "》";

List<SysFile> fileList = sysFileService.list(new LambdaQueryWrapper<SysFile>()
.like(SysFile::getFileSource, oldPostPrefix));

if (fileList.isEmpty()) {
return;
}

for (SysFile sysFile : fileList) {
String fileSource = sysFile.getFileSource();
if (StringUtils.hasText(fileSource)) {
sysFile.setFileSource(fileSource.replace(oldPostPrefix, newPostPrefix));
}
}

sysFileService.updateBatchById(fileList);
}





PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记-后台』之更新通知接口的实现
http://example.com/2026/05/29/『博客开发日记-后台』之更新通知接口的实现/
作者
云梦泽
发布于
2026年5月29日
更新于
2026年5月29日
许可协议