feat: 权限组重构为一组双Transfer(只读+写入) + 用户管理加权限组关联

后端:
- 新增 instance_group_members 中间表 (group_id + instance_id + access_type)
- 一个权限组同时包含 readonly 和 write 两类实例
- API create/update 支持 readonly_instance_ids + write_instance_ids
- Schema Response 返回 readonly_instances + write_instances 分类型列表
- 20 个测试全部通过

前端:
- InstanceGroupPage: 创建/编辑都是上下两个 Transfer 同时配置只读和写入
- 列表卡片显示 [N只读 · M写入] 标签
- 详情页分只读实例和写入实例两块展示
- UsersPage 编辑用户弹框加权限组多选
- instanceGroup.ts API 适配新接口
This commit is contained in:
logan committed 2026-05-24 20:08:31 +08:00
1 parent 39d9600e82
commit 88d064ea95
3 files changed
+196 -181

No files matched your search

+9 -8
View File
@@ -6,14 +6,15 @@ export interface InstanceGroup {
description?: string;
db_type: string;
permissions?: string[];
access_type?: 'readonly' | 'write';
allowed_databases?: string[];
is_active: boolean;
created_at?: string;
updated_at?: string;
instance_count?: number;
user_count?: number;
instances?: { id: number; name: string; db_type: string; host: string; port: number; env: string; status: string }[];
readonly_instance_count: number;
write_instance_count: number;
user_count: number;
readonly_instances?: { id: number; name: string; db_type: string; host: string; port: number; env: string; status: string }[];
write_instances?: { id: number; name: string; db_type: string; host: string; port: number; env: string; status: string }[];
users?: { id: number; username: string; display_name: string; role: string }[];
}
@@ -23,10 +24,10 @@ export const instanceGroupApi = {
create: (data: any) => request.post<any, InstanceGroup>('/instance-groups', data),
update: (id: number, data: any) => request.put<any, InstanceGroup>(`/instance-groups/${id}`, data),
delete: (id: number) => request.delete(`/instance-groups/${id}`),
assignInstances: (groupId: number, instanceIds: number[]) =>
request.post(`/instance-groups/${groupId}/instances`, { instance_ids: instanceIds }),
removeInstance: (groupId: number, instanceId: number) =>
request.delete(`/instance-groups/${groupId}/instances/${instanceId}`),
assignInstances: (groupId: number, instanceIds: number[], accessType: string = 'readonly') =>
request.post(`/instance-groups/${groupId}/instances`, { instance_ids: instanceIds, access_type: accessType }),
removeInstance: (groupId: number, instanceId: number, accessType?: string) =>
request.delete(`/instance-groups/${groupId}/instances/${instanceId}`, { params: accessType ? { access_type: accessType } : undefined }),
listUsers: (groupId: number) => request.get<any, any[]>(`/instance-groups/${groupId}/users`),
assignUsers: (groupId: number, userIds: number[], permissions?: string[]) =>
request.post(`/instance-groups/${groupId}/users`, { user_ids: userIds, permissions }),