fix: 文章重命名与删除对双链图表的影响

This commit is contained in:
xiaozzzi 2024-01-15 00:17:39 +08:00
parent 479df5bc09
commit 86a07075b5
13 changed files with 194 additions and 22 deletions

View File

@ -139,6 +139,7 @@ public class ArticleController {
public R<Long> insert(@Validated @RequestBody ArticleUpdReq req) { public R<Long> insert(@Validated @RequestBody ArticleUpdReq req) {
ArticleEntity article = req.to(ArticleEntity.class); ArticleEntity article = req.to(ArticleEntity.class);
article.setTags(DocUtil.toTagStr(req.getTags())); article.setTags(DocUtil.toTagStr(req.getTags()));
article.setUserId(AuthContext.getUserId());
return R.ok(baseService.update(article)); return R.ok(baseService.update(article));
} }
@ -166,6 +167,7 @@ public class ArticleController {
@PostMapping("/upd/name") @PostMapping("/upd/name")
public R<?> updName(@Validated @RequestBody ArticleUpdNameReq name) { public R<?> updName(@Validated @RequestBody ArticleUpdNameReq name) {
ArticleEntity article = name.to(ArticleEntity.class); ArticleEntity article = name.to(ArticleEntity.class);
article.setUserId(AuthContext.getUserId());
baseService.update(article); baseService.update(article);
return R.ok(); return R.ok();
} }
@ -187,6 +189,7 @@ public class ArticleController {
} }
ArticleEntity article = req.to(ArticleEntity.class); ArticleEntity article = req.to(ArticleEntity.class);
article.setTags(DocUtil.toTagStr(tags)); article.setTags(DocUtil.toTagStr(tags));
article.setUserId(AuthContext.getUserId());
baseService.update(article); baseService.update(article);
return R.ok(tags); return R.ok(tags);
} }
@ -207,7 +210,9 @@ public class ArticleController {
*/ */
@PostMapping("/star") @PostMapping("/star")
public R<Long> star(@Validated @RequestBody ArticleStarReq req) { public R<Long> star(@Validated @RequestBody ArticleStarReq req) {
return R.ok(baseService.update(req.to(ArticleEntity.class))); ArticleEntity article = req.to(ArticleEntity.class);
article.setUserId(AuthContext.getUserId());
return R.ok(baseService.update(article));
} }
/** /**

View File

@ -166,6 +166,7 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
public Long update(ArticleEntity req) { public Long update(ArticleEntity req) {
XzException404.throwBy(req.getId() == null, "ID不得为空"); XzException404.throwBy(req.getId() == null, "ID不得为空");
baseMapper.updById(req); baseMapper.updById(req);
referenceService.updateInnerName(req.getUserId(), req.getId(), req.getName());
return req.getId(); return req.getId();
} }
@ -212,8 +213,10 @@ public class ArticleService extends ServiceImpl<ArticleMapper, ArticleEntity> {
baseMapper.deleteById(id); baseMapper.deleteById(id);
// 删除公开文章 // 删除公开文章
openMapper.delById(id); openMapper.delById(id);
// 删除引用 // 删除主动引用
referenceService.delete(id); referenceService.delete(id);
// 将被动引用中的名称修改为未知
referenceService.updateToUnknown(userId, id);
// 删除访问记录 // 删除访问记录
viewService.delete(id); viewService.delete(id);
} }

View File

@ -16,8 +16,8 @@ import java.util.List;
* 文章回收站 [A#Recycle] * 文章回收站 [A#Recycle]
* *
* @author xzzz * @author xzzz
* @since 1.10.0
* @order 7 * @order 7
* @since 1.10.0
*/ */
@Slf4j @Slf4j
@RestController @RestController
@ -44,7 +44,7 @@ public class ArticleRecycleController {
*/ */
@PostMapping("/restore") @PostMapping("/restore")
public R<?> restore(@Validated @RequestBody ArticleRecycleRestoreReq req) { public R<?> restore(@Validated @RequestBody ArticleRecycleRestoreReq req) {
baseService.restore(req.getId()); baseService.restore(AuthContext.getUserId(), req.getId());
return R.ok(); return R.ok();
} }
} }

View File

@ -9,6 +9,7 @@ import com.blossom.backend.base.param.pojo.ParamEntity;
import com.blossom.backend.base.search.EnableIndex; import com.blossom.backend.base.search.EnableIndex;
import com.blossom.backend.base.search.message.IndexMsgTypeEnum; import com.blossom.backend.base.search.message.IndexMsgTypeEnum;
import com.blossom.backend.server.article.recycle.pojo.ArticleRecycleEntity; import com.blossom.backend.server.article.recycle.pojo.ArticleRecycleEntity;
import com.blossom.backend.server.article.reference.ArticleReferenceService;
import com.blossom.backend.server.folder.FolderService; import com.blossom.backend.server.folder.FolderService;
import com.blossom.backend.server.folder.pojo.FolderEntity; import com.blossom.backend.server.folder.pojo.FolderEntity;
import com.blossom.common.base.util.DateUtils; import com.blossom.common.base.util.DateUtils;
@ -34,6 +35,7 @@ public class ArticleRecycleService extends ServiceImpl<ArticleRecycleMapper, Art
private final FolderService folderService; private final FolderService folderService;
private final ParamService paramService; private final ParamService paramService;
private final ArticleReferenceService referenceService;
/** /**
@ -52,7 +54,7 @@ public class ArticleRecycleService extends ServiceImpl<ArticleRecycleMapper, Art
*/ */
@EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#id") @EnableIndex(type = IndexMsgTypeEnum.ADD, id = "#id")
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void restore(Long id) { public void restore(Long userId, Long id) {
ArticleRecycleEntity article = baseMapper.selectById(id); ArticleRecycleEntity article = baseMapper.selectById(id);
FolderEntity folder = folderService.selectById(article.getPid()); FolderEntity folder = folderService.selectById(article.getPid());
if (ObjUtil.isNull(folder)) { if (ObjUtil.isNull(folder)) {
@ -61,10 +63,13 @@ public class ArticleRecycleService extends ServiceImpl<ArticleRecycleMapper, Art
baseMapper.restore(id, folder.getId()); baseMapper.restore(id, folder.getId());
} }
baseMapper.deleteById(id); baseMapper.deleteById(id);
// 将被动引用中的未知文章名修改为正常文章名
referenceService.updateToKnown(userId, id, article.getName());
} }
/** /**
* 每天凌晨4点执行 * 每天凌晨4点执行
*
* @Scheduled(cron = "0 0/1 * * * ?") * @Scheduled(cron = "0 0/1 * * * ?")
*/ */
@Scheduled(cron = "0 0 04 * * ?") @Scheduled(cron = "0 0 04 * * ?")

View File

@ -0,0 +1,30 @@
package com.blossom.backend.server.article.reference;
import lombok.Getter;
public enum ArticleReferenceEnum {
/**
* 图片
*/
FILE(10),
/**
* 内部文章
*/
INNER(11),
/**
* 未知内部文章
*/
INNER_UNKNOWN(12),
/**
* 外部文章
*/
OUTSIDE(21);
@Getter
private final Integer type;
ArticleReferenceEnum(Integer type) {
this.type = type;
}
}

View File

@ -40,4 +40,25 @@ public interface ArticleReferenceMapper extends BaseMapper<ArticleReferenceEntit
* @param articleId 文章ID * @param articleId 文章ID
*/ */
List<ArticleReferenceEntity> listGraph(@Param("inner") Boolean inner, @Param("userId") Long userId, @Param("articleId") Long articleId); List<ArticleReferenceEntity> listGraph(@Param("inner") Boolean inner, @Param("userId") Long userId, @Param("articleId") Long articleId);
/**
* 修改 sourceName
*/
void updateSourceName(@Param("userId") Long userId, @Param("sourceId") Long sourceId, @Param("sourceName") String sourceName);
/**
* 修改 targetName
*/
void updateTargetName(@Param("userId") Long userId, @Param("targetId") Long targetId, @Param("targetName") String targetName);
/**
* 被引用的文章修改为未知
*/
void updateToUnknown(@Param("userId") Long userId, @Param("targetId") Long targetId, @Param("targetName") String targetName);
/**
* 被引用的文章修改为未知
*/
void updateToKnown(@Param("userId") Long userId, @Param("targetId") Long targetId, @Param("targetName") String targetName);
} }

View File

@ -1,6 +1,7 @@
package com.blossom.backend.server.article.reference; package com.blossom.backend.server.article.reference;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.blossom.backend.server.article.reference.pojo.ArticleReferenceEntity; import com.blossom.backend.server.article.reference.pojo.ArticleReferenceEntity;
@ -29,11 +30,15 @@ public class ArticleReferenceService extends ServiceImpl<ArticleReferenceMapper,
/** /**
* 文章引用记录 * 文章引用记录
*
* @param userId 用户ID
* @param sourceId 引用源
* @param sourceName 引用源名称
* @param references 目标
*/ */
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void bind(Long userId, Long sourceId, String sourceName, List<ArticleReferenceReq> references) { public void bind(Long userId, Long sourceId, String sourceName, List<ArticleReferenceReq> references) {
delete(sourceId); delete(sourceId);
// 没有图片, 则不保存 // 没有图片, 则不保存
if (CollUtil.isEmpty(references)) { if (CollUtil.isEmpty(references)) {
return; return;
@ -47,11 +52,51 @@ public class ArticleReferenceService extends ServiceImpl<ArticleReferenceMapper,
baseMapper.insertList(refs); baseMapper.insertList(refs);
} }
/**
* 将文章修改为内部未知文章
*
* @param userId 用户ID
* @param targetId 目标文章ID
*/
@Transactional(rollbackFor = Exception.class)
public void updateToUnknown(Long userId, Long targetId) {
baseMapper.updateToUnknown(userId, targetId, "未知文章-" + targetId);
}
/**
* 将文章引用修改为内部具名文章
*
* @param userId 用户ID
* @param targetId 目标文章ID
* @param name 文章名称
*/
@Transactional(rollbackFor = Exception.class)
public void updateToKnown(Long userId, Long targetId, String name) {
baseMapper.updateToKnown(userId, targetId, name);
}
/**
* 内部链接修改名称时, 同时修改双链中的名称
*
* @param articleId ID
* @param name 名称
*/
@Transactional(rollbackFor = Exception.class)
public void updateInnerName(Long userId, Long articleId, String name) {
if (articleId <= 0 || StrUtil.isBlank(name)) {
return;
}
baseMapper.updateSourceName(userId, articleId, name);
baseMapper.updateTargetName(userId, articleId, name);
}
/** /**
* 删除引用 * 删除引用
* *
* @param articleId 文章ID * @param articleId 文章ID
*/ */
@Transactional(rollbackFor = Exception.class)
public void delete(Long articleId) { public void delete(Long articleId) {
LambdaQueryWrapper<ArticleReferenceEntity> where = new LambdaQueryWrapper<>(); LambdaQueryWrapper<ArticleReferenceEntity> where = new LambdaQueryWrapper<>();
where.eq(ArticleReferenceEntity::getSourceId, articleId); where.eq(ArticleReferenceEntity::getSourceId, articleId);
@ -100,14 +145,15 @@ public class ArticleReferenceService extends ServiceImpl<ArticleReferenceMapper,
Set<Node> nodes = new HashSet<>(); Set<Node> nodes = new HashSet<>();
source.forEach((id, list) -> { source.forEach((id, list) -> {
Node node = new Node(list.get(0).getSourceName(), 11); Node node = new Node(list.get(0).getSourceName(), ArticleReferenceEnum.INNER.getType());
node.setInner(true); node.setInner(true);
node.setArtId(id); node.setArtId(id);
nodes.add(node); nodes.add(node);
}); });
target.forEach((name, list) -> { target.forEach((name, list) -> {
Node node = new Node(list.get(0).getTargetName(), list.get(0).getType()); final Integer type = list.get(0).getType();
if (list.get(0).getType().equals(11)) { Node node = new Node(list.get(0).getTargetName(), type);
if (type.equals(ArticleReferenceEnum.INNER.getType()) || type.equals(ArticleReferenceEnum.INNER_UNKNOWN.getType())) {
node.setInner(true); node.setInner(true);
node.setArtId(list.get(0).getTargetId()); node.setArtId(list.get(0).getTargetId());
} else { } else {

View File

@ -78,6 +78,7 @@
upd_time = now() upd_time = now()
</set> </set>
where id = #{id} where id = #{id}
and user_id = #{userId}
</update> </update>
<!-- 根据ID修改 --> <!-- 根据ID修改 -->

View File

@ -48,14 +48,43 @@
from blossom_article_reference from blossom_article_reference
where user_id = #{userId} where user_id = #{userId}
<if test="inner == true"> <if test="inner == true">
and type = 11 and type in(11,12)
</if> </if>
<if test="inner == false"> <if test="inner == false">
and type in (11,21) and type in (11,12,21)
</if> </if>
<if test="articleId != null"> <if test="articleId != null">
and (source_id = #{articleId} or target_id = #{articleId}) and (source_id = #{articleId} or target_id = #{articleId})
</if> </if>
</select> </select>
<update id="updateSourceName">
update blossom_article_reference
set source_name = #{sourceName}
where source_id = #{sourceId}
and user_id = #{userId}
</update>
<update id="updateTargetName">
update blossom_article_reference
set target_name = #{targetName}
where target_Id = #{targetId}
and user_id = #{userId}
</update>
<update id="updateToUnknown">
update blossom_article_reference
set target_name = #{targetName},
type = 12
where target_Id = #{targetId}
and user_id = #{userId}
</update>
<update id="updateToKnown">
update blossom_article_reference
set target_name = #{targetName},
type = 11
where target_Id = #{targetId}
and user_id = #{userId}
</update>
</mapper> </mapper>

View File

@ -68,8 +68,9 @@ let stat = ref({
outside: 0 outside: 0
}) })
let inside = { itemStyle: {}, label: {} } let inside: any = { itemStyle: {}, label: {} }
let outside = { itemStyle: {}, label: {} } let insideUnknown: any = { itemStyle: {}, label: {} }
let outside: any = { itemStyle: {}, label: {} }
const changeStyle = () => { const changeStyle = () => {
let primaryColor = getPrimaryColor() let primaryColor = getPrimaryColor()
// //
@ -85,6 +86,17 @@ const changeStyle = () => {
textBorderWidth: 2 textBorderWidth: 2
} }
} }
insideUnknown = {
itemStyle: {
color: isDark.value ? '#7B0000' : '#EB6969'
},
label: {
fontSize: 13,
color: isDark.value ? '#BABABA' : '#030303',
textBorderColor: isDark.value ? '#7B0000' : '#EB6969',
textBorderWidth: 1
}
}
outside = { outside = {
itemStyle: { itemStyle: {
color: isDark.value ? '#7B5E00' : '#FDC81A87' color: isDark.value ? '#7B5E00' : '#FDC81A87'
@ -116,6 +128,9 @@ const getArticleRefList = (onlyInner: boolean) => {
node.itemStyle = inside.itemStyle node.itemStyle = inside.itemStyle
node.label = inside.label node.label = inside.label
stat.value.inside += 1 stat.value.inside += 1
} else if (node.artType === 12) {
node.itemStyle = insideUnknown.itemStyle
node.label = insideUnknown.label
} else if (node.artType == 21) { } else if (node.artType == 21) {
node.itemStyle = outside.itemStyle node.itemStyle = outside.itemStyle
node.label = outside.label node.label = outside.label
@ -172,10 +187,19 @@ const renderChart = () => {
userStore.userinfo.userParams.WEB_ARTICLE_URL + params.data.artId userStore.userinfo.userParams.WEB_ARTICLE_URL + params.data.artId
}</a></div>` }</a></div>`
} }
return `<div class="chart-graph-article-ref-tooltip"> console.log(params.data)
let type = ''
if (params.data.artType === 11) {
type = `<div>类型: 内部文章</div>`
} else if (params.data.artType === 12) {
type = `<div style="color:${insideUnknown.itemStyle.color}">类型: 未知文章, 可能是文章ID错误或已被删除</div>`
} else if (params.data.artType === 21) {
type = `<div>类型: 外网文章</div>`
}
return `<div class="chart-graph-article-ref-tooltip" style="border:1px solid ${params.data.itemStyle.color}">
<div class="title">${params.data.name}</div> <div class="title">${params.data.name}</div>
<div class="content"> <div class="content">
<div>类型: ${params.data.inner ? '内部文章' : '外网文章'}</div> ${type}
${url} ${url}
</div> </div>
</div>` </div>`
@ -250,7 +274,7 @@ const renderChart = () => {
// edgeLabel: { show: false }, // edgeLabel: { show: false },
}, },
blur: { blur: {
itemStyle: { opacity: 0.1 }, // itemStyle: { opacity: 0.1 },
lineStyle: { opacity: 0.1 }, lineStyle: { opacity: 0.1 },
label: { show: false }, label: { show: false },
edgeLabel: { show: false } edgeLabel: { show: false }
@ -377,10 +401,10 @@ onUnmounted(() => {
white-space: normal; white-space: normal;
background-color: var(--bl-html-color); background-color: var(--bl-html-color);
border-radius: 4px; border-radius: 4px;
border: 1px solid var(--el-color-primary-light-5); color: var(--bl-text-color);
.title { .title {
@include font(15px, 300); @include font(15px, 500);
border-bottom: 1px solid var(--el-color-primary-light-5); border-bottom: 1px solid var(--el-color-primary-light-5);
padding: 10px; padding: 10px;
overflow: hidden; overflow: hidden;

View File

@ -573,11 +573,14 @@ const syncDoc = () => {
const delDoc = () => { const delDoc = () => {
let type = curDoc.value.ty === 3 ? '文章' : '文件夹' let type = curDoc.value.ty === 3 ? '文章' : '文件夹'
ElMessageBox.confirm( ElMessageBox.confirm(
`是否确定删除${type}: <span style="color:#C02B2B;text-decoration: underline;">${curDoc.value.n}</span>?删除后的文章可在回收站中查看。`, `<strong>注意:</strong><br/>
1. 公开访问记录将永久删除<br/>
2. 双链引用将永久删除还原后续重新编辑才可再次生成<br/>
是否继续删除${type}: <span style="color:#C02B2B;text-decoration: underline;">${curDoc.value.n}</span>`,
{ {
confirmButtonText: '确定删除', confirmButtonText: '确定删除',
cancelButtonText: '我再想想', cancelButtonText: '我再想想',
type: 'info', // type: 'warning',
draggable: true, draggable: true,
dangerouslyUseHTMLString: true dangerouslyUseHTMLString: true
} }

View File

@ -41,7 +41,8 @@ export interface ArticleReference {
* *
* 10 : picture * 10 : picture
* 11 : inner article * 11 : inner article
* 12 : unknown inner article
* 21 : public article * 21 : public article
*/ */
type: 10 | 11 | 21 type: 10 | 11 | 12 | 21
} }

View File

@ -413,14 +413,18 @@ export const renderLink = (href: string | null, title: string | null, text: stri
} }
// 从文章列表中获取文章, 如果找到则认为是内部引用, 否则即使是内部引用格式, 也认为是个外部文章. // 从文章列表中获取文章, 如果找到则认为是内部引用, 否则即使是内部引用格式, 也认为是个外部文章.
// 内部引用不会使用 Markdown 中的链接名, 而是用内部文章名
let article = getDocById(articleId.toString(), docTrees) let article = getDocById(articleId.toString(), docTrees)
if (article != undefined) { if (article != undefined) {
ref.targetId = article.i ref.targetId = article.i
ref.targetName = article.n ref.targetName = article.n
ref.type = 11 ref.type = 11
} else {
ref.targetId = articleId.toString()
ref.targetName = '未知文章-' + articleId.toString()
ref.type = 12
} }
// class="inner-link bl-tip bl-tip-bottom" data-tip="双链引用: 《${text}》"
link = `<a target="_blank" href=${href} class="inner-link" link = `<a target="_blank" href=${href} class="inner-link"
onclick="onHtmlEventDispatch(this,'',event,'showArticleReferenceView','${ref.targetId}')">${text}</a>` onclick="onHtmlEventDispatch(this,'',event,'showArticleReferenceView','${ref.targetId}')">${text}</a>`
} else { } else {