mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 20:08:18 +08:00
fix(workspace): harden workspace create and delete
This commit is contained in:
parent
b2f9976f44
commit
0a57cb3358
@ -9,6 +9,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import vip.mate.exception.MateClawException;
|
import vip.mate.exception.MateClawException;
|
||||||
import vip.mate.i18n.I18nService;
|
import vip.mate.i18n.I18nService;
|
||||||
|
import vip.mate.wiki.service.WikiKnowledgeBaseService;
|
||||||
import vip.mate.workspace.conversation.model.ConversationEntity;
|
import vip.mate.workspace.conversation.model.ConversationEntity;
|
||||||
import vip.mate.workspace.conversation.repository.ConversationMapper;
|
import vip.mate.workspace.conversation.repository.ConversationMapper;
|
||||||
import vip.mate.workspace.core.model.WorkspaceAccessVO;
|
import vip.mate.workspace.core.model.WorkspaceAccessVO;
|
||||||
@ -39,6 +40,7 @@ public class WorkspaceService {
|
|||||||
private final WorkspaceMapper workspaceMapper;
|
private final WorkspaceMapper workspaceMapper;
|
||||||
private final WorkspaceMemberMapper memberMapper;
|
private final WorkspaceMemberMapper memberMapper;
|
||||||
private final ConversationMapper conversationMapper;
|
private final ConversationMapper conversationMapper;
|
||||||
|
private final WikiKnowledgeBaseService wikiKnowledgeBaseService;
|
||||||
private final I18nService i18n;
|
private final I18nService i18n;
|
||||||
|
|
||||||
/** 默认工作区 slug */
|
/** 默认工作区 slug */
|
||||||
@ -134,8 +136,34 @@ public class WorkspaceService {
|
|||||||
.eq(WorkspaceEntity::getSlug, slug));
|
.eq(WorkspaceEntity::getSlug, slug));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive a URL-safe, unique slug from a workspace name. Non-alphanumeric
|
||||||
|
* runs collapse to a single hyphen; a name with no ASCII alphanumerics
|
||||||
|
* (e.g. a purely Chinese name) falls back to a generic base. A numeric
|
||||||
|
* suffix is appended until the slug is free.
|
||||||
|
*/
|
||||||
|
private String generateUniqueSlug(String name) {
|
||||||
|
String base = (name == null ? "" : name.toLowerCase())
|
||||||
|
.replaceAll("[^a-z0-9]+", "-")
|
||||||
|
.replaceAll("^-|-$", "");
|
||||||
|
if (base.isBlank()) {
|
||||||
|
base = "workspace";
|
||||||
|
}
|
||||||
|
String slug = base;
|
||||||
|
int n = 1;
|
||||||
|
while (getBySlug(slug) != null) {
|
||||||
|
slug = base + "-" + (++n);
|
||||||
|
}
|
||||||
|
return slug;
|
||||||
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public WorkspaceEntity create(WorkspaceEntity entity, Long creatorUserId) {
|
public WorkspaceEntity create(WorkspaceEntity entity, Long creatorUserId) {
|
||||||
|
// Auto-derive a slug from the name when the caller did not supply one,
|
||||||
|
// so workspace creation never fails on the NOT NULL slug column.
|
||||||
|
if (entity.getSlug() == null || entity.getSlug().isBlank()) {
|
||||||
|
entity.setSlug(generateUniqueSlug(entity.getName()));
|
||||||
|
}
|
||||||
// 验证 slug 唯一
|
// 验证 slug 唯一
|
||||||
if (getBySlug(entity.getSlug()) != null) {
|
if (getBySlug(entity.getSlug()) != null) {
|
||||||
throw new MateClawException("err.workspace.slug_exists", "工作区标识已存在: " + entity.getSlug());
|
throw new MateClawException("err.workspace.slug_exists", "工作区标识已存在: " + entity.getSlug());
|
||||||
@ -206,6 +234,14 @@ public class WorkspaceService {
|
|||||||
if (DEFAULT_SLUG.equals(existing.getSlug())) {
|
if (DEFAULT_SLUG.equals(existing.getSlug())) {
|
||||||
throw new MateClawException("err.workspace.cannot_delete_default", "不能删除默认工作区");
|
throw new MateClawException("err.workspace.cannot_delete_default", "不能删除默认工作区");
|
||||||
}
|
}
|
||||||
|
// Refuse to delete a workspace that still owns wiki knowledge bases —
|
||||||
|
// dropping the workspace row would orphan them. The caller must delete
|
||||||
|
// the knowledge bases first.
|
||||||
|
int kbCount = wikiKnowledgeBaseService.listByWorkspace(id).size();
|
||||||
|
if (kbCount > 0) {
|
||||||
|
throw new MateClawException("err.workspace.not_empty", 409,
|
||||||
|
"工作区下还有 " + kbCount + " 个知识库,请先删除知识库再删除工作区");
|
||||||
|
}
|
||||||
workspaceMapper.deleteById(id);
|
workspaceMapper.deleteById(id);
|
||||||
log.info("Deleted workspace: {} (id={})", existing.getName(), id);
|
log.info("Deleted workspace: {} (id={})", existing.getName(), id);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -173,6 +173,7 @@ err.workspace.not_member=\u7528\u6237\u4e0d\u662f\u8be5\u5de5\u4f5c\u533a\u7684\
|
|||||||
err.workspace.cannot_modify_owner=\u4e0d\u80fd\u4fee\u6539\u5de5\u4f5c\u533a\u62e5\u6709\u8005\u7684\u89d2\u8272
|
err.workspace.cannot_modify_owner=\u4e0d\u80fd\u4fee\u6539\u5de5\u4f5c\u533a\u62e5\u6709\u8005\u7684\u89d2\u8272
|
||||||
err.workspace.cannot_remove_owner=\u4e0d\u80fd\u79fb\u9664\u5de5\u4f5c\u533a\u62e5\u6709\u8005
|
err.workspace.cannot_remove_owner=\u4e0d\u80fd\u79fb\u9664\u5de5\u4f5c\u533a\u62e5\u6709\u8005
|
||||||
err.workspace.insufficient_permission=\u6743\u9650\u4e0d\u8db3
|
err.workspace.insufficient_permission=\u6743\u9650\u4e0d\u8db3
|
||||||
|
err.workspace.not_empty=\u5de5\u4f5c\u533a\u4e0b\u8fd8\u6709\u77e5\u8bc6\u5e93\uff0c\u8bf7\u5148\u5220\u9664\u77e5\u8bc6\u5e93\u540e\u518d\u5220\u9664\u5de5\u4f5c\u533a
|
||||||
err.channel.not_found=\u6e20\u9053\u4e0d\u5b58\u5728
|
err.channel.not_found=\u6e20\u9053\u4e0d\u5b58\u5728
|
||||||
err.channel.name_required=\u6e20\u9053\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a
|
err.channel.name_required=\u6e20\u9053\u540d\u79f0\u4e0d\u80fd\u4e3a\u7a7a
|
||||||
err.channel.type_required=\u6e20\u9053\u7c7b\u578b\u4e0d\u80fd\u4e3a\u7a7a
|
err.channel.type_required=\u6e20\u9053\u7c7b\u578b\u4e0d\u80fd\u4e3a\u7a7a
|
||||||
|
|||||||
@ -180,6 +180,7 @@ err.workspace.not_member=User is not a member of this workspace
|
|||||||
err.workspace.cannot_modify_owner=Cannot modify workspace owner role
|
err.workspace.cannot_modify_owner=Cannot modify workspace owner role
|
||||||
err.workspace.cannot_remove_owner=Cannot remove workspace owner
|
err.workspace.cannot_remove_owner=Cannot remove workspace owner
|
||||||
err.workspace.insufficient_permission=Insufficient permission
|
err.workspace.insufficient_permission=Insufficient permission
|
||||||
|
err.workspace.not_empty=Workspace still contains knowledge bases; delete them first
|
||||||
# channel
|
# channel
|
||||||
err.channel.not_found=Channel not found
|
err.channel.not_found=Channel not found
|
||||||
err.channel.name_required=Channel name cannot be empty
|
err.channel.name_required=Channel name cannot be empty
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user