PayrollManagementSystem/backend/alembic/versions/22a3b4c5d6e7_authorization.py

48 lines
17 KiB
Python
Raw Normal View History

"""V22 完整角色及个人授权;目录与模板固化于此版本。"""
from alembic import op
import sqlalchemy as sa
revision: str = "22a3b4c5d6e7"
down_revision: str | None = "21f2a3b4c5d6"
branch_labels = None
depends_on = None
DDL = ["CREATE TABLE authorization_settings (\n\tid BIGINT UNSIGNED NOT NULL COMMENT '单例主键ID' AUTO_INCREMENT, \n\tmaker_user_id BIGINT UNSIGNED COMMENT '现任工资制作人账号ID', \n\trevision INTEGER NOT NULL COMMENT '全局授权版本' DEFAULT '1', \n\tupdated_by BIGINT UNSIGNED COMMENT '最近修改人ID', \n\tcreate_time DATETIME NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP, \n\tupdate_time DATETIME NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, \n\tCONSTRAINT pk_authorization_settings PRIMARY KEY (id), \n\tCONSTRAINT fk_authorization_settings_maker_user_id FOREIGN KEY(maker_user_id) REFERENCES users (id) ON DELETE RESTRICT, \n\tCONSTRAINT fk_authorization_settings_updated_by FOREIGN KEY(updated_by) REFERENCES users (id) ON DELETE SET NULL\n)ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='授权设置表(单例与并发修改锁)' COLLATE utf8mb4_general_ci;", "CREATE TABLE user_permission_overrides (\n\tid BIGINT UNSIGNED NOT NULL COMMENT '主键ID' AUTO_INCREMENT, \n\tuser_id BIGINT UNSIGNED NOT NULL COMMENT '系统用户ID', \n\tpermission_id BIGINT UNSIGNED NOT NULL COMMENT '权限ID', \n\teffect VARCHAR(8) NOT NULL COMMENT '个人覆盖:ALLOW允许 DENY禁止', \n\tcreate_time DATETIME NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP, \n\tupdate_time DATETIME NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, \n\tCONSTRAINT pk_user_permission_overrides PRIMARY KEY (id), \n\tCONSTRAINT uk_user_permission_overrides_user_permission UNIQUE (user_id, permission_id), \n\tCONSTRAINT fk_user_permission_overrides_user_id FOREIGN KEY(user_id) REFERENCES users (id) ON DELETE CASCADE, \n\tCONSTRAINT fk_user_permission_overrides_permission_id FOREIGN KEY(permission_id) REFERENCES permissions (id) ON DELETE RESTRICT\n)ENGINE=InnoDB CHARSET=utf8mb4 COMMENT='用户权限覆盖表(个人禁止优先)' COLLATE utf8mb4_general_ci;", 'CREATE INDEX idx_user_permission_overrides_permission_id ON user_permission_overrides (permission_id);']
PERMISSIONS = [('dashboard:view', '首页:查看', 1, None, 1), ('system:users:view', '用户管理:查看', 1, None, 2), ('system:users:create', '用户管理:新增', 2, 'system:users:view', 3), ('system:users:update', '用户管理:编辑', 2, 'system:users:view', 4), ('system:users:delete', '用户管理:删除', 2, 'system:users:view', 5), ('system:users:status', '用户管理:变更状态', 2, 'system:users:view', 6), ('system:users:password', '用户管理:重置密码', 2, 'system:users:view', 7), ('system:roles:view', '角色管理:查看', 1, None, 8), ('system:roles:create', '角色管理:新增', 2, 'system:roles:view', 9), ('system:roles:update', '角色管理:编辑', 2, 'system:roles:view', 10), ('system:roles:delete', '角色管理:删除', 2, 'system:roles:view', 11), ('system:roles:status', '角色管理:变更状态', 2, 'system:roles:view', 12), ('system:authorization:view', '权限与工资制作人:查看', 1, None, 13), ('system:authorization:update', '权限与工资制作人:编辑', 2, 'system:authorization:view', 14), ('system:business_roles:view', '岗位角色:查看', 1, None, 15), ('system:business_roles:create', '岗位角色:新增', 2, 'system:business_roles:view', 16), ('system:business_roles:update', '岗位角色:编辑', 2, 'system:business_roles:view', 17), ('system:business_roles:delete', '岗位角色:删除', 2, 'system:business_roles:view', 18), ('system:business_roles:status', '岗位角色:变更状态', 2, 'system:business_roles:view', 19), ('system:params:view', '计提参数:查看', 1, None, 20), ('system:params:update', '计提参数:编辑', 2, 'system:params:view', 21), ('system:params:withdraw', '计提参数:撤回参数', 2, 'system:params:view', 22), ('system:params:export', '计提参数:导出', 2, 'system:params:view', 23), ('system:params:preview', '计提参数:预览', 2, 'system:params:view', 24), ('system:settings:view', '公司信息:查看', 1, None, 25), ('system:settings:update', '公司信息:编辑', 2, 'system:settings:view', 26), ('system:logs:view', '日志查询:查看', 1, None, 27), ('base:personnel:view', '人员管理:查看', 1, None, 28), ('base:personnel:create', '人员管理:新增', 2, 'base:personnel:view', 29), ('base:personnel:update', '人员管理:编辑', 2, 'base:personnel:view', 30), ('base:personnel:delete', '人员管理:删除', 2, 'base:personnel:view', 31), ('base:personnel:status', '人员管理:变更状态', 2, 'base:personnel:view', 32), ('base:personnel:regions', '人员管理:维护区域', 2, 'base:personnel:view', 33), ('base:personnel:plans', '人员管理:维护计划', 2, 'base:personnel:view', 34), ('base:products:view', '产品管理:查看', 1, None, 35), ('base:products:create', '产品管理:新增', 2, 'base:products:view', 36), ('base:products:update', '产品管理:编辑', 2, 'base:products:view', 37), ('base:products:delete', '产品管理:删除', 2, 'base:products:view', 38), ('base:products:status', '产品管理:变更状态', 2, 'base:products:view', 39), ('base:customers:view', '客户管理:查看', 1, None, 40), ('base:customers:create', '客户管理:新增', 2, 'base:customers:view', 41), ('base:customers:update', '客户管理:编辑', 2, 'base:customers:view', 42), ('base:customers:delete', '客户管理:删除', 2, 'base:customers:view', 43), ('base:customers:rating', '客户管理:客户评级', 2, 'base:customers:view', 44), ('base:customers:ownership', '客户管理:变更归属', 2, 'base:customers:view', 45), ('business:businesses:view', '业务管理:查看', 1, None, 46), ('business:businesses:create', '业务管理:新增', 2, 'business:businesses:view', 47), ('business:businesses:update', '业务管理:编辑', 2, 'business:businesses:view', 48), ('business:businesses:delete', '业务管理:删除', 2, 'business:businesses:view', 49), ('business:businesses:status', '业务管理:变更状态', 2, 'business:businesses:view', 50), ('business:businesses:ownership', '业务管理:变更
DEFAULTS = {'admin': ['adjustments:create', 'adjustments:view', 'approvals:finance', 'approvals:leader', 'approvals:view', 'base:customers:create', 'base:customers:delete', 'base:customers:ownership', 'base:customers:rating', 'base:customers:update', 'base:customers:view', 'base:personnel:create', 'base:personnel:delete', 'base:personnel:plans', 'base:personnel:regions', 'base:personnel:status', 'base:personnel:update', 'base:personnel:view', 'base:products:create', 'base:products:delete', 'base:products:status', 'base:products:update', 'base:products:view', 'business:businesses:create', 'business:businesses:delete', 'business:businesses:ownership', 'business:businesses:status', 'business:businesses:update', 'business:businesses:view', 'business:ratings:create', 'business:ratings:delete', 'business:ratings:update', 'business:ratings:view', 'business:receipts:create', 'business:receipts:delete', 'business:receipts:update', 'business:receipts:view', 'business:shipments:create', 'business:shipments:delete', 'business:shipments:update', 'business:shipments:view', 'dashboard:view', 'payroll:allocation', 'payroll:calculate', 'payroll:prepare', 'payroll:view', 'reports:business:export', 'reports:business:view', 'reports:department:export', 'reports:department:view', 'reports:payments:export', 'reports:payments:view', 'reports:personal:export', 'reports:personal:view', 'settlements:generate', 'settlements:submit', 'settlements:view', 'system:authorization:update', 'system:authorization:view', 'system:business_roles:create', 'system:business_roles:delete', 'system:business_roles:status', 'system:business_roles:update', 'system:business_roles:view', 'system:logs:view', 'system:params:export', 'system:params:preview', 'system:params:update', 'system:params:view', 'system:params:withdraw', 'system:roles:create', 'system:roles:delete', 'system:roles:status', 'system:roles:update', 'system:roles:view', 'system:settings:update', 'system:settings:view', 'system:users:create', 'system:users:delete', 'system:users:password', 'system:users:status', 'system:users:update', 'system:users:view', 'tplus:collect', 'tplus:export', 'tplus:facts', 'tplus:import', 'tplus:preview', 'tplus:retry', 'tplus:view'], 'user': ['base:customers:view', 'base:personnel:view', 'base:products:view', 'business:businesses:view', 'business:ratings:view', 'business:receipts:view', 'business:shipments:view'], 'marketing_ops': ['base:customers:view', 'base:personnel:view', 'base:products:view', 'business:businesses:view', 'business:ratings:view', 'business:receipts:view', 'business:shipments:view'], 'finance': ['adjustments:view', 'approvals:finance', 'approvals:view', 'base:customers:view', 'base:personnel:view', 'base:products:view', 'business:businesses:view', 'business:ratings:view', 'business:receipts:view', 'business:shipments:view', 'dashboard:view', 'payroll:view', 'reports:business:export', 'reports:business:view', 'reports:department:export', 'reports:department:view', 'reports:payments:export', 'reports:payments:view', 'reports:personal:export', 'reports:personal:view', 'settlements:view', 'system:params:preview', 'system:params:view'], 'leader': ['adjustments:view', 'approvals:leader', 'approvals:view', 'base:customers:view', 'base:personnel:view', 'base:products:view', 'business:businesses:view', 'business:ratings:view', 'business:receipts:view', 'business:shipments:view', 'dashboard:view', 'payroll:view', 'reports:business:export', 'reports:business:view', 'reports:department:export', 'reports:department:view', 'reports:payments:export', 'reports:payments:view', 'reports:personal:export', 'reports:personal:view', 'settlements:view', 'system:params:preview', 'system:params:view'], 'payroll_maker': ['adjustments:create', 'adjustments:view', 'approvals:view', 'base:customers:create', 'base:customers:delete', 'base:customers:ownership', 'base:customers:rating', 'base:customers:update', 'base:customers:view', 'base:personnel:create', 'base:personnel:delete', 'base:personnel:plans', 'base:personnel:regions', 'base:personnel:status', 'base:personnel:update', 'b
def upgrade():
bind=op.get_bind()
for sql in DDL:
op.execute(sql)
for code,name,kind,_parent,order in PERMISSIONS:
bind.execute(sa.text("INSERT INTO permissions (perm_code,perm_name,perm_type,sort_order,status) VALUES (:code,:name,:kind,:order,1)"),{"code": code,"name": name,"kind": kind,"order": order})
for code,_,_,parent,_ in PERMISSIONS:
if parent:
bind.execute(sa.text("UPDATE permissions p JOIN permissions parent ON parent.perm_code=:parent SET p.parent_id=parent.id WHERE p.perm_code=:code"),{"parent": parent,"code": code})
bind.execute(sa.text("INSERT INTO roles (role_code,role_name,description,sort_order,status) VALUES ('payroll_maker','工资制作人','指定制作人还需在权限设置中绑定,角色本身不能绕过月份锁',5,1)"))
for role,codes in DEFAULTS.items():
for code in codes:
bind.execute(sa.text("INSERT INTO role_permissions (role_id,permission_id) SELECT r.id,p.id FROM roles r JOIN permissions p ON p.perm_code=:code WHERE r.role_code=:role"),{"code": code,"role": role})
from app.core.config import get_settings
maker=get_settings().payroll_maker_user_id
if maker is not None and not bind.execute(sa.text("SELECT id FROM users WHERE id=:id AND status=1"),{"id": maker}).scalar():
raise RuntimeError("旧工资制作人账号无效,请修正配置后重试迁移")
bind.execute(sa.text("INSERT INTO authorization_settings (id,maker_user_id,revision) VALUES (1,:maker,1)"),{"maker": maker})
if maker is not None:
bind.execute(sa.text("INSERT INTO user_roles (user_id,role_id) SELECT :maker,id FROM roles WHERE role_code='payroll_maker'"),{"maker": maker})
def downgrade():
bind=op.get_bind()
if bind.execute(sa.text("SELECT revision FROM authorization_settings WHERE id=1")).scalar()!=1 or bind.execute(sa.text("SELECT COUNT(*) FROM user_permission_overrides")).scalar():
raise RuntimeError("已发生授权修改,拒绝删除授权依据;请保留数据并回退应用版本")
op.drop_table('user_permission_overrides')
op.drop_table('authorization_settings')
codes=[row[0] for row in PERMISSIONS]
for code in codes:
bind.execute(sa.text("DELETE rp FROM role_permissions rp JOIN permissions p ON p.id=rp.permission_id WHERE p.perm_code=:code"),{"code": code})
bind.execute(sa.text("DELETE ur FROM user_roles ur JOIN roles r ON r.id=ur.role_id WHERE r.role_code='payroll_maker'"))
bind.execute(sa.text("DELETE FROM roles WHERE role_code='payroll_maker'"))
for code in reversed(codes):
bind.execute(sa.text("DELETE FROM permissions WHERE perm_code=:code"),{"code": code})