后台菜单重构:菜单类型分组排序(软件在前/后台在后)、新增同类型末尾排序;任务中心分组移除,视频任务记录归入记录与版本、店铺数据记录归入店铺管理;界面名词优化(菜单权限配置/去重数据汇总/无效ASIN数据/视频任务记录/店铺数据记录/生成记录)
Build Backend JAR / build (push) Has been cancelled

This commit is contained in:
2026-08-31 01:48:05 +08:00
parent 0408c9752e
commit 26f4bb3f25
8 changed files with 134 additions and 66 deletions
@@ -49,15 +49,15 @@ public class PermissionMenuSchemaInitializer {
private static final List<DefaultAdminMenu> DEFAULT_ADMIN_MENUS = List.of(
new DefaultAdminMenu("用户管理", "admin_users", "users", 10),
new DefaultAdminMenu("栏目权限配置", "admin_columns", "columns", 20),
new DefaultAdminMenu("数据去重数据", "admin_dedupe_total_data", "dedupe-total-data", 30),
new DefaultAdminMenu("不符合ASIN数据", "admin_invalid_asin_data", "invalid-asin-data", 35),
new DefaultAdminMenu("菜单权限配置", "admin_columns", "columns", 20),
new DefaultAdminMenu("去重数据汇总", "admin_dedupe_total_data", "dedupe-total-data", 30),
new DefaultAdminMenu("无效ASIN数据", "admin_invalid_asin_data", "invalid-asin-data", 35),
new DefaultAdminMenu("店铺密钥管理", "admin_shop_keys", "shop-keys", 40),
new DefaultAdminMenu("店铺管理", "admin_shop_manage", "shop-manage", 50),
new DefaultAdminMenu("跳过跟价ASIN", "admin_skip_price_asin", "skip-price-asin", 60),
new DefaultAdminMenu("查询ASIN", "admin_query_asin", "query-asin", 65),
new DefaultAdminMenu("商品类目", "admin_product_categories", "product-categories", 66),
new DefaultAdminMenu("查看生成记录", "admin_history", "history", 70),
new DefaultAdminMenu("生成记录", "admin_history", "history", 70),
new DefaultAdminMenu("软件版本管理", "admin_version", "version", 80),
new DefaultAdminMenu("数字人版本管理", "digital_human_version", "digital-human-version", 81)
);
@@ -179,7 +179,7 @@ public class PermissionMenuSchemaInitializer {
private boolean ensureShopDataCrawlAdminDefaults() {
executeQuietly("""
INSERT INTO columns (name, column_key, menu_type, route_path, sort_order)
SELECT '店铺数据任务管理', 'admin_shop_data_crawl_tasks', 'admin', 'shop-data-crawl-tasks', 82
SELECT '店铺数据记录', 'admin_shop_data_crawl_tasks', 'admin', 'shop-data-crawl-tasks', 82
WHERE NOT EXISTS (
SELECT 1 FROM columns WHERE column_key = 'admin_shop_data_crawl_tasks'
)
@@ -73,7 +73,7 @@ public class PermissionMenuService {
String name = normalizeRequired(request.getName(), "菜单名称不能为空");
String columnKey = normalizeRequired(request.getColumnKey(), "菜单标识不能为空");
String menuType = normalizeMenuTypeRequired(request.getMenuType());
String routePath = normalizeRequired(request.getRoutePath(), "菜单路由不能为空");
String routePath = normalizeRequired(request.getRoutePath(), "页面路径不能为空");
Long parentId = normalizeParentId(request.getParentId());
ensureParentValid(parentId, null, menuType);
ensureUniqueColumnKey(columnKey, null);
@@ -106,7 +106,7 @@ public class PermissionMenuService {
String name = normalizeRequired(request.getName(), "菜单名称不能为空");
String columnKey = normalizeRequired(request.getColumnKey(), "菜单标识不能为空");
String menuType = normalizeMenuTypeRequired(request.getMenuType());
String routePath = normalizeRequired(request.getRoutePath(), "菜单路由不能为空");
String routePath = normalizeRequired(request.getRoutePath(), "页面路径不能为空");
Long parentId = normalizeParentId(request.getParentId());
ensureParentValid(parentId, id, menuType);
ensureMenuTypeChangeAllowed(entity, menuType);
@@ -706,7 +706,7 @@ public class PermissionMenuService {
.eq(PermissionMenuEntity::getRoutePath, routePath)
.last("LIMIT 1"));
if (exists != null && (excludeId == null || !excludeId.equals(exists.getId()))) {
throw new BusinessException("同一菜单类型下的菜单路由已存在");
throw new BusinessException("同一菜单类型下的页面路径已存在");
}
}
@@ -0,0 +1,28 @@
-- V97: 后台管理菜单显示名改为用户易理解的命名
-- 只更新 admin 类型菜单的 name 显示名,column_key / route_path 保持不变,
-- 所有权限校验、前端分组路由、面板加载逻辑均按 route_path 匹配,不受影响。
-- 对应前端静态文案见 backend/static/admin.js 与 backend/web_source/admin.html。
-- 栏目权限配置 -> 菜单权限配置
UPDATE `columns` SET `name` = '菜单权限配置'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_columns';
-- 数据去重总数据 -> 去重数据汇总
UPDATE `columns` SET `name` = '去重数据汇总'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_dedupe_total_data';
-- 不符合ASIN数据 -> 无效ASIN数据
UPDATE `columns` SET `name` = '无效ASIN数据'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_invalid_asin_data';
-- 视频任务管理 -> 视频任务记录
UPDATE `columns` SET `name` = '视频任务记录'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_image_video_tasks';
-- 店铺数据任务管理 -> 店铺数据记录
UPDATE `columns` SET `name` = '店铺数据记录'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_shop_data_crawl_tasks';
-- 查看生成记录 -> 生成记录
UPDATE `columns` SET `name` = '生成记录'
WHERE `menu_type` = 'admin' AND `column_key` = 'admin_history';
@@ -584,6 +584,55 @@ class PermissionMenuServiceTest {
assertThat(inserted.getValue().getColumnId()).isEqualTo(76L);
}
@Test
void fullCatalogOrdersAppMenusBeforeAdminMenus() {
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
// 全量目录:app/admin 交错出现,且各自 sort_order 交叉。期望先按类型分组
// app 在 admin 之前),组内再按 sort_order 升序。
when(menuMapper.selectList(any())).thenReturn(List.of(
menu(11L, null, "admin", 20),
menu(21L, null, "app", 120),
menu(12L, null, "admin", 10),
menu(22L, null, "app", 110)));
List<PermissionMenuItemVo> result = service.list(null);
assertThat(result).extracting(PermissionMenuItemVo::getId)
.containsExactly(22L, 21L, 12L, 11L);
}
@Test
void createWithoutSortOrderAppendsWithinSameMenuType() {
PermissionMenuMapper menuMapper = mock(PermissionMenuMapper.class);
UserColumnPermissionMapper permissionMapper = mock(UserColumnPermissionMapper.class);
AdminUserMapper userMapper = mock(AdminUserMapper.class);
PermissionMenuService service = new PermissionMenuService(menuMapper, permissionMapper, userMapper);
// create 内部依次用 selectOne 做唯一性校验(column_key、route_path),
// 最后 resolveSortOrder 取同类型尾项(admin 最后一个 sort_order = 134)。
PermissionMenuEntity adminTail = menu(9L, null, "admin", 134);
adminTail.setName("admin tail");
when(menuMapper.selectOne(any())).thenReturn(null, null, adminTail);
// create 成功后 getMenuById(insert 未回填的 id) 会走 selectById
when(menuMapper.selectById(any())).thenReturn(adminTail);
PermissionMenuCreateRequest request = createRequest(null, "admin");
request.setName("新菜单");
request.setColumnKey("new_admin_menu");
request.setRoutePath("new-admin-menu");
service.create(request);
ArgumentCaptor<PermissionMenuEntity> inserted = ArgumentCaptor.forClass(PermissionMenuEntity.class);
verify(menuMapper).insert(inserted.capture());
assertThat(inserted.getValue().getSortOrder()).isEqualTo(135);
assertThat(inserted.getValue().getMenuType()).isEqualTo("admin");
}
private PermissionMenuCreateRequest createRequest(Long parentId, String menuType) {
PermissionMenuCreateRequest request = new PermissionMenuCreateRequest();
request.setName("child");