Files
ewoooc/database/permission_models.py
ogt 1b4f3a7bbe
Some checks failed
CD Pipeline / deploy (push) Failing after 59s
feat: EwoooC 初始化 — 完整專案推版至 Gitea
- 建立 Gitea Actions CD pipeline (.gitea/workflows/cd.yaml)
- 部署模式: rsync Python 檔案至 188 → docker restart (volume mount)
- Dockerfile/requirements 變動時自動重建 Docker image
- 部署通知: Telegram (開始/成功/失敗)
- 健康檢查: https://mo.wooo.work/health (最多 5 次重試)
- 同步最新 CLAUDE.md / ADR-008 / memory (2026-04-19)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 01:21:13 +08:00

234 lines
12 KiB
Python

"""
權限系統資料模型
================
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)}"