Skip to content

Commit a04d0c4

Browse files
yyyCodeclaude
andcommitted
refactor: improve code quality across frontend and backend
- Remove unused @element-plus/icons-vue dependency (-1 dep) - Delete deprecated ConsoleArticlesView.vue (299 lines, replaced by ConsoleArticleManageView) - Reduce style.css from 277 to 10 lines (keep only essential rendering base styles) - Refactor media.js to reuse http.js request() (148 lines → 33 lines, -115 lines) - Extract TreeUtils: eliminate ~150 lines of duplicated tree logic between CategoryService and MediaFolderService (indexById, buildPathNames, collectDescendants, isDescendant) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 68bb5bd commit a04d0c4

8 files changed

Lines changed: 126 additions & 781 deletions

File tree

OpenBlog-business/src/main/java/com/yqz/openblog/category/service/CategoryService.java

Lines changed: 12 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,15 @@
77
import com.yqz.openblog.category.entity.ArticleCategory;
88
import com.yqz.openblog.category.repo.ArticleCategoryRepository;
99
import com.yqz.openblog.common.BizException;
10+
import com.yqz.openblog.common.TreeUtils;
1011
import org.springframework.stereotype.Service;
1112
import org.springframework.transaction.annotation.Transactional;
1213

1314
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
1415
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
1516
import com.yqz.openblog.article.entity.Article;
1617

17-
import java.util.ArrayList;
18-
import java.util.Collections;
19-
import java.util.HashMap;
20-
import java.util.HashSet;
21-
import java.util.LinkedHashSet;
22-
import java.util.List;
23-
import java.util.Map;
24-
import java.util.Set;
18+
import java.util.*;
2519

2620
@Service
2721
public class CategoryService {
@@ -107,37 +101,24 @@ public CategoryMeta resolveMeta(Long categoryId) {
107101
return CategoryMeta.empty();
108102
}
109103
List<String> path = buildPathNames(categoryId, byId);
110-
String name = c.getName();
111-
return new CategoryMeta(categoryId, name, path);
104+
return new CategoryMeta(categoryId, c.getName(), path);
112105
}
113106

114107
public Set<Long> collectSelfAndDescendantIds(Long categoryId) {
115108
if (categoryId == null) {
116109
return Collections.emptySet();
117110
}
118111
List<ArticleCategory> all = categoryRepository.findAllByOrderBySortOrderAscIdAsc();
119-
Map<Long, List<Long>> childrenMap = new HashMap<>();
120-
for (ArticleCategory c : all) {
121-
if (c.getParentId() == null) {
122-
continue;
123-
}
124-
childrenMap.computeIfAbsent(c.getParentId(), k -> new ArrayList<>()).add(c.getId());
125-
}
112+
List<Long> ids = all.stream().map(ArticleCategory::getId).toList();
113+
Map<Long, List<Long>> childrenMap = TreeUtils.buildChildrenMap(ids, id -> {
114+
ArticleCategory cat = all.stream().filter(c -> c.getId().equals(id)).findFirst().orElse(null);
115+
return cat != null ? cat.getParentId() : null;
116+
});
126117
Set<Long> out = new LinkedHashSet<>();
127-
collectDescendants(categoryId, childrenMap, out);
118+
TreeUtils.collectDescendants(categoryId, childrenMap, out);
128119
return out;
129120
}
130121

131-
private void collectDescendants(Long id, Map<Long, List<Long>> childrenMap, Set<Long> out) {
132-
if (id == null || !out.add(id)) {
133-
return;
134-
}
135-
List<Long> children = childrenMap.getOrDefault(id, List.of());
136-
for (Long childId : children) {
137-
collectDescendants(childId, childrenMap, out);
138-
}
139-
}
140-
141122
private void apply(ArticleCategory c, CategoryUpsertRequest req, Long excludeId) {
142123
String name = req.getName() == null ? "" : req.getName().trim();
143124
if (name.isEmpty()) {
@@ -162,15 +143,7 @@ private void apply(ArticleCategory c, CategoryUpsertRequest req, Long excludeId)
162143

163144
private boolean isDescendant(Long ancestorId, Long nodeId) {
164145
Map<Long, ArticleCategory> byId = indexById(categoryRepository.findAllByOrderBySortOrderAscIdAsc());
165-
Long current = nodeId;
166-
while (current != null) {
167-
if (current.equals(ancestorId)) {
168-
return true;
169-
}
170-
ArticleCategory c = byId.get(current);
171-
current = c == null ? null : c.getParentId();
172-
}
173-
return false;
146+
return TreeUtils.isDescendant(ancestorId, nodeId, byId, ArticleCategory::getParentId);
174147
}
175148

176149
private List<CategoryTreeNodeResponse> buildTree(List<ArticleCategory> all) {
@@ -201,26 +174,11 @@ private List<CategoryTreeNodeResponse> buildTree(List<ArticleCategory> all) {
201174
}
202175

203176
private Map<Long, ArticleCategory> indexById(List<ArticleCategory> all) {
204-
Map<Long, ArticleCategory> map = new HashMap<>();
205-
for (ArticleCategory c : all) {
206-
map.put(c.getId(), c);
207-
}
208-
return map;
177+
return TreeUtils.indexById(all, ArticleCategory::getId);
209178
}
210179

211180
private List<String> buildPathNames(Long categoryId, Map<Long, ArticleCategory> byId) {
212-
List<String> path = new ArrayList<>();
213-
Set<Long> visited = new HashSet<>();
214-
Long current = categoryId;
215-
while (current != null && visited.add(current)) {
216-
ArticleCategory c = byId.get(current);
217-
if (c == null) {
218-
break;
219-
}
220-
path.add(0, c.getName());
221-
current = c.getParentId();
222-
}
223-
return path;
181+
return TreeUtils.buildPathNames(categoryId, byId, ArticleCategory::getParentId, ArticleCategory::getName);
224182
}
225183

226184
private CategoryFlatItemResponse toFlatItem(ArticleCategory c, Map<Long, ArticleCategory> byId) {
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package com.yqz.openblog.common;
2+
3+
import java.util.*;
4+
import java.util.function.Function;
5+
6+
/**
7+
* 树形结构通用工具方法。CategoryService 和 MediaFolderService 共用。
8+
*/
9+
public final class TreeUtils {
10+
11+
private TreeUtils() {
12+
}
13+
14+
/**
15+
* 按 ID 建立索引 Map。
16+
*/
17+
public static <T> Map<Long, T> indexById(List<T> list, Function<T, Long> idGetter) {
18+
Map<Long, T> map = new HashMap<>();
19+
for (T item : list) {
20+
map.put(idGetter.apply(item), item);
21+
}
22+
return map;
23+
}
24+
25+
/**
26+
* 从指定节点向上追溯,构建路径名列表(从根到当前节点)。
27+
*/
28+
public static <T> List<String> buildPathNames(Long nodeId,
29+
Map<Long, T> byId,
30+
Function<T, Long> parentIdGetter,
31+
Function<T, String> nameGetter) {
32+
List<String> path = new ArrayList<>();
33+
Set<Long> visited = new HashSet<>();
34+
Long current = nodeId;
35+
while (current != null && visited.add(current)) {
36+
T node = byId.get(current);
37+
if (node == null) {
38+
break;
39+
}
40+
path.add(0, nameGetter.apply(node));
41+
current = parentIdGetter.apply(node);
42+
}
43+
return path;
44+
}
45+
46+
/**
47+
* 递归收集所有子孙节点 ID(含自身)。
48+
*/
49+
public static void collectDescendants(Long id, Map<Long, List<Long>> childrenMap, Set<Long> out) {
50+
if (id == null || !out.add(id)) {
51+
return;
52+
}
53+
for (Long childId : childrenMap.getOrDefault(id, List.of())) {
54+
collectDescendants(childId, childrenMap, out);
55+
}
56+
}
57+
58+
/**
59+
* 构建 parentId → children 映射。
60+
*/
61+
public static Map<Long, List<Long>> buildChildrenMap(List<Long> ids, Function<Long, Long> parentIdGetter) {
62+
Map<Long, List<Long>> map = new HashMap<>();
63+
for (Long id : ids) {
64+
Long parentId = parentIdGetter.apply(id);
65+
if (parentId != null) {
66+
map.computeIfAbsent(parentId, k -> new ArrayList<>()).add(id);
67+
}
68+
}
69+
return map;
70+
}
71+
72+
/**
73+
* 检查 nodeId 是否为 ancestorId 的后代。
74+
*/
75+
public static <T> boolean isDescendant(Long ancestorId, Long nodeId, Map<Long, T> byId, Function<T, Long> parentIdGetter) {
76+
Long current = nodeId;
77+
while (current != null) {
78+
if (current.equals(ancestorId)) {
79+
return true;
80+
}
81+
T node = byId.get(current);
82+
current = node == null ? null : parentIdGetter.apply(node);
83+
}
84+
return false;
85+
}
86+
}

OpenBlog-business/src/main/java/com/yqz/openblog/media/service/MediaFolderService.java

Lines changed: 10 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.yqz.openblog.media.service;
22

33
import com.yqz.openblog.common.BizException;
4+
import com.yqz.openblog.common.TreeUtils;
45
import com.yqz.openblog.media.dto.MediaFolderFlatItemResponse;
56
import com.yqz.openblog.media.dto.MediaFolderTreeNodeResponse;
67
import com.yqz.openblog.media.dto.MediaFolderUpsertRequest;
@@ -85,28 +86,16 @@ public Set<Long> collectSelfAndDescendantIds(Long folderId) {
8586
return Collections.emptySet();
8687
}
8788
List<MediaFolder> all = folderRepository.findAllByOrderBySortOrderAscIdAsc();
88-
Map<Long, List<Long>> childrenMap = new HashMap<>();
89-
for (MediaFolder f : all) {
90-
if (f.getParentId() == null) {
91-
continue;
92-
}
93-
childrenMap.computeIfAbsent(f.getParentId(), k -> new ArrayList<>()).add(f.getId());
94-
}
89+
List<Long> ids = all.stream().map(MediaFolder::getId).toList();
90+
Map<Long, List<Long>> childrenMap = TreeUtils.buildChildrenMap(ids, id -> {
91+
MediaFolder f = all.stream().filter(mf -> mf.getId().equals(id)).findFirst().orElse(null);
92+
return f != null ? f.getParentId() : null;
93+
});
9594
Set<Long> out = new LinkedHashSet<>();
96-
collectDescendants(folderId, childrenMap, out);
95+
TreeUtils.collectDescendants(folderId, childrenMap, out);
9796
return out;
9897
}
9998

100-
private void collectDescendants(Long id, Map<Long, List<Long>> childrenMap, Set<Long> out) {
101-
if (id == null || !out.add(id)) {
102-
return;
103-
}
104-
List<Long> children = childrenMap.getOrDefault(id, List.of());
105-
for (Long childId : children) {
106-
collectDescendants(childId, childrenMap, out);
107-
}
108-
}
109-
11099
private void apply(MediaFolder f, MediaFolderUpsertRequest req, Long excludeId) {
111100
String name = req.getName() == null ? "" : req.getName().trim();
112101
if (name.isEmpty()) {
@@ -131,20 +120,11 @@ private void apply(MediaFolder f, MediaFolderUpsertRequest req, Long excludeId)
131120

132121
private boolean isDescendant(Long ancestorId, Long nodeId) {
133122
Map<Long, MediaFolder> byId = indexById(folderRepository.findAllByOrderBySortOrderAscIdAsc());
134-
Long current = nodeId;
135-
while (current != null) {
136-
if (current.equals(ancestorId)) {
137-
return true;
138-
}
139-
MediaFolder f = byId.get(current);
140-
current = f == null ? null : f.getParentId();
141-
}
142-
return false;
123+
return TreeUtils.isDescendant(ancestorId, nodeId, byId, MediaFolder::getParentId);
143124
}
144125

145126
private List<MediaFolderTreeNodeResponse> buildTree(List<MediaFolder> all) {
146127
Map<Long, MediaFolderTreeNodeResponse> nodes = new LinkedHashMap<>();
147-
// count files per folder
148128
Map<Long, Long> fileCounts = new HashMap<>();
149129
List<Media> allMedia = mediaMapper.selectList(Wrappers.lambdaQuery(Media.class).isNotNull(Media::getFolderId));
150130
for (Media m : allMedia) {
@@ -181,26 +161,11 @@ private List<MediaFolderTreeNodeResponse> buildTree(List<MediaFolder> all) {
181161
}
182162

183163
private Map<Long, MediaFolder> indexById(List<MediaFolder> all) {
184-
Map<Long, MediaFolder> map = new HashMap<>();
185-
for (MediaFolder f : all) {
186-
map.put(f.getId(), f);
187-
}
188-
return map;
164+
return TreeUtils.indexById(all, MediaFolder::getId);
189165
}
190166

191167
private List<String> buildPathNames(Long folderId, Map<Long, MediaFolder> byId) {
192-
List<String> path = new ArrayList<>();
193-
Set<Long> visited = new HashSet<>();
194-
Long current = folderId;
195-
while (current != null && visited.add(current)) {
196-
MediaFolder f = byId.get(current);
197-
if (f == null) {
198-
break;
199-
}
200-
path.add(0, f.getName());
201-
current = f.getParentId();
202-
}
203-
return path;
168+
return TreeUtils.buildPathNames(folderId, byId, MediaFolder::getParentId, MediaFolder::getName);
204169
}
205170

206171
private MediaFolderFlatItemResponse toFlatItem(MediaFolder f, Map<Long, MediaFolder> byId) {

vue/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"preview": "vite preview"
1010
},
1111
"dependencies": {
12-
"@element-plus/icons-vue": "^2.3.2",
1312
"dompurify": "^3.3.3",
1413
"marked": "^17.0.5",
1514
"oh-my-live2d": "^0.19.3",

vue/src/api/http.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ function buildUrl(path) {
2525
}
2626

2727
export async function request(path, options = {}) {
28+
const isFormData = options.body instanceof FormData
2829
const headers = {
29-
'Content-Type': 'application/json',
30+
...(isFormData ? {} : { 'Content-Type': 'application/json' }),
3031
...(options.headers || {})
3132
}
3233

0 commit comments

Comments
 (0)