""" 權限系統資料模型 ================ Permission - 權限定義表 UserPermission - 用戶權限關聯表 """ from datetime import datetime from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, UniqueConstraint from database.models import Base class Permission(Base): """權限定義表 - 系統預設權限項目""" __tablename__ = 'permissions' id = Column(Integer, primary_key=True) code = Column(String(50), unique=True, nullable=False, index=True) # 權限代碼,如 'dashboard.view' name = Column(String(100), nullable=False) # 顯示名稱,如 '查看首頁看板' category = Column(String(50), nullable=False) # 分類,如 '首頁/看板' description = Column(String(200)) # 詳細說明 sort_order = Column(Integer, default=0) # 排序順序 def to_dict(self): return { 'id': self.id, 'code': self.code, 'name': self.name, 'category': self.category, 'description': self.description, 'sort_order': self.sort_order } class UserPermission(Base): """用戶權限關聯表""" __tablename__ = 'user_permissions' id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False, index=True) permission_code = Column(String(50), nullable=False, index=True) granted_by = Column(Integer, ForeignKey('users.id', ondelete='SET NULL')) granted_at = Column(DateTime, default=datetime.utcnow) __table_args__ = ( UniqueConstraint('user_id', 'permission_code', name='uq_user_permission'), ) def to_dict(self): return { 'id': self.id, 'user_id': self.user_id, 'permission_code': self.permission_code, 'granted_by': self.granted_by, 'granted_at': self.granted_at.isoformat() if self.granted_at else None } # ============================================================================ # 權限定義常數 # ============================================================================ PERMISSIONS = [ # 首頁/看板 {'code': 'dashboard.view', 'name': '查看首頁看板', 'category': '首頁/看板', 'description': '訪問首頁商品看板', 'sort_order': 10}, {'code': 'dashboard.export', 'name': '匯出看板資料', 'category': '首頁/看板', 'description': '匯出商品資料 Excel', 'sort_order': 11}, # 報表 {'code': 'report.daily_sales.view', 'name': '查看每日銷售', 'category': '報表', 'description': '訪問每日銷售頁面', 'sort_order': 20}, {'code': 'report.daily_sales.export', 'name': '匯出每日銷售', 'category': '報表', 'description': '匯出每日銷售 Excel', 'sort_order': 21}, {'code': 'report.monthly_summary.view', 'name': '查看月度總結', 'category': '報表', 'description': '訪問月度總結分析頁面', 'sort_order': 22}, {'code': 'report.monthly_summary.import', 'name': '匯入月度資料', 'category': '報表', 'description': '匯入月度總結 Excel', 'sort_order': 23}, {'code': 'report.sales_analysis.view', 'name': '查看銷售分析', 'category': '報表', 'description': '訪問銷售分析頁面', 'sort_order': 24}, {'code': 'report.growth_analysis.view', 'name': '查看成長分析', 'category': '報表', 'description': '訪問成長分析頁面', 'sort_order': 25}, {'code': 'report.abc_analysis.view', 'name': '查看 ABC 分析', 'category': '報表', 'description': '訪問 ABC 分析頁面', 'sort_order': 26}, # 活動看板 {'code': 'edm.view', 'name': '查看 EDM 看板', 'category': '活動看板', 'description': '訪問 EDM 看板頁面', 'sort_order': 30}, {'code': 'edm.trigger', 'name': '觸發 EDM 爬蟲', 'category': '活動看板', 'description': '手動觸發 EDM 爬蟲', 'sort_order': 31}, {'code': 'festival.view', 'name': '查看節慶看板', 'category': '活動看板', 'description': '訪問節慶看板頁面', 'sort_order': 32}, {'code': 'festival.trigger', 'name': '觸發節慶爬蟲', 'category': '活動看板', 'description': '手動觸發節慶爬蟲', 'sort_order': 33}, # 廠商缺貨 {'code': 'vendor.index.view', 'name': '查看廠商缺貨首頁', 'category': '廠商缺貨', 'description': '訪問廠商缺貨首頁', 'sort_order': 40}, {'code': 'vendor.import', 'name': '匯入缺貨資料', 'category': '廠商缺貨', 'description': '匯入缺貨 Excel', 'sort_order': 41}, {'code': 'vendor.list.view', 'name': '查看缺貨清單', 'category': '廠商缺貨', 'description': '訪問缺貨清單頁面', 'sort_order': 42}, {'code': 'vendor.list.edit', 'name': '編輯缺貨資料', 'category': '廠商缺貨', 'description': '編輯/刪除缺貨記錄', 'sort_order': 43}, {'code': 'vendor.management.view', 'name': '查看廠商管理', 'category': '廠商缺貨', 'description': '訪問廠商管理頁面', 'sort_order': 44}, {'code': 'vendor.management.edit', 'name': '管理廠商資料', 'category': '廠商缺貨', 'description': '新增/編輯/刪除廠商', 'sort_order': 45}, {'code': 'vendor.email.view', 'name': '查看郵件發送', 'category': '廠商缺貨', 'description': '訪問郵件發送頁面', 'sort_order': 46}, {'code': 'vendor.email.send', 'name': '發送廠商郵件', 'category': '廠商缺貨', 'description': '發送缺貨通知郵件', 'sort_order': 47}, {'code': 'vendor.history.view', 'name': '查看歷史記錄', 'category': '廠商缺貨', 'description': '訪問歷史記錄頁面', 'sort_order': 48}, # 匯入 {'code': 'import.auto.view', 'name': '查看自動匯入', 'category': '匯入', 'description': '訪問自動匯入頁面', 'sort_order': 50}, {'code': 'import.auto.manage', 'name': '管理匯入任務', 'category': '匯入', 'description': '新增/編輯/刪除匯入任務', 'sort_order': 51}, {'code': 'import.manual', 'name': '手動匯入資料', 'category': '匯入', 'description': '系統設定頁手動匯入', 'sort_order': 52}, # 系統 {'code': 'system.settings.view', 'name': '查看系統設定', 'category': '系統', 'description': '訪問系統設定頁面', 'sort_order': 60}, {'code': 'system.settings.edit', 'name': '修改系統設定', 'category': '系統', 'description': '儲存系統設定變更', 'sort_order': 61}, {'code': 'system.advanced.view', 'name': '查看進階設定', 'category': '系統', 'description': '訪問進階設定頁面', 'sort_order': 62}, {'code': 'system.advanced.edit', 'name': '修改進階設定', 'category': '系統', 'description': '分類管理等進階操作', 'sort_order': 63}, {'code': 'system.logs.view', 'name': '查看系統日誌', 'category': '系統', 'description': '訪問系統日誌頁面', 'sort_order': 64}, {'code': 'system.crawler.view', 'name': '查看爬蟲管理', 'category': '系統', 'description': '訪問爬蟲管理頁面', 'sort_order': 65}, {'code': 'system.crawler.manage', 'name': '管理爬蟲設定', 'category': '系統', 'description': '修改爬蟲設定', 'sort_order': 66}, {'code': 'system.backup', 'name': '備份資料庫', 'category': '系統', 'description': '執行資料庫備份', 'sort_order': 67}, {'code': 'system.users.view', 'name': '查看用戶管理', 'category': '系統', 'description': '訪問用戶管理頁面', 'sort_order': 68}, {'code': 'system.users.manage', 'name': '管理用戶帳號', 'category': '系統', 'description': '新增/編輯/刪除用戶', 'sort_order': 69}, # 其他 {'code': 'brand_assets.view', 'name': '查看品牌素材', 'category': '其他', 'description': '訪問品牌素材頁面', 'sort_order': 90}, ] # 所有權限代碼列表 ALL_PERMISSION_CODES = [p['code'] for p in PERMISSIONS] # ============================================================================ # 角色預設權限模板 # ============================================================================ # admin: 全部權限 ROLE_ADMIN_PERMISSIONS = ALL_PERMISSION_CODES.copy() # manager: 大部分權限,排除用戶管理和高危操作 ROLE_MANAGER_PERMISSIONS = [ # 首頁/看板 'dashboard.view', 'dashboard.export', # 報表 'report.daily_sales.view', 'report.daily_sales.export', 'report.monthly_summary.view', 'report.monthly_summary.import', 'report.sales_analysis.view', 'report.growth_analysis.view', 'report.abc_analysis.view', # 活動看板 'edm.view', 'edm.trigger', 'festival.view', 'festival.trigger', # 廠商缺貨 'vendor.index.view', 'vendor.import', 'vendor.list.view', 'vendor.list.edit', 'vendor.management.view', 'vendor.management.edit', 'vendor.email.view', 'vendor.email.send', 'vendor.history.view', # 匯入 'import.auto.view', 'import.auto.manage', 'import.manual', # 系統 (有限) 'system.settings.view', 'system.settings.edit', 'system.advanced.view', # 可查看但不能編輯進階設定 'system.logs.view', 'system.crawler.view', # 可查看但不能管理爬蟲 # 其他 'brand_assets.view', ] # user: 僅查看權限 ROLE_USER_PERMISSIONS = [ # 首頁/看板 'dashboard.view', # 報表 (僅查看) 'report.daily_sales.view', 'report.monthly_summary.view', 'report.sales_analysis.view', 'report.growth_analysis.view', 'report.abc_analysis.view', # 活動看板 (僅查看) 'edm.view', 'festival.view', # 廠商缺貨 (部分查看) 'vendor.index.view', 'vendor.list.view', 'vendor.history.view', # 其他 'brand_assets.view', ] # 角色權限模板映射 ROLE_DEFAULT_PERMISSIONS = { 'admin': ROLE_ADMIN_PERMISSIONS, 'manager': ROLE_MANAGER_PERMISSIONS, 'user': ROLE_USER_PERMISSIONS, } def init_permissions(db_session): """初始化權限表資料 Args: db_session: 資料庫 session Returns: tuple: (success, message) """ try: # 檢查權限表是否已有資料 existing_count = db_session.query(Permission).count() if existing_count > 0: # 同步更新權限(新增缺少的、更新已有的) existing_codes = {p.code for p in db_session.query(Permission).all()} added = 0 updated = 0 for perm_data in PERMISSIONS: if perm_data['code'] in existing_codes: # 更新已有權限 perm = db_session.query(Permission).filter_by(code=perm_data['code']).first() perm.name = perm_data['name'] perm.category = perm_data['category'] perm.description = perm_data.get('description') perm.sort_order = perm_data.get('sort_order', 0) updated += 1 else: # 新增權限 perm = Permission( code=perm_data['code'], name=perm_data['name'], category=perm_data['category'], description=perm_data.get('description'), sort_order=perm_data.get('sort_order', 0) ) db_session.add(perm) added += 1 db_session.commit() return True, f"權限表已同步:新增 {added} 項,更新 {updated} 項" # 首次初始化,批量新增 for perm_data in PERMISSIONS: perm = Permission( code=perm_data['code'], name=perm_data['name'], category=perm_data['category'], description=perm_data.get('description'), sort_order=perm_data.get('sort_order', 0) ) db_session.add(perm) db_session.commit() return True, f"權限表初始化完成,共 {len(PERMISSIONS)} 項權限" except Exception as e: db_session.rollback() return False, f"權限表初始化失敗: {str(e)}"