feat(permission): extract PermissionDef into shared apps.permission_def

Move PermissionDef out of TENANT_APPS into a new SHARED app so all
tenants read a single global definition table in public schema.

- new app apps.permission_def (label=fonrey_permission_def)
- db_table preserved as permission_defs (no rename)
- FK refs updated: fonrey_permission.PermissionDef -> fonrey_permission_def.PermissionDef
- migrations: permission_def/0001_initial creates table in public,
  permission/0004 drops the now-orphan table from tenant schemas
This commit is contained in:
2026-04-30 12:45:44 +08:00
parent 5dedd19c0a
commit b9245cd891
11 changed files with 88 additions and 4 deletions

View File

@@ -0,0 +1,28 @@
# Generated by Django 4.2.16 on 2026-04-30 04:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('fonrey_permission_def', '0001_initial'),
('fonrey_permission', '0003_alter_permissionchangelog_action_and_more'),
]
operations = [
migrations.AlterField(
model_name='rolepermission',
name='permission_def',
field=models.ForeignKey(help_text='RESTRICT 防止删除仍被引用的权限项', on_delete=django.db.models.deletion.PROTECT, related_name='role_assignments', to='fonrey_permission_def.permissiondef', verbose_name='权限定义'),
),
migrations.AlterField(
model_name='staffpermissionoverride',
name='permission_def',
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='staff_overrides', to='fonrey_permission_def.permissiondef', verbose_name='被覆盖权限项'),
),
migrations.DeleteModel(
name='PermissionDef',
),
]

View File

@@ -1,4 +1,3 @@
from apps.permission.models.permission_def import PermissionDef
from apps.permission.models.role import Role, RolePermission
from apps.permission.models.staff_perm import (
PermissionChangeLog,
@@ -9,7 +8,6 @@ from apps.permission.models.staff_perm import (
__all__ = [
"PermissionChangeLog",
"PermissionDef",
"Role",
"RolePermission",
"StaffDataScope",

View File

@@ -91,7 +91,7 @@ class RolePermission(TimeStampedModel):
help_text="稀疏存储:角色删除时级联清理权限值",
)
permission_def = models.ForeignKey(
"fonrey_permission.PermissionDef",
"fonrey_permission_def.PermissionDef",
on_delete=models.PROTECT,
related_name="role_assignments",
verbose_name="权限定义",

View File

@@ -86,7 +86,7 @@ class StaffPermissionOverride(UUIDPrimaryKeyModel):
help_text="员工删除时级联删除覆盖记录",
)
permission_def = models.ForeignKey(
"fonrey_permission.PermissionDef",
"fonrey_permission_def.PermissionDef",
on_delete=models.PROTECT,
related_name="staff_overrides",
verbose_name="被覆盖权限项",

View File

View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class PermissionDefConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.permission_def"
label = "fonrey_permission_def"
verbose_name = "权限定义(全局共享)"

View File

@@ -0,0 +1,46 @@
# Generated by Django 4.2.16 on 2026-04-30 04:41
import django.contrib.postgres.fields
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='PermissionDef',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('code', models.CharField(help_text='规则:{module}.{sub_module}.{action}[.{qualifier}]', max_length=150, unique=True, verbose_name='权限编码')),
('module', models.CharField(choices=[('home', '首页'), ('property', '房源'), ('new_house', '新房'), ('client', '客源'), ('transaction', '交易'), ('data', '数据'), ('marketing', '营销'), ('hr', '人事OA'), ('contract', '合同'), ('trinet', '三网'), ('system', '系统'), ('mobile', '移动端'), ('smart_store', '智能门店'), ('recharge', '在线充值')], help_text='home/property/new_house/client/transaction/data/marketing/hr/contract/trinet/system/mobile/smart_store/recharge', max_length=50, verbose_name='一级模块')),
('sub_module', models.CharField(blank=True, default='', help_text='如「二手&租赁」「商圈精耕」', max_length=50, verbose_name='二级模块')),
('group_name', models.CharField(help_text='如「私客基础权限」「联系人基础权限」', max_length=100, verbose_name='分组标题')),
('name', models.CharField(max_length=200, verbose_name='显示名称')),
('description', models.TextField(blank=True, default='', verbose_name='权限作用描述')),
('value_type', models.CharField(choices=[('boolean', '开关型'), ('scope', '范围型'), ('integer', '数值型')], help_text='BOOLEAN=开关型 / SCOPE=范围型 / INTEGER=数值型', max_length=20, verbose_name='权限值类型')),
('scope_choices', models.JSONField(blank=True, default=list, help_text='仅 SCOPE 类型有效,可选枚举 code 列表,如 ["none","self","store","company"]', verbose_name='可选范围')),
('integer_min', models.IntegerField(blank=True, help_text='仅 INTEGER 类型有效', null=True, verbose_name='最小值')),
('integer_max', models.IntegerField(blank=True, help_text='仅 INTEGER 类型有效NULL=无上限(业务上 0 通常代表不限制)', null=True, verbose_name='最大值')),
('default_value', models.JSONField(default=dict, help_text='系统最小默认值,格式 {"v": <value>}', verbose_name='默认值')),
('max_allowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=50), blank=True, default=list, help_text='允许配置此权限的角色类别列表,空数组=所有类别均可', size=None, verbose_name='可配置角色类别')),
('sort_order', models.PositiveIntegerField(default=0, help_text='分组内排序', verbose_name='排序顺序')),
('is_active', models.BooleanField(default=True, help_text='下线权限项置 FALSE历史记录保留', verbose_name='是否启用')),
('is_deprecated', models.BooleanField(default=False, help_text='不再推荐使用但保持兼容', verbose_name='是否废弃')),
('version', models.PositiveIntegerField(default=1, help_text='变更时递增,用于缓存失效', verbose_name='定义版本')),
],
options={
'verbose_name': '权限定义',
'verbose_name_plural': '权限定义',
'db_table': 'permission_defs',
'indexes': [models.Index(condition=models.Q(('is_active', True)), fields=['module', 'sub_module', 'sort_order'], name='idx_perm_defs_module'), models.Index(condition=models.Q(('is_active', True)), fields=['is_active'], name='idx_perm_defs_active')],
},
),
]

View File

@@ -0,0 +1,3 @@
from apps.permission_def.models.permission_def import PermissionDef
__all__ = ["PermissionDef"]