chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
package com.xspaceagi.system.application.aspect;
import com.xspaceagi.system.application.service.SysUserPermissionCacheService;
import com.xspaceagi.system.spec.annotation.ClearAllUserPermissionCache;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* 用户权限缓存清理切面
*
* 对标记了 {@link ClearAllUserPermissionCache} 的应用层方法,
* 在方法成功执行后统一清理所有用户的权限缓存。
*/
@Slf4j
@Aspect
@Component
public class UserPermissionCacheClearAspect {
@Resource
private SysUserPermissionCacheService sysUserPermissionCacheService;
@AfterReturning("@annotation(clearAllUserPermissionCache)")
public void clearAllCacheAfterSuccess(ClearAllUserPermissionCache clearAllUserPermissionCache) {
try {
sysUserPermissionCacheService.clearCacheAll();
} catch (Exception e) {
log.warn("Failed to clear all user permission caches", e);
}
}
}

View File

@@ -0,0 +1,68 @@
package com.xspaceagi.system.application.aspect;
import com.xspaceagi.system.application.service.SysUserPermissionCacheService;
import com.xspaceagi.system.spec.annotation.ClearUserPermissionCacheByUserIds;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 用户维度权限缓存清理切面
*/
@Slf4j
@Aspect
@Component
public class UserPermissionUserCacheAspect {
@Resource
private SysUserPermissionCacheService sysUserPermissionCacheService;
@AfterReturning("@annotation(clearAnno)")
public void clearCacheByUserIds(JoinPoint joinPoint, ClearUserPermissionCacheByUserIds clearAnno) {
Object[] args = joinPoint.getArgs();
int[] indexes = clearAnno.userIdParamIndexes();
List<Long> userIds = new ArrayList<>();
if (indexes != null) {
for (int idx : indexes) {
if (idx < 0 || idx >= args.length) {
continue;
}
Object arg = args[idx];
collectUserIdsFromArg(arg, userIds);
}
}
if (!userIds.isEmpty()) {
try {
sysUserPermissionCacheService.clearCacheByUserIds(userIds);
} catch (Exception e) {
log.warn("按用户ID清理权限缓存失败, userIds={}", userIds, e);
}
}
}
@SuppressWarnings("unchecked")
private void collectUserIdsFromArg(Object arg, List<Long> userIds) {
if (arg == null) {
return;
}
if (arg instanceof Long) {
userIds.add((Long) arg);
} else if (arg instanceof Collection<?>) {
for (Object item : (Collection<?>) arg) {
if (item instanceof Long) {
userIds.add((Long) item);
}
}
}
}
}

View File

@@ -0,0 +1,4 @@
/**
* Translators.
*/
package com.xspaceagi.system.application.assembler;

View File

@@ -0,0 +1,25 @@
package com.xspaceagi.system.application.component;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 记录服务启动时间,在应用启动完成时写入
*/
@Component
public class ServerStartupTimeHolder {
private volatile LocalDateTime serverStartupTime;
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady(ApplicationReadyEvent event) {
this.serverStartupTime = LocalDateTime.now();
}
public LocalDateTime getServerStartupTime() {
return serverStartupTime;
}
}

View File

@@ -0,0 +1,78 @@
package com.xspaceagi.system.application.constant;
import java.util.IllformedLocaleException;
import java.util.Locale;
import java.util.Optional;
/**
* 语言标识lang校验使用 JDK {@link Locale.Builder#setLanguageTag(String)}IETF BCP 47
*/
public final class I18nLangTagConstraints {
/** Bean Validation 默认提示(中文) */
public static final String LANG_TAG_MESSAGE = "语言标识格式不正确,请使用合法的语言标签,如 zh-CN、en-US、zh-Hans-CN";
/** 与 {@link #LANG_TAG_MESSAGE} 语义一致,用于 {@code IllegalArgumentException} 等直接透出 API 的场景 */
public static final String LANG_TAG_MESSAGE_EN =
"Invalid language tag. Use a valid BCP 47 tag such as zh-CN, en-US, or zh-Hans-CN.";
private I18nLangTagConstraints() {
}
/**
* 是否为 JDK 可解析的合法语言标签,且包含非空 language 子标签。
*/
public static boolean isWellFormedLanguageTag(String raw) {
if (raw == null) {
return false;
}
String s = raw.trim();
if (s.isEmpty()) {
return false;
}
try {
Locale loc = new Locale.Builder().setLanguageTag(s).build();
return !loc.getLanguage().isEmpty();
} catch (IllformedLocaleException e) {
return false;
}
}
/**
* 解析并规范为入库形式:{@link Locale#toLanguageTag()} 原样JDK 典型形式,如 zh-CN、zh-Hans-CN
* 与 {@link Locale}、HTTP 常见写法一致。
*/
public static Optional<String> tryNormalizeToStoredForm(String raw) {
if (raw == null) {
return Optional.empty();
}
String s = raw.trim();
if (s.isEmpty()) {
return Optional.empty();
}
try {
Locale loc = new Locale.Builder().setLanguageTag(s).build();
if (loc.getLanguage().isEmpty()) {
return Optional.empty();
}
return Optional.of(loc.toLanguageTag());
} catch (IllformedLocaleException e) {
return Optional.empty();
}
}
/**
* 是否指向同一语言标签(忽略大小写/等价规范化)。
*/
public static boolean sameLanguageTag(String a, String b) {
if (a == null || b == null) {
return a == null && b == null;
}
Optional<String> ca = tryNormalizeToStoredForm(a);
Optional<String> cb = tryNormalizeToStoredForm(b);
if (ca.isPresent() && cb.isPresent()) {
return ca.get().equalsIgnoreCase(cb.get());
}
return a.trim().equalsIgnoreCase(b.trim());
}
}

View File

@@ -0,0 +1,71 @@
package com.xspaceagi.system.application.constant;
import java.util.List;
/**
* BCP 47 语言标识白名单(覆盖主要经济体与常见访客区域)
*/
public final class SupportedLocaleConstants {
private SupportedLocaleConstants() {
}
/**
* 默认可选语言/区域(约数十种),按 tag 字母序便于 diff。
*/
public static final List<String> DEFAULT_LOCALE_TAG_WHITELIST = List.of(
"ar-SA",
"bg-BG",
"bn-IN",
"cs-CZ",
"da-DK",
"de-DE",
"el-GR",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-SG",
"en-US",
"es-419",
"es-ES",
"es-MX",
"et-EE",
"fa-IR",
"fi-FI",
"fil-PH",
"fr-CA",
"fr-FR",
"he-IL",
"hi-IN",
"hr-HR",
"hu-HU",
"id-ID",
"is-IS",
"it-IT",
"ja-JP",
"ko-KR",
"lt-LT",
"lv-LV",
"ms-MY",
"nb-NO",
"nl-NL",
"pl-PL",
"pt-BR",
"pt-PT",
"ro-RO",
"ru-RU",
"sk-SK",
"sl-SI",
"sv-SE",
"sw-KE",
"th-TH",
"tr-TR",
"uk-UA",
"ur-PK",
"vi-VN",
"zh-CN",
"zh-HK",
"zh-TW"
);
}

View File

@@ -0,0 +1,40 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.domain.model.GroupBindMenuModel;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import com.xspaceagi.system.application.dto.permission.SysGroupBindMenuDto;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* DTO到Model转换器
*/
public class GroupBindMenuModelConverter {
public static GroupBindMenuModel convertToModel(SysGroupBindMenuDto dto) {
if (dto == null || dto.getGroupId() == null) {
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.fieldRequiredButEmpty, "用户组ID");
}
GroupBindMenuModel model = new GroupBindMenuModel();
model.setGroupId(dto.getGroupId());
// 获取所有菜单节点
List<MenuNodeDto> menuNodes = dto.getAllMenuNodes();
if (CollectionUtils.isEmpty(menuNodes)) {
model.setMenuBindResourceList(new ArrayList<>());
return model;
}
// 转换为Domain模型
List<MenuNode> menuBindInfos = MenuBindModelConverterUtil.convertMenuNodes(menuNodes);
model.setMenuBindResourceList(menuBindInfos);
return model;
}
}

View File

@@ -0,0 +1,74 @@
//package com.xspaceagi.system.application.converter;
//
//import java.util.ArrayList;
//import java.util.List;
//import java.util.Map;
//import java.util.stream.Collectors;
//
//import org.apache.commons.collections4.CollectionUtils;
//
//import com.xspaceagi.system.application.dto.permission.SysGroupDto;
//
///**
// * 用户组树工具类
// */
//public class GroupTreeUtil {
//
// /**
// * 构建用户组树形结构
// */
// public static List<SysGroupDto> buildGroupTree(List<SysGroupDto> groupList) {
// if (CollectionUtils.isEmpty(groupList)) {
// return new ArrayList<>();
// }
//
// // 按ID建立索引方便查找
// Map<Long, SysGroupDto> groupMap = groupList.stream()
// .collect(Collectors.toMap(SysGroupDto::getId, group -> group));
//
// // 构建树形结构
// List<SysGroupDto> rootGroups = new ArrayList<>();
// for (SysGroupDto group : groupList) {
// Long parentId = group.getParentId();
// if (parentId == null || parentId == 0L) {
// // 根节点
// rootGroups.add(group);
// } else {
// // 子节点添加到父节点的children中
// SysGroupDto parent = groupMap.get(parentId);
// if (parent != null) {
// if (parent.getChildren() == null) {
// parent.setChildren(new ArrayList<>());
// }
// parent.getChildren().add(group);
// }
// }
// }
//
// // 对每个节点的children进行排序
// sortGroupTree(rootGroups);
//
// // 返回根节点列表(已排序)
// return rootGroups;
// }
//
// /**
// * 递归排序用户组树
// */
// private static void sortGroupTree(List<SysGroupDto> groupList) {
// if (CollectionUtils.isEmpty(groupList)) {
// return;
// }
// groupList.sort((a, b) -> {
// Integer sortA = a.getSortIndex() != null ? a.getSortIndex() : 0;
// Integer sortB = b.getSortIndex() != null ? b.getSortIndex() : 0;
// return sortA.compareTo(sortB);
// });
// for (SysGroupDto group : groupList) {
// if (CollectionUtils.isNotEmpty(group.getChildren())) {
// sortGroupTree(group.getChildren());
// }
// }
// }
//}
//

View File

@@ -0,0 +1,35 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 菜单绑定模型转换工具类
*/
public class MenuBindModelConverterUtil {
/**
* 将菜单节点列表转换为MenuBindResource列表
*/
public static List<MenuNode> convertMenuNodes(List<MenuNodeDto> menuNodeDtos) {
if (CollectionUtils.isEmpty(menuNodeDtos)) {
return new ArrayList<>();
}
List<MenuNode> menuNodes = new ArrayList<>();
for (MenuNodeDto menuNodeDto : menuNodeDtos) {
if (menuNodeDto.getId() == null) {
continue;
}
MenuNode menuNode = MenuBindResourceModelConverter.convertToMenuNode(menuNodeDto);
menuNodes.add(menuNode);
}
return menuNodes;
}
}

View File

@@ -0,0 +1,83 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.domain.model.ResourceNode;
import com.xspaceagi.system.spec.enums.BindTypeEnum;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import com.xspaceagi.system.application.dto.permission.ResourceNodeDto;
import com.xspaceagi.system.application.dto.permission.SysMenuBindResourceDto;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 菜单资源绑定 DTO -> Domain 模型转换器
*/
public class MenuBindResourceModelConverter {
public static MenuNode convertToMenuNode(SysMenuBindResourceDto dto) {
if (dto == null) {
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemParamRequired);
}
MenuNodeDto menuNodeDto = new MenuNodeDto();
menuNodeDto.setId(dto.getMenuId());
menuNodeDto.setResourceTree(dto.getResourceTree());
MenuNode model = convertToMenuNode(menuNodeDto);
return model;
}
// 转换后资源是树形结构,不做扁平化处理
public static MenuNode convertToMenuNode(MenuNodeDto menuNodeDto) {
MenuNode menuNode = new MenuNode();
menuNode.setId(menuNodeDto.getId());
Integer menuBindType = menuNodeDto.getMenuBindType() != null ? menuNodeDto.getMenuBindType() : BindTypeEnum.NONE.getCode();
menuNode.setMenuBindType(menuBindType);
List<ResourceNode> resourceTree = convertResourceTree(menuNodeDto.getResourceTree());
menuNode.setResourceTree(resourceTree);
// 如果resourceBindType为PART从resourceTree中提取所有资源ID并保留完整资源树
// if (BindTypeEnum.PART.getCode().equals(resourceBindType)) {
// // 转换ResourceNode
// List<ResourceNode> resourceTree = convertResourceTree(menuNodeDto.getResourceTree());
// menuNode.setResourceTree(resourceTree);
// } else {
// menuNode.setResourceTree(null);
// }
return menuNode;
}
/**
* 转换资源树。支持两种格式:
* 1. 树形结构(含 children
* 2. 扁平列表(无 children仅传叶子节点含 parentId
*/
public static List<ResourceNode> convertResourceTree(List<ResourceNodeDto> nodes) {
if (CollectionUtils.isEmpty(nodes)) {
return new ArrayList<>();
}
List<ResourceNode> result = new ArrayList<>();
for (ResourceNodeDto node : nodes) {
ResourceNode resourceNode = new ResourceNode();
resourceNode.setId(node.getId());
resourceNode.setParentId(node.getParentId());
Integer resourceBindType = node.getResourceBindType() != null ? node.getResourceBindType() : BindTypeEnum.NONE.getCode();
resourceNode.setResourceBindType(resourceBindType);
resourceNode.setCode(node.getCode());
if (!CollectionUtils.isEmpty(node.getChildren())) {
resourceNode.setChildren(convertResourceTree(node.getChildren()));
}
result.add(resourceNode);
}
return result;
}
}

View File

@@ -0,0 +1,80 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import com.xspaceagi.system.application.dto.permission.ResourceNodeDto;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.domain.model.ResourceNode;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.BeanUtils;
import java.util.ArrayList;
import java.util.List;
/**
* MenuNode 转换为 MenuNodeDto 的转换器
*/
public class MenuNodeConverter {
public static List<MenuNodeDto> convertMenuTreeToDtoTree(List<MenuNode> menuNodeList) {
if (CollectionUtils.isEmpty(menuNodeList)) {
return new ArrayList<>();
}
List<MenuNodeDto> result = new ArrayList<>();
for (MenuNode menuNode : menuNodeList) {
MenuNodeDto dto = new MenuNodeDto();
BeanUtils.copyProperties(menuNode, dto);
// 转换资源树资源详细信息已在application层填充
if (CollectionUtils.isNotEmpty(menuNode.getResourceTree())) {
dto.setResourceTree(convertResourceTree(menuNode.getResourceTree()));
}
// 递归转换children
if (CollectionUtils.isNotEmpty(menuNode.getChildren())) {
dto.setChildren(convertMenuTreeToDtoTree(menuNode.getChildren()));
}
result.add(dto);
}
return result;
}
private static List<ResourceNodeDto> convertResourceTree(
List<ResourceNode> resourceNodes) {
if (CollectionUtils.isEmpty(resourceNodes)) {
return new ArrayList<>();
}
List<ResourceNodeDto> result = new ArrayList<>();
for (ResourceNode node : resourceNodes) {
ResourceNodeDto dto = new ResourceNodeDto();
dto.setId(node.getId());
dto.setResourceBindType(node.getResourceBindType());
// 资源详细信息已在application层填充直接复制
dto.setCode(node.getCode());
dto.setName(node.getName());
dto.setDescription(node.getDescription());
dto.setSource(node.getSource());
dto.setType(node.getType());
dto.setParentId(node.getParentId());
dto.setPath(node.getPath());
dto.setIcon(node.getIcon());
dto.setSortIndex(node.getSortIndex());
dto.setStatus(node.getStatus());
// 递归转换children
if (CollectionUtils.isNotEmpty(node.getChildren())) {
dto.setChildren(convertResourceTree(node.getChildren()));
}
result.add(dto);
}
return result;
}
}

View File

@@ -0,0 +1,100 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import com.xspaceagi.system.application.dto.permission.ResourceNodeDto;
import org.apache.commons.collections4.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 菜单树工具类
*/
public class MenuTreeUtil {
/**
* 构建菜单树形结构,同时将每个菜单节点的 resourceTree 规范为树形结构
*/
public static List<MenuNodeDto> buildMenuTree(List<MenuNodeDto> menuList) {
if (CollectionUtils.isEmpty(menuList)) {
return new ArrayList<>();
}
// 按ID建立索引方便查找
Map<Long, MenuNodeDto> menuMap = menuList.stream()
.collect(Collectors.toMap(MenuNodeDto::getId, menu -> menu));
// 构建树形结构
List<MenuNodeDto> rootMenus = new ArrayList<>();
for (MenuNodeDto menu : menuList) {
Long parentId = menu.getParentId();
if (parentId == null || parentId == 0L) {
// 根节点
rootMenus.add(menu);
} else {
// 子节点添加到父节点的children中
MenuNodeDto parent = menuMap.get(parentId);
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(menu);
}
}
}
// 对每个节点的children进行排序
sortMenuTree(rootMenus);
// 将每个菜单节点的 resourceTree 转成树形结构
ensureResourceTreeForMenuTree(rootMenus);
// 返回根节点列表(已排序)
return rootMenus;
}
/**
* 递归确保菜单树中每个节点的 resourceTree 均为树形结构。
* 若存在 resourceNodes扁平结构则从中构建树形并 set 到 resourceTree
* 否则若 resourceTree 本身非标准树形,则规范为树形。
*/
private static void ensureResourceTreeForMenuTree(List<MenuNodeDto> menuList) {
if (CollectionUtils.isEmpty(menuList)) {
return;
}
for (MenuNodeDto menu : menuList) {
List<ResourceNodeDto> resourceList = CollectionUtils.isNotEmpty(menu.getResourceNodes())
? menu.getResourceNodes()
: menu.getResourceTree();
if (CollectionUtils.isNotEmpty(resourceList)) {
menu.setResourceTree(ResourceTreeUtil.buildResourceTreeFromNodes(resourceList));
// 不需要返回 resourceNodes
menu.setResourceNodes(null);
}
if (CollectionUtils.isNotEmpty(menu.getChildren())) {
ensureResourceTreeForMenuTree(menu.getChildren());
}
}
}
/**
* 递归排序菜单树
*/
private static void sortMenuTree(List<MenuNodeDto> menuList) {
if (CollectionUtils.isEmpty(menuList)) {
return;
}
menuList.sort((a, b) -> {
Integer sortA = a.getSortIndex() != null ? a.getSortIndex() : 0;
Integer sortB = b.getSortIndex() != null ? b.getSortIndex() : 0;
return sortA.compareTo(sortB);
});
for (MenuNodeDto menu : menuList) {
if (CollectionUtils.isNotEmpty(menu.getChildren())) {
sortMenuTree(menu.getChildren());
}
}
}
}

View File

@@ -0,0 +1,230 @@
package com.xspaceagi.system.application.converter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.collections4.CollectionUtils;
import com.xspaceagi.system.infra.dao.entity.SysResource;
import com.xspaceagi.system.spec.enums.BindTypeEnum;
import com.xspaceagi.system.application.dto.permission.ResourceNodeDto;
/**
* 资源树工具类
*/
public class ResourceTreeUtil {
/**
* 构建资源树
*/
public static List<ResourceNodeDto> buildResourceTree(List<SysResource> resourceList) {
return buildResourceTree(resourceList, Map.of());
}
/**
* 构建资源树
*
* @param resourceList 资源列表
* @param resourceBindTypeMap 资源ID到绑定类型的映射key: resourceId, value: resourceBindType
* @return 资源树形结构
*/
public static List<ResourceNodeDto> buildResourceTree(List<SysResource> resourceList, Map<Long, Integer> resourceBindTypeMap) {
if (CollectionUtils.isEmpty(resourceList)) {
return new ArrayList<>();
}
// 转换为 ResourceNodeDto
List<ResourceNodeDto> nodeList = resourceList.stream().map(resource -> {
ResourceNodeDto node = convertResourceToNode(resource);
Integer resourceBindType = resourceBindTypeMap.containsKey(resource.getId()) ? resourceBindTypeMap.get(resource.getId()) : BindTypeEnum.NONE.getCode();
node.setResourceBindType(resourceBindType);
return node;
}).toList();
// 按ID建立索引方便查找
Map<Long, ResourceNodeDto> nodeMap = nodeList.stream()
.collect(Collectors.toMap(ResourceNodeDto::getId, node -> node));
// 构建树形结构
List<ResourceNodeDto> rootNodes = new ArrayList<>();
for (ResourceNodeDto node : nodeList) {
Long parentId = node.getParentId();
if (parentId == null || parentId == 0L) {
// 根节点
rootNodes.add(node);
} else {
// 子节点添加到父节点的children中
ResourceNodeDto parent = nodeMap.get(parentId);
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(node);
} else {
// 父节点不在当前列表中,作为根节点处理
rootNodes.add(node);
}
}
}
// 对每个节点及children进行排序
sortResourceTree(rootNodes);
// 对每个节点进行 ALL 绑定类型的处理
markChildrenForAllBindType(rootNodes, resourceBindTypeMap);
// 返回根节点列表(已排序)
return rootNodes;
}
/**
* 递归排序资源树
*/
private static void sortResourceTree(List<ResourceNodeDto> nodeList) {
if (CollectionUtils.isEmpty(nodeList)) {
return;
}
nodeList.sort((a, b) -> {
Integer sortA = a.getSortIndex() != null ? a.getSortIndex() : 0;
Integer sortB = b.getSortIndex() != null ? b.getSortIndex() : 0;
return sortA.compareTo(sortB);
});
for (ResourceNodeDto node : nodeList) {
if (CollectionUtils.isNotEmpty(node.getChildren())) {
sortResourceTree(node.getChildren());
}
}
}
/**
* 对于绑定方式为 ALL 的节点,将其所有子节点也强制标记为 ALL
* 注意ALL 类型的资源会强制覆盖子资源的绑定类型
*/
private static void markChildrenForAllBindType(List<ResourceNodeDto> nodes, Map<Long, Integer> resourceBindTypeMap) {
if (CollectionUtils.isEmpty(nodes)) {
return;
}
for (ResourceNodeDto node : nodes) {
if (BindTypeEnum.ALL.getCode().equals(node.getResourceBindType())) {
// 父节点是 ALL强制将所有子节点也标记为 ALL
markAllChildren(node, BindTypeEnum.ALL.getCode());
} else if (CollectionUtils.isNotEmpty(node.getChildren())) {
// 继续递归处理子节点
markChildrenForAllBindType(node.getChildren(), resourceBindTypeMap);
}
}
}
/**
* 递归将子节点强制标记为指定绑定类型
* 注意:此方法会强制覆盖子节点的绑定类型(用于处理 ALL 类型)
*/
private static void markAllChildren(ResourceNodeDto node, Integer resourceBindType) {
if (CollectionUtils.isEmpty(node.getChildren())) {
return;
}
for (ResourceNodeDto child : node.getChildren()) {
// 强制设置子节点的绑定类型为 ALL覆盖原有类型
// 因为父节点是 ALL 时,所有子节点也必须是 ALL
child.setResourceBindType(resourceBindType);
// 递归处理子节点的子节点
markAllChildren(child, resourceBindType);
}
}
private static ResourceNodeDto convertResourceToNode(SysResource resource) {
ResourceNodeDto node = new ResourceNodeDto();
node.setId(resource.getId());
node.setCode(resource.getCode());
node.setName(resource.getName());
node.setDescription(resource.getDescription());
node.setSource(resource.getSource());
node.setType(resource.getType());
node.setParentId(resource.getParentId());
node.setPath(resource.getPath());
node.setIcon(resource.getIcon());
node.setSortIndex(resource.getSortIndex());
node.setStatus(resource.getStatus());
return node;
}
/**
* 将 ResourceNodeDto 列表(扁平或已是树形)构建/规范为树形结构
*
* @param nodeList 资源节点列表,每个节点需包含 id 和 parentId若已是树形则先扁平化再重建
* @return 资源树形结构(根节点列表)
*/
public static List<ResourceNodeDto> buildResourceTreeFromNodes(List<ResourceNodeDto> nodeList) {
if (CollectionUtils.isEmpty(nodeList)) {
return new ArrayList<>();
}
List<ResourceNodeDto> flatList = flattenToNodeListWithParentId(nodeList, null);
return buildTreeFromFlatNodeList(flatList);
}
/**
* 递归将资源树扁平化为列表,并确保每个节点的 parentId 正确
*/
private static List<ResourceNodeDto> flattenToNodeListWithParentId(List<ResourceNodeDto> nodes, Long parentId) {
if (CollectionUtils.isEmpty(nodes)) {
return new ArrayList<>();
}
List<ResourceNodeDto> result = new ArrayList<>();
for (ResourceNodeDto node : nodes) {
ResourceNodeDto copy = new ResourceNodeDto();
copy.setId(node.getId());
copy.setParentId(node.getParentId() != null ? node.getParentId() : parentId);
copy.setResourceBindType(node.getResourceBindType());
copy.setCode(node.getCode());
copy.setName(node.getName());
copy.setDescription(node.getDescription());
copy.setSource(node.getSource());
copy.setType(node.getType());
copy.setPath(node.getPath());
copy.setIcon(node.getIcon());
copy.setSortIndex(node.getSortIndex());
copy.setStatus(node.getStatus());
result.add(copy);
if (CollectionUtils.isNotEmpty(node.getChildren())) {
result.addAll(flattenToNodeListWithParentId(node.getChildren(), node.getId()));
}
}
return result;
}
private static List<ResourceNodeDto> buildTreeFromFlatNodeList(List<ResourceNodeDto> flatList) {
if (CollectionUtils.isEmpty(flatList)) {
return new ArrayList<>();
}
Map<Long, ResourceNodeDto> nodeMap = flatList.stream()
.filter(n -> n.getId() != null)
.collect(Collectors.toMap(ResourceNodeDto::getId, n -> n, (a, b) -> a));
List<ResourceNodeDto> rootNodes = new ArrayList<>();
for (ResourceNodeDto node : flatList) {
Long nodeParentId = node.getParentId();
if (nodeParentId == null || nodeParentId == 0L) {
rootNodes.add(node);
} else {
ResourceNodeDto parent = nodeMap.get(nodeParentId);
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(node);
} else {
rootNodes.add(node);
}
}
}
sortResourceTree(rootNodes);
return rootNodes;
}
}

View File

@@ -0,0 +1,41 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.domain.model.MenuNode;
import com.xspaceagi.system.domain.model.RoleBindMenuModel;
import com.xspaceagi.system.application.dto.permission.MenuNodeDto;
import com.xspaceagi.system.application.dto.permission.SysRoleBindMenuDto;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 角色菜单绑定DTO到Model转换器
*/
public class RoleBindMenuModelConverter {
/**
* 将Web层DTO转换为Domain层模型
*/
public static RoleBindMenuModel convertToModel(SysRoleBindMenuDto dto) {
if (dto == null || dto.getRoleId() == null) {
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.fieldRequiredButEmpty, "角色ID");
}
RoleBindMenuModel model = new RoleBindMenuModel();
model.setRoleId(dto.getRoleId());
List<MenuNodeDto> menuNodes = dto.getFlattenlMenuNodes();
if (CollectionUtils.isEmpty(menuNodes)) {
model.setMenuBindResourceList(new ArrayList<>());
return model;
}
List<MenuNode> menuBindInfos = MenuBindModelConverterUtil.convertMenuNodes(menuNodes);
model.setMenuBindResourceList(menuBindInfos);
return model;
}
}

View File

@@ -0,0 +1,78 @@
package com.xspaceagi.system.application.converter;
import com.xspaceagi.system.application.dto.permission.SysDataPermissionBindDto;
import com.xspaceagi.system.infra.dao.entity.SysDataPermission;
import com.xspaceagi.system.sdk.service.dto.TokenLimit;
import com.xspaceagi.system.spec.jackson.JsonSerializeUtil;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 数据权限转换器
*/
public final class SysDataPermissionConverter {
private SysDataPermissionConverter() {
}
/**
* 实体转 DTO用于查询接口返回
*/
public static SysDataPermissionBindDto toDto(SysDataPermission entity) {
if (entity == null) {
return null;
}
SysDataPermissionBindDto dto = new SysDataPermissionBindDto();
BeanUtils.copyProperties(entity, dto);
return dto;
}
public static SysDataPermission toEntity(SysDataPermissionBindDto dto) {
if (dto == null) {
return null;
}
SysDataPermission entity = new SysDataPermission();
entity.setModelIds(dto.getModelIds());
entity.setAgentIds(dto.getAgentIds());
entity.setPageAgentIds(dto.getPageAgentIds());
Map<String, String> openApiConfigMap = new LinkedHashMap<>();
if (CollectionUtils.isNotEmpty(dto.getOpenApiConfigs())) {
for (SysDataPermissionBindDto.OpenApiConfig config : dto.getOpenApiConfigs()) {
if (config == null || StringUtils.isBlank(config.getKey())) {
continue;
}
Map<String, Integer> configValue = new LinkedHashMap<>();
configValue.put("rpm", config.getRpm());
configValue.put("rpd", config.getRpd());
openApiConfigMap.put(config.getKey(), JsonSerializeUtil.toJSONString(configValue));
}
}
entity.setOpenApiConfigMap(openApiConfigMap);
entity.setKnowledgeIds(dto.getKnowledgeIds());
// 数量类型不传默认 -1不限制
entity.setTokenLimit(dto.getTokenLimit() != null ? dto.getTokenLimit() : new TokenLimit(-1L));
entity.setMaxSpaceCount(dto.getMaxSpaceCount() != null ? dto.getMaxSpaceCount() : -1);
entity.setMaxAgentCount(dto.getMaxAgentCount() != null ? dto.getMaxAgentCount() : -1);
entity.setMaxPageAppCount(dto.getMaxPageAppCount() != null ? dto.getMaxPageAppCount() : -1);
entity.setMaxKnowledgeCount(dto.getMaxKnowledgeCount() != null ? dto.getMaxKnowledgeCount() : -1);
entity.setKnowledgeStorageLimitGb(dto.getKnowledgeStorageLimitGb() != null ? dto.getKnowledgeStorageLimitGb() : BigDecimal.valueOf(-1L));
entity.setMaxDataTableCount(dto.getMaxDataTableCount() != null ? dto.getMaxDataTableCount() : -1);
entity.setMaxScheduledTaskCount(dto.getMaxScheduledTaskCount() != null ? dto.getMaxScheduledTaskCount() : -1);
entity.setAgentFileStorageDays(dto.getAgentFileStorageDays() != null ? dto.getAgentFileStorageDays() : -1);
entity.setAgentDailyPromptLimit(dto.getAgentDailyPromptLimit() != null ? dto.getAgentDailyPromptLimit() : -1);
entity.setPageDailyPromptLimit(dto.getPageDailyPromptLimit() != null ? dto.getPageDailyPromptLimit() : -1);
//entity.setAllowApiExternalCall(dto.getAllowApiExternalCall());
// 智能体电脑内存不传默认 4交换分区不传默认 8CPU核心数不传默认 2
entity.setAgentComputerCpuCores(dto.getAgentComputerCpuCores() != null ? dto.getAgentComputerCpuCores() : 2);
entity.setAgentComputerMemoryGb(dto.getAgentComputerMemoryGb() != null ? dto.getAgentComputerMemoryGb() : 4);
entity.setAgentComputerSwapGb(null);
return entity;
}
}

View File

@@ -0,0 +1,392 @@
//package com.xspaceagi.system.web.converter;
//
//import java.util.ArrayList;
//import java.util.Comparator;
//import java.util.HashMap;
//import java.util.HashSet;
//import java.util.List;
//import java.util.Map;
//import java.util.Objects;
//import java.util.Set;
//import java.util.stream.Collectors;
//
//import org.apache.commons.collections4.CollectionUtils;
//
//import com.xspaceagi.system.domain.model.MenuNode;
//import com.xspaceagi.system.domain.model.ResourceNode;
//import com.xspaceagi.system.infra.dao.entity.SysMenu;
//import com.xspaceagi.system.infra.dao.entity.SysResource;
//import com.xspaceagi.system.spec.enums.BindTypeEnum;
//
///**
// * 用户维度菜单 + 资源权限合并工具
// */
//public class UserMenuMergeUtil {
//
// /**
// * 合并来自多个角色 / 用户组的菜单权限
// * - 同一 menuId 的菜单进行合并
// * - menuBindType 取"最大权限"ALL > PART > NONE
// * - 资源权限按 resourceId 合并resourceBindType 同样取最大权限
// */
// public static List<MenuNode> mergeUserMenuNodes(List<MenuNode> nodes) {
// if (CollectionUtils.isEmpty(nodes)) {
// return new ArrayList<>();
// }
//
// // 按 menuId 分组
// Map<Long, List<MenuNode>> menuGroup = nodes.stream()
// .filter(n -> n.getMenuId() != null)
// .collect(Collectors.groupingBy(MenuNode::getMenuId));
//
// List<MenuNode> result = new ArrayList<>();
// for (Map.Entry<Long, List<MenuNode>> entry : menuGroup.entrySet()) {
// Long menuId = entry.getKey();
// List<MenuNode> menuNodes = entry.getValue();
// if (CollectionUtils.isEmpty(menuNodes)) {
// continue;
// }
//
// MenuNode merged = new MenuNode();
// merged.setMenuId(menuId);
//
// // 1. 合并 menuBindType取最大权限
// Integer mergedMenuBindType = menuNodes.stream()
// .map(MenuNode::getMenuBindType)
// .filter(Objects::nonNull)
// .max(Comparator.comparingInt(UserMenuMergeUtil::bindTypeOrder))
// .orElse(BindTypeEnum.NONE.getCode());
// merged.setMenuBindType(mergedMenuBindType);
//
// // 2. 合并资源权限(合并所有资源树)
// List<ResourceNode> allResources = menuNodes.stream()
// .filter(n -> CollectionUtils.isNotEmpty(n.getResourceTree()))
// .flatMap(n -> flattenResourceTree(n.getResourceTree()).stream())
// .collect(Collectors.toList());
//
// if (CollectionUtils.isNotEmpty(allResources)) {
// Map<Long, List<ResourceNode>> resourceGroup = allResources.stream()
// .filter(r -> r.getResourceId() != null)
// .collect(Collectors.groupingBy(ResourceNode::getResourceId));
//
// List<ResourceNode> mergedResourceList = new ArrayList<>();
// for (Map.Entry<Long, List<ResourceNode>> re : resourceGroup.entrySet()) {
// Long resourceId = re.getKey();
// List<ResourceNode> resourceNodes = re.getValue();
// if (CollectionUtils.isEmpty(resourceNodes)) {
// continue;
// }
// ResourceNode mergedResource = new ResourceNode();
// mergedResource.setResourceId(resourceId);
// Integer mergedBindType = resourceNodes.stream()
// .map(ResourceNode::getResourceBindType)
// .filter(Objects::nonNull)
// .max(Comparator.comparingInt(UserMenuMergeUtil::bindTypeOrder))
// .orElse(BindTypeEnum.NONE.getCode());
// mergedResource.setResourceBindType(mergedBindType);
// mergedResourceList.add(mergedResource);
// }
//
// // 保存扁平化的资源列表(后续会重建资源树)
// // 先保存到resourceTree中后续会重建树形结构
// merged.setResourceTree(mergedResourceList);
// }
//
// result.add(merged);
// }
//
// return result;
// }
//
// /**
// * 传播菜单和资源的绑定类型,并过滤出有权限的菜单和资源
// *
// * @param mergedMenuNodes 合并后的菜单节点列表
// * @param allMenus 所有菜单列表
// * @param allResources 所有资源列表
// * @return 有权限的菜单和资源(已传播并过滤)
// */
// public static List<MenuNode> propagateAndFilterAuthorizedMenus(List<MenuNode> mergedMenuNodes,
// List<SysMenu> allMenus,
// List<SysResource> allResources) {
// if (CollectionUtils.isEmpty(mergedMenuNodes)) {
// return new ArrayList<>();
// }
//
// // 构建菜单ID到MenuNode的映射
// Map<Long, MenuNode> menuNodeMap = mergedMenuNodes.stream()
// .filter(n -> n.getMenuId() != null)
// .collect(Collectors.toMap(MenuNode::getMenuId, n -> n));
//
// // 构建菜单父子关系映射
// Map<Long, List<SysMenu>> menuChildrenMap = new HashMap<>();
// for (SysMenu menu : allMenus) {
// Long parentId = menu.getParentId();
// if (parentId != null && parentId != 0L) {
// menuChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(menu);
// }
// }
//
// // 构建资源父子关系映射
// Map<Long, List<SysResource>> resourceChildrenMap = new HashMap<>();
// Map<Long, SysResource> resourceMap = allResources.stream()
// .collect(Collectors.toMap(SysResource::getId, r -> r));
// for (SysResource resource : allResources) {
// Long parentId = resource.getParentId();
// if (parentId != null && parentId != 0L) {
// resourceChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(resource);
// }
// }
//
// // 1. 传播菜单绑定类型ALL/NONE的子菜单也传播
// Map<Long, Integer> menuBindTypeMap = new HashMap<>();
// Set<Long> processedMenuIds = new HashSet<>();
//
// // 先设置合并后的菜单绑定类型
// for (MenuNode menuNode : mergedMenuNodes) {
// if (menuNode.getMenuId() != null) {
// menuBindTypeMap.put(menuNode.getMenuId(), menuNode.getMenuBindType());
// }
// }
//
// // 对于ALL类型的菜单递归设置所有子菜单为ALL
// for (MenuNode menuNode : mergedMenuNodes) {
// if (menuNode.getMenuId() != null && BindTypeEnum.ALL.getCode().equals(menuNode.getMenuBindType())) {
// propagateMenuBindType(menuNode.getMenuId(), menuChildrenMap, menuBindTypeMap,
// processedMenuIds, BindTypeEnum.ALL.getCode());
// }
// }
//
// // 对于NONE类型的菜单递归设置所有子菜单为NONE
// for (MenuNode menuNode : mergedMenuNodes) {
// if (menuNode.getMenuId() != null && BindTypeEnum.NONE.getCode().equals(menuNode.getMenuBindType())) {
// propagateMenuBindType(menuNode.getMenuId(), menuChildrenMap, menuBindTypeMap,
// processedMenuIds, BindTypeEnum.NONE.getCode());
// }
// }
//
// // 2. 收集所有有权限的菜单ID包括传播后的子菜单
// Set<Long> authorizedMenuIds = new HashSet<>();
// for (Map.Entry<Long, Integer> entry : menuBindTypeMap.entrySet()) {
// Integer bindType = entry.getValue();
// if (!BindTypeEnum.NONE.getCode().equals(bindType)) {
// authorizedMenuIds.add(entry.getKey());
// }
// }
//
// // 3. 处理每个菜单的资源权限
// List<MenuNode> result = new ArrayList<>();
// for (Long menuId : authorizedMenuIds) {
// MenuNode menuNode = menuNodeMap.get(menuId);
//
// // 处理资源权限
// List<ResourceNode> authorizedResources = new ArrayList<>();
// if (menuNode != null && CollectionUtils.isNotEmpty(menuNode.getResourceTree())) {
// authorizedResources = processAuthorizedResources(
// menuNode, resourceMap, resourceChildrenMap);
// }
//
// // 只返回有权限的菜单menuBindType != NONE
// MenuNode authorizedMenu = new MenuNode();
// authorizedMenu.setMenuId(menuId);
// authorizedMenu.setResourceTree(authorizedResources); // 只包含有权限的资源resourceBindType != NONE
// result.add(authorizedMenu);
// }
//
// return result;
// }
//
// /**
// * 传播菜单绑定类型
// */
// private static void propagateMenuBindType(Long menuId, Map<Long, List<SysMenu>> menuChildrenMap,
// Map<Long, Integer> menuBindTypeMap, Set<Long> processedMenuIds,
// Integer bindType) {
// if (processedMenuIds.contains(menuId)) {
// return;
// }
// processedMenuIds.add(menuId);
//
// List<SysMenu> children = menuChildrenMap.get(menuId);
// if (CollectionUtils.isEmpty(children)) {
// return;
// }
//
// for (SysMenu child : children) {
// if (child.getId() == null) {
// continue;
// }
// // 强制设置子菜单的绑定类型
// menuBindTypeMap.put(child.getId(), bindType);
// // 递归处理子菜单的子菜单
// propagateMenuBindType(child.getId(), menuChildrenMap, menuBindTypeMap, processedMenuIds, bindType);
// }
// }
//
// /**
// * 处理菜单的资源权限,传播并过滤出有权限的资源
// */
// private static List<ResourceNode> processAuthorizedResources(MenuNode menuNode,
// Map<Long, SysResource> resourceMap,
// Map<Long, List<SysResource>> resourceChildrenMap) {
// if (CollectionUtils.isEmpty(menuNode.getResourceTree())) {
// return new ArrayList<>();
// }
//
// // 扁平化资源树(可能是树形结构或扁平列表)
// List<ResourceNode> flatResources = flattenResourceTree(menuNode.getResourceTree());
//
// // 构建资源绑定类型映射
// Map<Long, Integer> resourceBindTypeMap = flatResources.stream()
// .filter(r -> r.getResourceId() != null && r.getResourceBindType() != null)
// .collect(Collectors.toMap(ResourceNode::getResourceId, ResourceNode::getResourceBindType));
//
// // 传播资源绑定类型ALL/NONE的子资源也传播
// Set<Long> processedResourceIds = new HashSet<>();
// for (ResourceNode resourceNode : flatResources) {
// Long resourceId = resourceNode.getResourceId();
// Integer bindType = resourceNode.getResourceBindType();
// if (resourceId != null && bindType != null && !processedResourceIds.contains(resourceId)) {
// if (BindTypeEnum.ALL.getCode().equals(bindType) || BindTypeEnum.NONE.getCode().equals(bindType)) {
// propagateResourceBindType(resourceId, resourceChildrenMap, resourceBindTypeMap,
// processedResourceIds, bindType);
// }
// }
// }
//
// // 收集所有有权限的资源IDresourceBindType != NONE
// Set<Long> authorizedResourceIds = resourceBindTypeMap.entrySet().stream()
// .filter(e -> !BindTypeEnum.NONE.getCode().equals(e.getValue()))
// .map(Map.Entry::getKey)
// .collect(Collectors.toSet());
//
// if (CollectionUtils.isEmpty(authorizedResourceIds)) {
// return new ArrayList<>();
// }
//
// // 从所有资源中筛选出有权限的资源
// List<SysResource> authorizedResources = resourceMap.values().stream()
// .filter(r -> authorizedResourceIds.contains(r.getId()))
// .collect(Collectors.toList());
//
// // 重建资源树不包含resourceBindType字段
// return rebuildResourceTree(authorizedResources, resourceMap, resourceChildrenMap);
// }
//
// /**
// * 传播资源绑定类型
// */
// private static void propagateResourceBindType(Long resourceId,
// Map<Long, List<SysResource>> resourceChildrenMap,
// Map<Long, Integer> resourceBindTypeMap,
// Set<Long> processedResourceIds,
// Integer bindType) {
// if (processedResourceIds.contains(resourceId)) {
// return;
// }
// processedResourceIds.add(resourceId);
//
// List<SysResource> children = resourceChildrenMap.get(resourceId);
// if (CollectionUtils.isEmpty(children)) {
// return;
// }
//
// for (SysResource child : children) {
// if (child.getId() == null) {
// continue;
// }
// // 强制设置子资源的绑定类型
// resourceBindTypeMap.put(child.getId(), bindType);
// // 递归处理子资源的子资源
// propagateResourceBindType(child.getId(), resourceChildrenMap, resourceBindTypeMap,
// processedResourceIds, bindType);
// }
// }
//
// /**
// * 重建资源树只包含有权限的资源不设置resourceBindType字段
// */
// private static List<ResourceNode> rebuildResourceTree(List<SysResource> authorizedResources,
// Map<Long, SysResource> resourceMap,
// Map<Long, List<SysResource>> resourceChildrenMap) {
// if (CollectionUtils.isEmpty(authorizedResources)) {
// return new ArrayList<>();
// }
//
// // 构建资源ID到ResourceNode的映射不设置resourceBindType
// Map<Long, ResourceNode> nodeMap = new HashMap<>();
// for (SysResource resource : authorizedResources) {
// ResourceNode node = new ResourceNode();
// node.setResourceId(resource.getId());
// // 不设置resourceBindType因为用户查询只返回有权限的资源
// nodeMap.put(resource.getId(), node);
// }
//
// // 构建树形结构
// List<ResourceNode> rootNodes = new ArrayList<>();
// for (SysResource resource : authorizedResources) {
// ResourceNode current = nodeMap.get(resource.getId());
// Long parentId = resource.getParentId();
// if (parentId == null || parentId == 0L || !nodeMap.containsKey(parentId)) {
// // 根节点
// rootNodes.add(current);
// } else {
// // 子节点添加到父节点的children中
// ResourceNode parent = nodeMap.get(parentId);
// if (parent.getChildren() == null) {
// parent.setChildren(new ArrayList<>());
// }
// parent.getChildren().add(current);
// }
// }
//
// return rootNodes;
// }
//
// /**
// * 过滤掉完全没有权限的菜单:
// * - menuBindType=NONE
// * - 且所有资源 resourceBindType=NONE 或无资源
// */
// public static List<MenuNode> filterNoPermissionMenus(List<MenuNode> nodes) {
// if (CollectionUtils.isEmpty(nodes)) {
// return new ArrayList<>();
// }
//
// return nodes.stream()
// .filter(node -> {
// Integer menuBindType = node.getMenuBindType();
// if (!BindTypeEnum.NONE.getCode().equals(menuBindType)) {
// return true;
// }
// // menuBindType=NONE再看资源
// if (CollectionUtils.isEmpty(node.getFlattenResourceList())) {
// return false;
// }
// // 如果存在任意一个资源的绑定类型 != NONE则保留菜单
// return node.getFlattenResourceList().stream()
// .anyMatch(r -> !BindTypeEnum.NONE.getCode().equals(r.getResourceBindType()));
// })
// .collect(Collectors.toList());
// }
//
// /**
// * 绑定类型优先级NONE < PART < ALL
// */
// private static int bindTypeOrder(Integer bindType) {
// if (bindType == null) {
// return 0;
// }
// if (BindTypeEnum.NONE.getCode().equals(bindType)) {
// return 1;
// }
// if (BindTypeEnum.PART.getCode().equals(bindType)) {
// return 2;
// }
// if (BindTypeEnum.ALL.getCode().equals(bindType)) {
// return 3;
// }
// return 0;
// }
//}
//

View File

@@ -0,0 +1,18 @@
package com.xspaceagi.system.application.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.Set;
/**
* 用户有权限的菜单ID与资源ID集合用于按需加载避免全量查询
*/
@Data
@AllArgsConstructor
public class AuthorizedIds {
private final Set<Long> menuIds;
private final Set<Long> resourceIds;
}

View File

@@ -0,0 +1,29 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 分类创建请求DTO
*/
@Data
@Schema(description = "分类创建请求")
public class CategoryCreateDto {
@NotBlank(message = "Category name is required")
@Schema(description = "分类名称", required = true)
private String name;
@NotBlank(message = "Category code is required")
@Schema(description = "分类编码", required = true)
private String code;
@NotBlank(message = "Category type is required")
@Schema(description = "分类类型Agent、PageApp、Component", required = true)
private String type;
@Schema(description = "分类描述")
private String description;
}

View File

@@ -0,0 +1,29 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
* 分类更新请求DTO
*/
@Data
@Schema(description = "分类更新请求")
public class CategoryUpdateDto {
@NotNull(message = "ID is required")
@Schema(description = "分类ID", required = true)
private Long id;
@Schema(description = "分类名称")
private String name;
@Schema(description = "分类编码")
private String code;
@Schema(description = "分类类型Agent、PageApp、Component")
private String type;
@Schema(description = "分类描述")
private String description;
}

View File

@@ -0,0 +1,24 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EventDto<E> {
public static final String EVENT_TYPE_NEW_NOTIFY_MESSAGE = "new_notify_message";
public static final String EVENT_TYPE_REFRESH_CHAT_MESSAGE = "refresh_chat_message";
public static final String EVENT_TYPE_CHAT_FINISHED = "chat_finished";
@Schema(description = "事件类型由具体的业务定义和前端同步当前已定义的类型new_notify_message - 有新的通知消息refresh_chat_message - 会话消息列表需要刷新")
private String type;
@Schema(description = "事件内容,由具体的业务定义,和前端同步")
private E event;
}

View File

@@ -0,0 +1,25 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
@Data
@Schema(description = "批量翻译请求")
public class I18nBatchTranslateDto {
@NotEmpty(message = "The key list cannot be empty.")
@Schema(description = "待翻译的 fieldKey 列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> keys;
@NotBlank(message = "The source language cannot be empty.")
@Schema(description = "源语言,如 zh-CN", requiredMode = Schema.RequiredMode.REQUIRED)
private String sourceLang;
@NotBlank(message = "The target language cannot be empty.")
@Schema(description = "目标语言,如 en-US", requiredMode = Schema.RequiredMode.REQUIRED)
private String targetLang;
}

View File

@@ -0,0 +1,32 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class I18nConfigDto {
@Schema(description = "类型,系统 System业务数据 BizData暂时不用关注该字段", hidden = true)
private String type;
@Schema(description = "")
private String side;
@Schema(description = "业务模块", hidden = true)
private String module;
@Schema(description = "业务模块具体记录ID", hidden = true)
private String dataId;
@Schema(description = "具体语言中文zh-cn英文en-us")
private String lang;
@Schema(description = "")
private String key;
@Schema(description = "")
private String value;
@Schema(description = "备注")
private String remark;
}

View File

@@ -0,0 +1,30 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "多语言配置分页查询条件")
public class I18nConfigQueryDto {
@Schema(description = "端,如 Backend")
private String side;
@Schema(description = "语言,如 zh-cn")
private String lang;
@Schema(description = "业务模块,精确匹配")
private String module;
@Schema(description = "配置键 fieldKey模糊匹配")
private String key;
@Schema(description = "配置值 fieldValue模糊匹配")
private String value;
@Schema(description = "页码,从 1 开始", example = "1")
private Long pageNo;
@Schema(description = "每页条数", example = "20")
private Long pageSize;
}

View File

@@ -0,0 +1,19 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.application.dto.permission.export.I18nConfigRowExportDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 按查询条件全量导出的 JSON 结构(与列表查询条件一致,忽略分页字段)。
*/
@Data
@Schema(description = "多语言配置按条件导出")
public class I18nConfigQueryExportDto {
@Schema(description = "与 i18n-config-*.json 中单条结构一致camelCase")
private List<I18nConfigRowExportDto> configs = new ArrayList<>();
}

View File

@@ -0,0 +1,34 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.application.validation.ValidI18nLanguageTag;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.io.Serializable;
/**
* 语言创建请求 DTO
*/
@Data
@Schema(description = "语言创建请求")
public class I18nLangAddDto implements Serializable {
@NotBlank(message = "Language name is required")
@Schema(description = "语言名称,例如 简体中文", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@NotBlank(message = "Language tag is required")
@ValidI18nLanguageTag
@Schema(description = "语言标识BCP 47如 zh-CN、en-US", requiredMode = Schema.RequiredMode.REQUIRED)
private String lang;
@Schema(description = "语言状态0 停用1 启用", defaultValue = "1")
private Integer status = 1;
@Schema(description = "是否为默认语言0 否1 是", defaultValue = "0")
private Integer isDefault = 0;
@Schema(description = "排序,值越小越靠前", defaultValue = "0")
private Integer sort = 0;
}

View File

@@ -0,0 +1,41 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 语言信息 DTO
*/
@Data
@Schema(description = "语言信息")
public class I18nLangDto implements Serializable {
@Schema(description = "语言 ID")
private Long id;
@Schema(description = "语言名称,例如 简体中文")
private String name;
@Schema(description = "语言标识中文zh-cn英文en-us 等等")
private String lang;
@Schema(description = "语言状态0 停用1 启用")
private Integer status;
@Schema(description = "是否为默认语言0 否1 是")
private Integer isDefault;
@Schema(description = "排序,值越小越靠前")
private Integer sort;
@Schema(description = "更新时间")
private Date modified;
@Schema(description = "创建时间")
private Date created;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
* 语言排序更新请求 DTO
*/
@Data
@Schema(description = "语言排序更新请求")
public class I18nLangSortDto implements Serializable {
@NotNull(message = "Sort parameters are required")
@Schema(description = "排序参数key: 语言 ID, value: 排序值", requiredMode = Schema.RequiredMode.REQUIRED)
private Map<Long, Integer> sortMap;
}

View File

@@ -0,0 +1,31 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
/**
* 语言更新请求 DTO
*/
@Data
@Schema(description = "语言更新请求")
public class I18nLangUpdateDto implements Serializable {
@NotNull(message = "Language ID is required")
@Schema(description = "语言 ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "语言名称,例如 简体中文")
private String name;
@Schema(description = "语言状态0 停用1 启用")
private Integer status;
@Schema(description = "是否为默认语言0 否1 是")
private Integer isDefault;
@Schema(description = "排序,值越小越靠前")
private Integer sort;
}

View File

@@ -0,0 +1,44 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.NotifyMessageUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NotifyMessageDto implements Serializable {
@Schema(description = "消息ID")
private Long id;
@Schema(description = "消息内容")
private String content;
@Schema(description = "消息状态")
private NotifyMessageUser.ReadStatus readStatus;
@Schema(description = "发送者信息")
private Sender sender;
@Schema(description = "创建时间(消息发送时间)")
private Date created;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class Sender {
private Long userId;
private String userName;
private String nickName;
private String avatar;
}
}

View File

@@ -0,0 +1,22 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.NotifyMessageUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class NotifyMessageQueryDto implements Serializable {
@Schema(description = "用户ID", hidden = true)
private Long userId;
@Schema(description = "最后消息ID")
private Long lastId;
@Schema(description = "查询条数")
private Integer size;
@Schema(description = "消息状态")
private NotifyMessageUser.ReadStatus readStatus;
}

View File

@@ -0,0 +1,30 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.NotifyMessage;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SendNotifyMessageDto implements Serializable {
private Long tenantId;
@Schema(description = "发送者ID", hidden = true)
private Long senderId;
@Schema(description = "消息内容")
private String content;
@Schema(description = "消息类型, Broadcast时可以不传userIds")
private NotifyMessage.MessageScope scope;
@Schema(description = "接收者ID列表")
private List<Long> userIds;
}

View File

@@ -0,0 +1,64 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.Space;
import com.xspaceagi.system.infra.dao.entity.SpaceUser;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
@I18n(module = "UserSpace")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class SpaceDto implements Serializable {
@Schema(description = "空间ID")
private Long id;
@Schema(description = "租户ID")
private Long tenantId;
@Schema(description = "空间名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "Space name is required")
private String name;
@Schema(description = "空间描述")
private String description;
@Schema(description = "空间图标")
private String icon;
@I18nField(keyPrefix = true)
@Schema(description = "空间类型")
private Space.Type type;
@Schema(description = "创建者ID")
private Long creatorId;
@Schema(description = "创建者名称")
private String creatorName;
@Schema(description = "更新时间")
private Date modified;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "当前登录用户在空间的角色")
private SpaceUser.Role currentUserRole;
@Schema(description = "空间是否接收来自外部的发布")
private Integer receivePublish;
@Schema(description = "空间是否开启开发功能")
private Integer allowDevelop;
}

View File

@@ -0,0 +1,44 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.SpaceUser;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class SpaceUserDto implements Serializable {
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "用户姓名")
private String userName;
@Schema(description = "昵称")
private String nickName;
@Schema(description = "头像")
private String avatar;
@Schema(description = "空间ID")
private Long spaceId;
@Schema(description = "角色")
private SpaceUser.Role role;
@Schema(description = "更新时间")
private Date modified;
@Schema(description = "创建时间")
private Date created;
public String getUserName() {
return userName == null ? "" : userName;
}
public String getNickName() {
return nickName == null ? "" : nickName;
}
}

View File

@@ -0,0 +1,34 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Locale;
/**
* 语言白名单单项BCP 47 标识 + 基于 tag 的展示名JDK CLDR运行时生成
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "语言选项(标识 + 展示名)")
public class SupportedLocaleOptionDto implements Serializable {
@Schema(description = "BCP 47 语言标识,如 zh-CN、en-US")
private String tag;
@Schema(description = "根据 tag 自动生成的展示名")
private String name;
public static SupportedLocaleOptionDto fromTag(String tag) {
Locale locale = Locale.forLanguageTag(tag);
String normalizedTag = locale.toLanguageTag();
return new SupportedLocaleOptionDto(
normalizedTag,
locale.getDisplayName(locale)
);
}
}

View File

@@ -0,0 +1,32 @@
package com.xspaceagi.system.application.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class TenantAddDto {
@NotNull(message = "Tenant name is required")
private String name;
private String description;
private String logo;
@NotNull(message = "Tenant domain is required")
private String domain;
@NotNull(message = "Tenant admin username is required")
private String userName;
private String nickName;
@NotNull(message = "Tenant admin phone is required")
private String phone;
@NotNull(message = "Tenant admin email is required")
private String email;
@NotNull(message = "Tenant admin password is required")
private String password;
}

View File

@@ -0,0 +1,192 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.spec.annotation.I18n;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@I18n(module = "TenantConfig")
@Data
public class TenantConfigDto implements Serializable {
@Schema(description = "租户ID", hidden = true)
private Long tenantId;
@Schema(description = "站点名称")
private String siteName;
private String siteUrl;
@Schema(description = "站点配置地址", hidden = true)
private String siteConfigUrl;
@Schema(description = "站点描述")
private String siteDescription;
@Schema(description = "站点LOGO为空使用现有默认的")
private String siteLogo;
private String faviconUrl;
@Schema(description = "登录页文案")
private String loginPageText;
@Schema(description = "登录页副文案")
private String loginPageSubText;
@Schema(description = "广场Banner地址为空使用现有默认的")
private String squareBanner;
@Schema(description = "广场Banner文案标题")
private String squareBannerText;
@Schema(description = "广场Banner文案副标题")
private String squareBannerSubText;
@Schema(description = "广场Banner链接如果链接不为空点击跳转")
private String squareBannerLinkUrl;
@Schema(description = "开启注册, 0 关闭1 开启如果为0前端不展示注册入口")
private Integer openRegister;
@Schema(description = "默认会话总结模型", hidden = true)
private Long defaultSummaryModelId;
@Schema(description = "默认问题建议模型", hidden = true)
private Long defaultSuggestModelId;
@Schema(description = "默认会话模型")
private Long defaultChatModelId;
@Schema(description = "默认嵌入模型")
private Long defaultEmbedModelId;
@Schema(description = "默认知识库模型")
private Long defaultKnowledgeModelId;
@Schema(description = "默认站点问答型Agent")
private Long defaultAgentId;
@Schema(description = "默认站点任务型Agent")
private Long defaultTaskAgentId;
@Schema(description = "首页会话框下的推荐问题")
private List<String> homeRecommendQuestions;
@Schema(description = "默认站点Agent集群", hidden = true)
private List<Long> defaultAgentIds;
@Schema(description = "推荐Agent列表", hidden = true)
private List<Long> recommendAgentIds;
@Schema(description = "官方智能体配置")
private List<Long> officialAgentIds;
@Schema(description = "官方用户名")
private String officialUserName;
@Schema(description = "站点域名")
private List<String> domainNames;
@Schema(description = "最大上传文件大小,例如 100MB")
private String maxFileSize;
private String casClientHostUrl;
private String casValidateUrl;
private String casLoginUrl;
private Integer authType;
private Integer agentPublishAudit;
private Integer pluginPublishAudit;
private Integer workflowPublishAudit;
private Integer skillPublishAudit;
private List<String> userWhiteList;
private String codeSafeCheckPrompt;
private Integer openCodeSafeCheck;
private String globalSystemPrompt;
private String smtpHost;
private Integer smtpPort;
private String smtpUsername;
private String smtpPassword;
private String smsAccessKeyId;
private String smsAccessKeySecret;
private String smsSignName;
private String smsTemplateCode;
private Long authExpire;
private Integer openCaptcha;
private String captchaAccessKeyId;
private String captchaAccessKeySecret;
private String captchaPrefix;
private String captchaSceneId;
private String pageFooterText;
private Integer allowAgentTempChat;
private Integer allowAgentApi;
private Integer allowMcpExport;
private String version;
private String templateConfig;
private String homeSlogan;
@Schema(description = "默认编码模型")
private Long defaultCodingModelId;
@Schema(description = "默认视觉模型")
private Long defaultVisualModelId;
private String mpAppId;
private String mpAppSecret;
private String sandboxConfig;
private boolean enabledSandbox;
private Boolean supportCustomDomain;
private String userComputerDefaultSkillIds;
private String officialPluginIds;
private String officialWorkflowIds;
private String officialSkillIds;
@Schema(description = "收入分成比例")
private Double revenueRatio;
@Schema(description = "支付网关地址")
private String paymentGateway;
@Schema(description = "是否开启订阅模式")
private Integer enableSubscription;
@Schema(description = "积分兑换比例,比如 1000标识1块钱可以兑换1000积分")
private Integer creditExchangeRate;
@Schema(description = "积分兑换说明")
private String creditExchangeDesc;
@Schema(description = "是否开启注册积分赠送")
private Integer enableGiftCredit;
@Schema(description = "注册赠送积分数")
private Integer giftCreditAmount;
@Schema(description = "注册赠送积分有效期(天)")
private Integer giftCreditExpire;
@Schema(description = "是否开启每日登录赠送积分")
private Integer enableDailyGiftCredit;
@Schema(description = "每日登录赠送积分数")
private Integer dailyGiftCreditAmount;
private boolean isCommercialEdition;
@Schema(description = "模型API根地址")
private String baseModelApiUrl;
public enum AuthTypeEnum {
PHONE(1, "手机"),
CAS(2, "CAS"),
EMAIL(3, "邮箱");
private int code;
private String desc;
AuthTypeEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
}
}

View File

@@ -0,0 +1,50 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.TenantConfig;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@I18n(module = "TenantConfig")
@Data
public class TenantConfigItemDto implements Serializable {
private Long tenantId;
@I18nField(keyPrefix = true)
@Schema(description = "配置项名称")
private String name;
@Schema(description = "配置项值")
private Object value;
@Schema(description = "配置项描述")
private String description;
@Schema(description = "配置项分类")
private TenantConfig.ConfigCategory category;
@Schema(description = "配置项输入类型")
private TenantConfig.InputType inputType;
@Schema(description = "配置项数据类型")
private TenantConfig.DataType dataType;
@Schema(description = "配置项提示")
private String notice;
@Schema(description = "配置项占位符")
private String placeholder;
@Schema(description = "配置项最小高度")
private Integer minHeight;
@Schema(description = "是否必填")
private boolean required;
@Schema(description = "排序", hidden = true)
private Integer sort;
}

View File

@@ -0,0 +1,34 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.spec.enums.TenantStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@Data
public class TenantDto implements Serializable {
@Schema(description = "租户ID")
private Long id;
@Schema(description = "租户名称")
private String name;
@Schema(description = "租户描述")
private String description;
@Schema(description = "租户状态")
private TenantStatus status;
@Schema(description = "租户域名")
private String domain;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "配置信息")
private List<TenantConfigItemDto> tenantConfigs;
}

View File

@@ -0,0 +1,28 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.sdk.service.dto.UserAccessKeyDto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.List;
@Data
public class UpdateApiKeyDto {
@Schema(description = "用户ID", hidden = true)
private Long userId;
@Schema(description = "API Key")
private String accessKey;
@Schema(description = "状态 0 停用; 1 启用")
private Integer status;
@Schema(description = "名称")
private String name;
@Schema(description = "过期时间")
private Date expire;
@Schema(description = "接口调用频率限制,每分钟调用次数")
private List<UserAccessKeyDto.ApiConfig> apiConfigs;
@Schema(description = "已授权的模型ID列表")
private List<Long> modelIds;
}

View File

@@ -0,0 +1,46 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
public class UsageDto {
@Schema(description = "今日TOKEN使用情况")
private Usage todayTokenUsage;
@Schema(description = "今日智能体提示使用情况")
private Usage todayAgentPromptUsage;
@Schema(description = "今日网页应用提示使用情况")
private Usage todayPageAppPromptUsage;
@Schema(description = "可创建工作空间数量")
private Usage newWorkspaceUsage;
@Schema(description = "可创建智能体数量")
private Usage newAgentUsage;
@Schema(description = "可创建网页应用数量")
private Usage newPageAppUsage;
@Schema(description = "可创建知识库数量")
private Usage newKnowledgeBaseUsage;
@Schema(description = "知识库存储上限")
private Usage knowledgeBaseStorageUsage;
@Schema(description = "可创建数据表数量")
private Usage newTableUsage;
@Schema(description = "可创建定时任务数量")
private Usage newTaskUsage;
@Schema(description = "智能体电脑最大内存")
private Usage sandboxMemoryLimit;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public static class Usage {
@Schema(description = "限制")
private String limit;
@Schema(description = "使用情况")
private String usage;
@Schema(description = "描述")
private String description;
}
}

View File

@@ -0,0 +1,97 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.User;
import com.xspaceagi.system.spec.common.UserContext;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Date;
import java.util.Map;
import java.util.Objects;
@Schema(description = "用户信息")
@Data
public class UserDto {
@Schema(description = "用户ID")
private Long id;
@Schema(description = "用户唯一标识")
private String uid;
@Schema(description = "商户ID")
private Long tenantId;
@Schema(description = "用户名")
private String userName;
@Schema(description = "用户昵称")
private String nickName;
@Schema(description = "用户头像")
private String avatar;
@Schema(description = "用户密码")
private String password;
@Schema(description = "判断用户是否设置过密码,如果未设置过,需要弹出密码设置框让用户设置密码")
private Integer resetPass;
@Schema(description = "用户状态")
private User.Status status;
@Schema(description = "角色")
private User.Role role;
@Schema(description = "邮箱")
private String email;
@Schema(description = "手机号码")
private String phone;
@Schema(description = "最后登录时间")
private Date lastLoginTime;
@Schema(description = "最近的语言环境")
private String lang;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "更新时间")
private Date modified;
@Schema(description = "语言环境对应的值仅类型为System")
private Map<String, String> langMap;
/**
* 转换为对象 UserContext,用于上下文
*/
public static UserContext convertToUserContext(UserDto userDto) {
if (Objects.isNull(userDto)) {
return null;
}
int status = 1;
if (userDto.getStatus() != User.Status.Enabled) {
status = -1;
}
return UserContext.builder()
.userId(userDto.getId())
.uid(userDto.getUid())
.userName(userDto.getUserName())
.nickName(userDto.getNickName())
.email(userDto.getEmail())
.phone(userDto.getPhone())
.status(status)
.orgId(userDto.getTenantId())
.orgName(null)
.roleType(null)
.langMap(userDto.getLangMap())
.build();
}
}

View File

@@ -0,0 +1,39 @@
package com.xspaceagi.system.application.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
@Schema(description = "用户计量信息")
@Data
public class UserMetricDto {
@Schema(description = "计量ID")
private Long id;
@Schema(description = "租户ID")
private Long tenantId;
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "业务类型")
private String bizType;
@Schema(description = "时段类型")
private String periodType;
@Schema(description = "时段值")
private String period;
@Schema(description = "计量值")
private BigDecimal value;
@Schema(description = "修改时间")
private Date modified;
@Schema(description = "创建时间")
private Date created;
}

View File

@@ -0,0 +1,32 @@
package com.xspaceagi.system.application.dto;
import com.xspaceagi.system.infra.dao.entity.User;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class UserQueryDto implements Serializable {
@Schema(description = "用户ID")
private Long id;
@Schema(description = "用户姓名")
private String userName;
@Schema(description = "昵称")
private String nickName;
@Schema(description = "邮箱")
private String email;
@Schema(description = "手机号码")
private String phone;
@Schema(description = "用户状态")
private User.Status status;
@Schema(description = "角色")
private User.Role role;
}

View File

@@ -0,0 +1,26 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 绑定限制访问对象请求
*/
@Data
public class BindRestrictionTargetsDto implements Serializable {
@NotNull(message = "subjectId is required")
@Schema(description = "主体ID如模型ID、智能体ID、网页应用智能体ID")
private Long subjectId;
@Schema(description = "可访问的角色ID列表")
private List<Long> roleIds = new ArrayList<>();
@Schema(description = "可访问的用户组ID列表")
private List<Long> groupIds = new ArrayList<>();
}

View File

@@ -0,0 +1,125 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import com.xspaceagi.system.spec.enums.OpenTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import com.xspaceagi.system.spec.enums.ErrorCodeEnum;
import com.xspaceagi.system.spec.exception.BizException;
import com.xspaceagi.system.spec.exception.BizExceptionCodeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 菜单树节点
*/
@I18n(module = "PermissionMenu")
@Data
@Schema(description = "菜单树节点")
public class MenuNodeDto implements Serializable {
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "子菜单绑定类型 0:未绑定 1:全部绑定 2:部分绑定")
private Integer menuBindType;
@I18nField(subObj = true)
@Schema(description = "子菜单列表")
private List<MenuNodeDto> children;
@I18nField(subObj = true)
@Schema(description = "资源树")
private List<ResourceNodeDto> resourceTree;
@I18nField(subObj = true)
@Schema(description = "资源列表", hidden = true)
private List<ResourceNodeDto> resourceNodes;
@I18nField(keyPrefix = true)
@Schema(description = "资源码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
@Schema(description = "访问路径")
private String path;
/**
* @see OpenTypeEnum
*/
@Schema(description = "打开方式 1-当前标签页打开 2-新标签页打开")
private Integer openType;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
@Schema(description = "创建人")
private String creator;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "修改人ID")
private Long modifierId;
@Schema(description = "修改人")
private String modifier;
@Schema(description = "修改时间")
private Date modified;
/**
* 将菜单树扁平化为列表
*/
@Schema(hidden = true)
public static List<MenuNodeDto> flattenMenuTree(List<MenuNodeDto> nodes) {
if (CollectionUtils.isEmpty(nodes)) {
return new ArrayList<>();
}
List<MenuNodeDto> result = new ArrayList<>();
for (MenuNodeDto node : nodes) {
if (node.getId() == null) {
throw BizException.of(ErrorCodeEnum.INVALID_PARAM, BizExceptionCodeEnum.systemMenuIdNullWithNodeName, node.getName());
} else {
// 创建新节点不包含children
MenuNodeDto flatNode = new MenuNodeDto();
flatNode.setId(node.getId());
flatNode.setMenuBindType(node.getMenuBindType());
flatNode.setResourceTree(node.getResourceTree());
result.add(flatNode);
}
if (CollectionUtils.isNotEmpty(node.getChildren())) {
result.addAll(flattenMenuTree(node.getChildren()));
}
}
return result;
}
}

View File

@@ -0,0 +1,112 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import com.xspaceagi.system.spec.enums.ResourceTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@I18n(module = "PermissionResource")
@Data
@Schema(description = "资源树节点")
public class ResourceNodeDto implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "资源绑定类型 0:未绑定 1:全部绑定 2:部分绑定")
private Integer resourceBindType;
@I18nField(subObj = true)
@Schema(description = "子资源列表")
private List<ResourceNodeDto> children;
@I18nField(keyPrefix = true)
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
/**
* @see ResourceTypeEnum
*/
@Schema(description = "类型 1:模块 2:组件")
private Integer type;
@Schema(description = "访问路径")
private String path;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
@Schema(description = "创建人ID")
private Long creatorId;
@Schema(description = "创建人")
private String creator;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "修改人ID")
private Long modifierId;
@Schema(description = "修改人")
private String modifier;
@Schema(description = "修改时间")
private Date modified;
/**
* 资源树扁平化处理
*/
@Schema(hidden = true)
public static List<ResourceNodeDto> flattenResourceTree(List<ResourceNodeDto> nodes) {
if (CollectionUtils.isEmpty(nodes)) {
return new ArrayList<>();
}
List<ResourceNodeDto> result = new ArrayList<>();
for (ResourceNodeDto node : nodes) {
if (node.getId() != null) {
// 创建新节点不包含children
ResourceNodeDto flatNode = new ResourceNodeDto();
flatNode.setId(node.getId());
flatNode.setResourceBindType(node.getResourceBindType());
flatNode.setCode(node.getCode());
result.add(flatNode);
}
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
result.addAll(flattenResourceTree(node.getChildren()));
}
}
return result;
}
}

View File

@@ -0,0 +1,22 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 顺序调整项
*/
@Data
public class SortIndexDto implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "父级ID0表示根节点不传则不修改无层级则忽略")
private Long parentId;
@Schema(description = "排序索引,不传则不修改")
private Integer sortIndex;
}

View File

@@ -0,0 +1,17 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 顺序批量调整请求
*/
@Data
public class SortIndexUpdateDto implements Serializable {
@Schema(description = "待调整的列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<SortIndexDto> items;
}

View File

@@ -0,0 +1,23 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.infra.dao.entity.SysGroup;
import com.xspaceagi.system.infra.dao.entity.SysRole;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 主体访问权限的目标对象(角色/用户组)
*/
@Data
public class SubjectTargetsDto implements Serializable {
@Schema(description = "可访问的角色列表")
private List<SysRole> roles = new ArrayList<>();
@Schema(description = "可访问的用户组列表")
private List<SysGroup> groups = new ArrayList<>();
}

View File

@@ -0,0 +1,89 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.sdk.service.dto.TokenLimit;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
@Data
@Schema(description = "数据权限绑定")
public class SysDataPermissionBindDto implements Serializable {
@Schema(description = "可用的模型ID列表")
private List<Long> modelIds;
@Schema(description = "可访问的智能体id列表null或空表示不限制")
private List<Long> agentIds;
@Schema(description = "可访问的网页应用智能体id列表null或空表示不限制")
private List<Long> pageAgentIds;
@Schema(description = "可访问的开放api及访问频率配置")
private List<OpenApiConfig> openApiConfigs;
@Schema(description = "有权限访问的知识库id列表")
private List<Long> knowledgeIds;
@Schema(description = "token限制")
private TokenLimit tokenLimit;
@Schema(description = "可创建工作空间数量,-1表示不限制")
private Integer maxSpaceCount;
@Schema(description = "可创建智能体数量,-1表示不限制")
private Integer maxAgentCount;
@Schema(description = "可创建网页应用数量,-1表示不限制")
private Integer maxPageAppCount;
@Schema(description = "可创建知识库数量,-1表示不限制")
private Integer maxKnowledgeCount;
@Schema(description = "知识库存储空间上限(GB保留三位小数)-1表示不限制")
private BigDecimal knowledgeStorageLimitGb;
@Schema(description = "可创建数据表数量,-1表示不限制")
private Integer maxDataTableCount;
@Schema(description = "可创建定时任务数量,-1表示不限制")
private Integer maxScheduledTaskCount;
@Schema(description = "智能体电脑CPU核心数null表示使用默认值2")
private Integer agentComputerCpuCores;
@Schema(description = "智能体电脑内存(GB)null表示使用默认值4")
private Integer agentComputerMemoryGb;
@Schema(description = "智能体电脑交换分区(GB)null表示使用默认值8")
private Integer agentComputerSwapGb;
@Schema(description = "通用智能体执行结果文件存储天数,-1表示不限制")
private Integer agentFileStorageDays;
@Schema(description = "通用智能体每天对话次数,-1表示不限制")
private Integer agentDailyPromptLimit;
@Schema(description = "网页应用开发每天对话次数,-1表示不限制")
private Integer pageDailyPromptLimit;
// @Schema(description = "是否允许API外部调用1-允许0-不允许")
// private Integer allowApiExternalCall;
@Data
@Schema(description = "开放API权限配置")
public static class OpenApiConfig implements Serializable {
@Schema(description = "开放api key")
private String key;
@Schema(description = "接口调用频率限制,每分钟调用次数")
private Integer rpm;
@Schema(description = "接口调用频率限制,每天调用次数")
private Integer rpd;
}
}

View File

@@ -0,0 +1,31 @@
package com.xspaceagi.system.application.dto.permission;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class SysGroupAddDto implements Serializable {
@Schema(description = "编码(可选,如果不传则根据名称自动生成)")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
private Integer source;
@Schema(description = "状态,1:启用 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 用户组绑定数据权限 DTO
*/
@Data
@Schema(description = "用户组绑定数据权限")
public class SysGroupBindDataPermissionDto implements Serializable {
@Schema(description = "用户组ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long groupId;
@Schema(description = "数据权限配置")
private SysDataPermissionBindDto dataPermission;
}

View File

@@ -0,0 +1,26 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "用户组绑定菜单")
@Data
public class SysGroupBindMenuDto implements Serializable {
@Schema(description = "用户组ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long groupId;
@Schema(description = "菜单树", requiredMode = Schema.RequiredMode.REQUIRED)
private List<MenuNodeDto> menuTree;
/**
* 获取所有菜单节点扁平化列表包含菜单ID和资源绑定信息
*/
@Schema(hidden = true)
public List<MenuNodeDto> getAllMenuNodes() {
return MenuNodeDto.flattenMenuTree(menuTree);
}
}

View File

@@ -0,0 +1,19 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "用户组绑定用户")
@Data
public class SysGroupBindUserDto implements Serializable {
@Schema(description = "组ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long groupId;
@Schema(description = "用户ID")
private List<Long> userIds;
}

View File

@@ -0,0 +1,18 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Schema(description = "用户组绑定用户(单个)")
@Data
public class SysGroupBindUserSingleDto implements Serializable {
@Schema(description = "用户组ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long groupId;
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
}

View File

@@ -0,0 +1,71 @@
package com.xspaceagi.system.application.dto.permission;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
import com.xspaceagi.system.sdk.service.dto.TokenLimit;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@I18n(module = "PermissionGroup")
@Data
public class SysGroupDto implements Serializable {
@Schema(description = "ID")
private Long id;
@I18nField(keyPrefix = true)
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "来源,1:系统内置 2:用户自定义")
private Integer source;
@Schema(description = "状态,1:启用 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "创建人ID")
private Long creatorId;
@Schema(description = "创建人")
private String creator;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "修改人ID")
private Long modifierId;
@Schema(description = "修改人")
private String modifier;
@Schema(description = "修改时间")
private Date modified;
@Schema(description = "模型ID列表 全部模型传[-1],未选中任何模型不传值")
private List<Long> modelIds;
@Schema(description = "token限制")
private TokenLimit tokenLimit;
@I18nField(subObj = true)
@Schema(description = "子用户组列表")
private List<SysGroupDto> children;
}

View File

@@ -0,0 +1,23 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysGroupQueryDto implements Serializable {
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "来源,1:系统内置 2:用户自定义")
private Integer source;
@Schema(description = "状态,1:启用 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,34 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysGroupUpdateDto implements Serializable {
@Schema(description = "ID")
private Long id;
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
@Schema(description = "最大用户数")
private Integer maxUserCount;
@Schema(description = "来源,1:系统内置 2:用户自定义", hidden = true)
private Integer source;
@Schema(description = "状态,1:启用 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 根据用户组ID查询用户列表的查询条件
*/
@Data
@Schema(description = "用户组用户查询条件")
public class SysGroupUserQueryDto implements Serializable {
@Schema(description = "用户组ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long groupId;
@Schema(description = "用户名,可选,模糊查询昵称或用户名")
private String userName;
}

View File

@@ -0,0 +1,54 @@
package com.xspaceagi.system.application.dto.permission;
import java.io.Serializable;
import java.util.List;
import com.xspaceagi.system.spec.enums.OpenTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class SysMenuAddDto implements Serializable {
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "编码(可选,如果不传则根据名称自动生成)")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
@Schema(description = "访问路径")
private String path;
/**
* @see OpenTypeEnum
*/
@Schema(description = "打开方式 1-当前标签页打开 2-新标签页打开")
private Integer openType;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
@Schema(description = "资源树")
private List<ResourceNodeDto> resourceTree;
}

View File

@@ -0,0 +1,29 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* 菜单绑定资源请求
*/
@Schema(description = "菜单绑定资源")
@Data
public class SysMenuBindResourceDto implements Serializable {
@Schema(description = "菜单ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long menuId;
@Schema(description = "资源树")
private List<ResourceNodeDto> resourceTree;
/**
* 获取扁平化资源列表
*/
@Schema(hidden = true)
public List<ResourceNodeDto> getFlattenlResourceList() {
return ResourceNodeDto.flattenResourceTree(resourceTree);
}
}

View File

@@ -0,0 +1,37 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.OpenTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysMenuQueryDto implements Serializable {
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
/**
* @see OpenTypeEnum
*/
@Schema(description = "打开方式 1-当前标签页打开 2-新标签页打开")
private Integer openType;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,55 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.OpenTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class SysMenuUpdateDto implements Serializable {
@Schema(description = "ID")
private Long id;
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "资源码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
@Schema(description = "访问路径")
private String path;
/**
* @see OpenTypeEnum
*/
@Schema(description = "打开方式 1-当前标签页打开 2-新标签页打开")
private Integer openType;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
@Schema(description = "资源树")
private List<ResourceNodeDto> resourceTree;
}

View File

@@ -0,0 +1,50 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.ResourceTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "资源")
@Data
public class SysResourceAddDto {
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "编码(可选,如果不传则根据名称自动生成)")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
/**
* @see ResourceTypeEnum
*/
@Schema(description = "资源类型 1:模块 2:组件 3:页面")
private Integer type;
@Schema(description = "访问路径")
private String path;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,37 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.ResourceTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "资源")
@Data
public class SysResourceQueryDto {
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
/**
* @see ResourceTypeEnum
*/
@Schema(description = "资源类型 1:模块 2:组件 3:页面")
private Integer type;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,52 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.ResourceTypeEnum;
import com.xspaceagi.system.spec.enums.SourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "资源")
@Data
public class SysResourceUpdateDto {
@Schema(description = "ID")
private Long id;
@Schema(description = "父级ID")
private Long parentId;
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
/**
* @see ResourceTypeEnum
*/
@Schema(description = "资源类型 1:模块 2:组件 3:页面")
private Integer type;
@Schema(description = "访问路径")
private String path;
@Schema(description = "图标")
private String icon;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "状态 1:启用 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,37 @@
package com.xspaceagi.system.application.dto.permission;
import java.io.Serializable;
import com.xspaceagi.system.spec.enums.SourceEnum;
import com.xspaceagi.system.spec.enums.StatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class SysRoleAddDto implements Serializable {
@Schema(description = "编码(可选,如果不传则根据名称自动生成)")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
/**
* @see StatusEnum
*/
@Schema(description = "状态 1:启动 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 角色绑定数据权限 DTO
*/
@Data
@Schema(description = "角色绑定数据权限")
public class SysRoleBindDataPermissionDto implements Serializable {
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roleId;
@Schema(description = "数据权限配置")
private SysDataPermissionBindDto dataPermission;
}

View File

@@ -0,0 +1,26 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "角色菜单绑定")
@Data
public class SysRoleBindMenuDto implements Serializable {
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roleId;
@Schema(description = "菜单树形结构", requiredMode = Schema.RequiredMode.REQUIRED)
private List<MenuNodeDto> menuTree;
/**
* 获取扁平化菜单列表包含菜单ID和资源绑定信息
*/
@Schema(hidden = true)
public List<MenuNodeDto> getFlattenlMenuNodes() {
return MenuNodeDto.flattenMenuTree(menuTree);
}
}

View File

@@ -0,0 +1,19 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "角色绑定用户")
@Data
public class SysRoleBindUserDto implements Serializable {
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roleId;
@Schema(description = "用户ID")
private List<Long> userIds;
}

View File

@@ -0,0 +1,19 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "角色绑定用户")
@Data
public class SysRoleBindUserSingleDto implements Serializable {
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roleId;
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
}

View File

@@ -0,0 +1,70 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.annotation.I18n;
import com.xspaceagi.system.spec.annotation.I18nField;
import com.xspaceagi.system.spec.enums.SourceEnum;
import com.xspaceagi.system.spec.enums.StatusEnum;
import com.xspaceagi.system.sdk.service.dto.TokenLimit;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
@I18n(module = "PermissionRole")
@Data
public class SysRoleDto implements Serializable {
@Schema(description = "ID")
private Long id;
@I18nField(keyPrefix = true)
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
/**
* @see StatusEnum
*/
@Schema(description = "状态 1:启动 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
@Schema(description = "模型ID列表 全部模型传[-1],未选中任何模型不传值")
private List<Long> modelIds;
@Schema(description = "token限制")
private TokenLimit tokenLimit;
@Schema(description = "创建人ID")
private Long creatorId;
@Schema(description = "创建人")
private String creator;
@Schema(description = "创建时间")
private Date created;
@Schema(description = "修改人ID")
private Long modifierId;
@Schema(description = "修改人")
private String modifier;
@Schema(description = "修改时间")
private Date modified;
}

View File

@@ -0,0 +1,31 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.SourceEnum;
import com.xspaceagi.system.spec.enums.StatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysRoleQueryDto implements Serializable {
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义")
private Integer source;
/**
* @see StatusEnum
*/
@Schema(description = "状态 1:启动 0:禁用")
private Integer status;
}

View File

@@ -0,0 +1,39 @@
package com.xspaceagi.system.application.dto.permission;
import com.xspaceagi.system.spec.enums.SourceEnum;
import com.xspaceagi.system.spec.enums.StatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
@Data
public class SysRoleUpdateDto implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "编码")
private String code;
@Schema(description = "名称")
private String name;
@Schema(description = "描述")
private String description;
/**
* @see SourceEnum
*/
@Schema(description = "来源 1:系统内置 2:用户自定义", hidden = true)
private Integer source;
/**
* @see StatusEnum
*/
@Schema(description = "状态 1:启动 0:禁用")
private Integer status;
@Schema(description = "排序")
private Integer sortIndex;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
/**
* 根据角色ID查询用户列表的查询条件
*/
@Data
@Schema(description = "角色用户查询条件")
public class SysRoleUserQueryDto implements Serializable {
@Schema(description = "角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roleId;
@Schema(description = "用户名,可选,模糊查询昵称或用户名")
private String userName;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "用户绑定用户组")
@Data
public class SysUserBindGroupDto implements Serializable {
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
@Schema(description = "组ID")
private List<Long> groupIds;
}

View File

@@ -0,0 +1,20 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Schema(description = "用户绑定角色")
@Data
public class SysUserBindRoleDto implements Serializable {
@Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long userId;
@Schema(description = "角色ID")
private List<Long> roleIds;
}

View File

@@ -0,0 +1,22 @@
package com.xspaceagi.system.application.dto.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "用户信息")
@Data
public class SysUserDto {
@Schema(description = "用户ID")
private Long userId;
@Schema(description = "用户名")
private String userName;
@Schema(description = "用户昵称")
private String nickName;
@Schema(description = "用户头像")
private String avatar;
}

View File

@@ -0,0 +1,30 @@
package com.xspaceagi.system.application.dto.permission.export;
import com.xspaceagi.system.sdk.service.dto.TokenLimit;
import lombok.Data;
import java.math.BigDecimal;
/**
* 数据权限导出DTOtargetCode表示角色或用户组code
*/
@Data
public class DataPermissionExportDto {
private Integer targetType;
private String targetCode;
private TokenLimit tokenLimit;
private Integer maxSpaceCount;
private Integer maxAgentCount;
private Integer maxPageAppCount;
private Integer maxKnowledgeCount;
private BigDecimal knowledgeStorageLimitGb;
private Integer maxDataTableCount;
private Integer maxScheduledTaskCount;
private Integer agentComputerCpuCores;
private Integer agentComputerMemoryGb;
private Integer agentComputerSwapGb;
private Integer agentFileStorageDays;
private Integer agentDailyPromptLimit;
private Integer pageDailyPromptLimit;
}

View File

@@ -0,0 +1,18 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
/**
* 用户组导出DTOcode关联无id
*/
@Data
public class GroupExportDto {
private String code;
private String name;
private String description;
private Integer maxUserCount;
private Integer source;
private Integer status;
private Integer sortIndex;
}

View File

@@ -0,0 +1,21 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.List;
/**
* 用户组菜单关联导出DTOcode关联
*/
@Data
public class GroupMenuExportDto {
private String groupCode;
private String menuCode;
private Integer menuBindType;
/**
* 资源树code版用于导入时根据目标租户的resource code->id 映射重写
*/
private List<ResourceNodeExportDto> resourceTree;
}

View File

@@ -0,0 +1,17 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* i18n 配置版本比对结果
*/
@Data
public class I18nConfigDiffSplitDto {
private List<I18nConfigRowExportDto> addRows = new ArrayList<>();
private List<I18nConfigRowExportDto> updateRows = new ArrayList<>();
}

View File

@@ -0,0 +1,18 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class I18nConfigExportDto {
/**
* 版本号与app.version对应如 1.0.5
*/
private String version;
private List<I18nConfigRowExportDto> configs = new ArrayList<>();
}

View File

@@ -0,0 +1,28 @@
package com.xspaceagi.system.application.dto.permission.export;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;
/**
* 与 resources/i18n/i18n-config-*.json 中单条结构一致fieldKey、fieldValue、dataId 等 camelCase
*/
@Data
@JsonInclude(JsonInclude.Include.ALWAYS)
public class I18nConfigRowExportDto {
private String type;
private String side;
private String module;
private String dataId;
private String lang;
private String fieldKey;
private String fieldValue;
private String remark;
}

View File

@@ -0,0 +1,21 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
/**
* 菜单导出DTOcode关联无id
*/
@Data
public class MenuExportDto {
private String parentCode;
private String code;
private String name;
private String description;
private Integer source;
private String path;
private Integer openType;
private String icon;
private Integer sortIndex;
private Integer status;
}

View File

@@ -0,0 +1,14 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
/**
* 菜单资源关联导出DTOcode关联
*/
@Data
public class MenuResourceExportDto {
private String menuCode;
private String resourceCode;
private Integer resourceBindType;
}

View File

@@ -0,0 +1,27 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 权限数据导出根DTO版本化管理随代码发布
*/
@Data
public class PermissionExportDto {
/**
* 版本号与app.version对应如 1.0.5
*/
private String version;
private List<ResourceExportDto> resources = new ArrayList<>();
private List<MenuExportDto> menus = new ArrayList<>();
private List<RoleExportDto> roles = new ArrayList<>();
private List<GroupExportDto> groups = new ArrayList<>();
private List<MenuResourceExportDto> menuResources = new ArrayList<>();
private List<RoleMenuExportDto> roleMenus = new ArrayList<>();
private List<GroupMenuExportDto> groupMenus = new ArrayList<>();
private List<DataPermissionExportDto> dataPermissions = new ArrayList<>();
}

View File

@@ -0,0 +1,21 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
/**
* 资源导出DTOcode关联无id
*/
@Data
public class ResourceExportDto {
private String code;
private String name;
private String description;
private Integer source;
private Integer type;
private String parentCode;
private String path;
private String icon;
private Integer sortIndex;
private Integer status;
}

View File

@@ -0,0 +1,16 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.List;
/**
* 资源树节点导出DTO使用code替代id用于resource_tree_json
*/
@Data
public class ResourceNodeExportDto {
private String code;
private Integer resourceBindType;
private List<ResourceNodeExportDto> children;
}

View File

@@ -0,0 +1,17 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
/**
* 角色导出DTOcode关联无id
*/
@Data
public class RoleExportDto {
private String code;
private String name;
private String description;
private Integer source;
private Integer status;
private Integer sortIndex;
}

View File

@@ -0,0 +1,21 @@
package com.xspaceagi.system.application.dto.permission.export;
import lombok.Data;
import java.util.List;
/**
* 角色菜单关联导出DTOcode关联
*/
@Data
public class RoleMenuExportDto {
private String roleCode;
private String menuCode;
private Integer menuBindType;
/**
* 资源树code版用于导入时根据目标租户的resource code->id 映射重写
*/
private List<ResourceNodeExportDto> resourceTree;
}

View File

@@ -0,0 +1,4 @@
/**
* Application layer.
*/
package com.xspaceagi.system.application;

View File

@@ -0,0 +1,37 @@
package com.xspaceagi.system.application.service;
import com.xspaceagi.system.application.dto.UserDto;
import java.util.List;
public interface AuthService {
String createToken(UserDto userDto, String clientId);
String loginWithCode(String emailOrPhone, String code);
String loginWithMpCode(String code);
String loginWithPassword(String emailOrPhone, String password);
UserDto getLoginUserInfo(String token);
String refreshToken(String token);
void expireToken(String token);
void renewToken(String token);
void expireUserAllToken(Long userId);
void expireUserAllToken(Long userId, String clientIdPrefix);
String getClientId(Long userId, String token);
List<String> getUserClientIds(Long userId);
String newTicket(UserDto userDto, String token);
String getTokenByTicket(String ticket);
}

View File

@@ -0,0 +1,53 @@
package com.xspaceagi.system.application.service;
import java.util.List;
import com.xspaceagi.system.infra.dao.entity.Category;
import com.xspaceagi.system.spec.common.UserContext;
import com.xspaceagi.system.spec.dto.ReqResult;
/**
* 分类管理应用服务
*/
public interface CategoryApplicationService {
/**
* 创建分类
*/
ReqResult<Category> create(Category category, UserContext userContext);
/**
* 更新分类
*/
ReqResult<Category> update(Category category, UserContext userContext);
/**
* 删除分类
*/
ReqResult<Void> delete(Long id, UserContext userContext);
/**
* 根据ID查询分类
*/
Category getById(Long id);
/**
* 根据类型查询分类列表
*/
List<Category> listByType(String type);
/**
* 根据租户ID查询分类列表
*/
List<Category> listByTenantId(Long tenantId);
/**
* 根据类型和租户ID查询分类列表
*/
List<Category> listByTypeAndTenantId(String type, Long tenantId);
/**
* 根据编码查询分类
*/
Category getByCode(String code);
}

View File

@@ -0,0 +1,36 @@
package com.xspaceagi.system.application.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.xspaceagi.system.application.dto.I18nConfigDto;
import com.xspaceagi.system.application.dto.I18nConfigQueryDto;
import com.xspaceagi.system.application.dto.I18nConfigQueryExportDto;
import java.util.List;
import java.util.Map;
public interface I18nApplicationService {
List<I18nConfigDto> queryI18nConfigList(Long tenantId, String type, String side, String module, String dataId, String lang);
IPage<I18nConfigDto> queryI18nConfigPage(Long tenantId, I18nConfigQueryDto query);
I18nConfigQueryExportDto exportI18nConfig(Long tenantId, I18nConfigQueryDto query);
Map<String, String> querySystemLangMap(Long tenantId, String side, String lang);
void addOrUpdateI18nConfig(I18nConfigDto I18nConfigDto);
void batchAddOrUpdateI18nConfig(List<I18nConfigDto> I18nConfigDtos);
void batchDeleteI18nConfig(List<I18nConfigDto> i18nConfigDtos);
void translateForKey(Long tenantId, I18nConfigDto i18nConfigDto, String sourceLang, String targetLang);
void translateForKeysBatch(Long tenantId, List<I18nConfigDto> sourceItems, String sourceLang, String targetLang);
/**
* 按 key 列表从源语言翻译到目标语言并写入目标语言配置
*/
void translateForKeys(Long tenantId, List<String> keys, String sourceLang, String targetLang);
}

View File

@@ -0,0 +1,14 @@
package com.xspaceagi.system.application.service;
import com.xspaceagi.system.application.dto.permission.export.I18nConfigDiffSplitDto;
/**
* i18n 配置差异比对服务
*/
public interface I18nConfigDiffService {
/**
* 比对两个版本的 i18n 配置 JSONfromVersion → toVersion分别输出新增行与变更行。
*/
I18nConfigDiffSplitDto generateDiff(String fromVersion, String toVersion);
}

View File

@@ -0,0 +1,13 @@
package com.xspaceagi.system.application.service;
import com.xspaceagi.system.application.dto.permission.export.I18nConfigExportDto;
import java.util.List;
public interface I18nExportService {
/**
* 从默认租户tenant_id=1导出 i18n 配置。
*/
I18nConfigExportDto exportConfig(String version, List<String> langs);
}

View File

@@ -0,0 +1,30 @@
package com.xspaceagi.system.application.service;
import com.xspaceagi.system.infra.dao.entity.Tenant;
/**
* 权限数据导入服务
*/
public interface I18nImportService {
/**
* 将指定版本的配置项导入到目标租户
*/
void importConfigToTenant(Tenant tenant, String version);
/**
* 将指定版本的语言包导入到目标租户
*/
void importLangToTenant(Tenant tenant, String version);
/**
* 将指定版本的新增差异配置导入到目标租户(仅插入,已存在则跳过)
*/
void addConfigToTenant(Tenant tenant, String version);
/**
* 将指定版本的变更差异配置更新到目标租户(仅更新,不存在则跳过)
*/
void updateConfigToTenant(Tenant tenant, String version);
}

View File

@@ -0,0 +1,71 @@
package com.xspaceagi.system.application.service;
import com.xspaceagi.system.application.dto.I18nLangAddDto;
import com.xspaceagi.system.application.dto.I18nLangDto;
import com.xspaceagi.system.application.dto.I18nLangUpdateDto;
import java.util.List;
/**
* 语言应用服务
*/
public interface I18nLangApplicationService {
/**
* 新增语言
*
* @param addDto 语言信息
* @return 语言 ID
*/
Long add(I18nLangAddDto addDto);
/**
* 删除语言
*
* @param id 语言 ID
*/
void delete(Long id);
/**
* 更新语言
*
* @param updateDto 语言信息
*/
void update(I18nLangUpdateDto updateDto);
/**
* 设置为默认语言
*
* @param id 语言 ID
*/
void setDefault(Long id);
/**
* 查询全部语言
*
* @return 语言列表
*/
List<I18nLangDto> queryAll();
/**
* 批量更新排序
*
* @param sortList key: 语言 ID, value: 排序值
*/
void updateSort(List<I18nLangDto> sortList);
/**
* 根据 ID 查询语言
*
* @param id 语言 ID
* @return 语言信息
*/
I18nLangDto queryById(Long id);
/**
* 获取默认语言
*
* @return 默认语言信息
*/
I18nLangDto getDefault(Long tenantId);
}

View File

@@ -0,0 +1,13 @@
package com.xspaceagi.system.application.service;
import java.util.Map;
/**
* 使用租户默认对话模型将翻译
*/
public interface I18nLlmTranslator {
String translate(String text, String sourceLangTag, String targetLangTag);
Map<String, String> translateBatch(Map<String, String> textByKey, String sourceLangTag, String targetLangTag);
}

Some files were not shown because too many files have changed in this diff Show More