refactor: 权限组从一组一类型改为一组同时包含readonly+write实例

- 新增 instance_group_members 中间表关联权限组和实例
- InstanceGroup 模型移除旧的 instances relationship,改为通过中间表查询
- Instance 模型保留 group_id FK(兼容)但移除 group relationship
- Schema 新增 readonly_instance_ids / write_instance_ids 字段
- InstanceGroupResponse 改为 readonly_instance_count + write_instance_count
- InstanceGroupDetailResponse 返回 readonly_instances + write_instances
- InstanceAssignRequest 新增 access_type 参数
- API create/update 支持同步写入 instance_group_members
- role_permissions 服务更新为使用中间表查询
- 新增迁移 SQL 并应用到 sqlite3.db
- 测试全部通过 (20/20)
This commit is contained in:
logan committed 2026-05-24 20:03:55 +08:00
1 parent 40868c61bf
commit 39d9600e82
8 files changed
+418 -97

No files matched your search

+1 -1
View File
@@ -1,3 +1,3 @@
from app.models.instance_group import InstanceGroup, user_groups # noqa: F401
from app.models.instance_group import InstanceGroup, user_groups, instance_group_members # noqa: F401
from app.models.system_setting import SystemSetting # noqa: F401
from app.models.query_history import QueryHistory, QuickQueryModel # noqa: F401
+2 -3
View File
@@ -26,12 +26,11 @@ class Instance(Base):
excluded_databases = Column(Text, nullable=True) # JSON: ["sys", "information_schema", ...]
mask_fields = Column(Text, nullable=True) # JSON: [{table, field, rule}]
# 权限组关联
# 权限组关联 (legacy FK, kept for compat)
group_id = Column(Integer, ForeignKey("instance_groups.id", ondelete="SET NULL"), nullable=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
# 关系
group = relationship("InstanceGroup", back_populates="instances")
# 关系 — legacy group relationship removed; use instance_group_members instead
workflow_def = relationship("WorkflowDefinition")
+14 -4
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
import json
from typing import List, Optional
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Table, func
from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Table, func, UniqueConstraint
from sqlalchemy.orm import relationship
from app.core.database import Base
@@ -17,6 +17,17 @@ user_groups = Table(
Column("permissions", Text, nullable=True), # JSON: 覆盖组默认权限 (legacy, kept for compat)
)
# 实例-权限组关联表(一个权限组可同时包含 readonly 和 write 实例)
instance_group_members = Table(
"instance_group_members",
Base.metadata,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("group_id", Integer, ForeignKey("instance_groups.id", ondelete="CASCADE"), nullable=False),
Column("instance_id", Integer, ForeignKey("instances.id", ondelete="CASCADE"), nullable=False),
Column("access_type", String(20), nullable=False, default="readonly"), # readonly / write
UniqueConstraint("group_id", "instance_id", "access_type", name="uq_group_instance_access"),
)
class InstanceGroup(Base):
__tablename__ = "instance_groups"
@@ -26,14 +37,13 @@ class InstanceGroup(Base):
description = Column(Text, nullable=True)
db_type = Column(String(20), nullable=False, default="all") # mysql/redis/pg/mongo/all
permissions = Column(Text, nullable=True) # JSON: ["query:select", ...] (legacy, kept for compat)
access_type = Column(String(20), nullable=True, default="readonly") # readonly / write
access_type = Column(String(20), nullable=True, default="readonly") # legacy, kept for compat
allowed_databases = Column(Text, nullable=True) # JSON array of database names; empty = all databases
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, server_default=func.now())
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
# 关系
instances = relationship("Instance", back_populates="group", lazy="selectin")
# 关系 — users 保持不变
users = relationship("User", secondary=user_groups, back_populates="groups", lazy="selectin")
# ---- Legacy helpers (kept for backward compatibility) ----