feat(auth): 添加用户权限获取接口并完善JWT令牌角色信息

- 在JWT令牌中添加用户角色信息
- 新增get_my_permissions接口用于获取当前用户权限码列表
- 重构认证回调逻辑,增加错误日志记录
- 更新用户信息获取接口使用Authorization头验证
```
This commit is contained in:
2026-04-06 00:40:08 +08:00
parent dfa8fa62a8
commit f1f8518985
71 changed files with 9402 additions and 191 deletions
+62
View File
@@ -0,0 +1,62 @@
"""审计日志 Celery 任务"""
import traceback
from app.core.celery_app import celery_app
from app.core.database import SessionLocal
@celery_app.task(queue='h3c_onu_ms', ignore_result=True)
def create_audit_log_task(
user_id: str,
username: str,
user_role: str,
method: str,
path: str,
ip_address: str,
user_agent: str,
status_code: int,
request_params: dict = None,
response_data: dict = None,
error_message: str = None,
description: str = None,
resource_id: str = None,
resource_name: str = None,
):
"""异步写入审计日志,不阻塞主请求流程"""
from app.services.audit_service import write_audit_log
db = SessionLocal()
try:
write_audit_log(
db,
user_id=user_id,
username=username,
user_role=user_role,
method=method,
path=path,
ip_address=ip_address,
user_agent=user_agent,
status_code=status_code,
request_params=request_params,
response_data=response_data,
error_message=error_message,
description=description,
resource_id=resource_id,
resource_name=resource_name,
)
except Exception:
pass # 审计日志失败不影响主业务
finally:
db.close()
@celery_app.task(queue='h3c_onu_ms')
def cleanup_audit_logs_task():
"""清理90天前的审计日志(每天执行)"""
from app.services.audit_service import cleanup_old_logs
db = SessionLocal()
try:
deleted = cleanup_old_logs(db)
return {'success': True, 'deleted': deleted}
except Exception as e:
return {'success': False, 'error': str(e), 'traceback': traceback.format_exc()}
finally:
db.close()
+105 -1
View File
@@ -1,13 +1,56 @@
"""状态检查任务"""
import time
import traceback
import redis as redis_lib
from datetime import datetime, timedelta
from sqlalchemy import func, case
from app.core.celery_app import celery_app
from app.core.database import SessionLocal
from app.core.config import settings
from app.services.check_service import CheckService
_LAST_RUN_KEY = "check_all_devices:last_run"
_RUNNING_KEY = "check_all_devices:running"
_INTERVAL_REDIS_KEY = "system:check_interval_seconds"
def _get_redis():
return redis_lib.from_url(settings.REDIS_URL, decode_responses=True)
def _get_check_interval(r) -> int:
"""从 Redis 读取配置间隔,回退到 DB,再回退到默认值"""
cached = r.get(_INTERVAL_REDIS_KEY)
if cached:
return int(cached)
# 从 DB 读取并缓存
db = SessionLocal()
try:
from app.models.setting import SystemSetting
setting = db.query(SystemSetting).filter_by(key='check_interval_seconds').first()
interval = int(setting.value) if setting else settings.CHECK_INTERVAL
r.set(_INTERVAL_REDIS_KEY, str(interval))
return interval
finally:
db.close()
@celery_app.task(bind=True)
def check_all_devices(self):
"""检查所有设备状态"""
"""检查所有设备状态(支持可配置间隔,最小5分钟)"""
r = _get_redis()
interval = _get_check_interval(r)
# Redis 节流:检查距上次运行是否已超过配置间隔
last_run = r.get(_LAST_RUN_KEY)
now = time.time()
if last_run and (now - float(last_run)) < interval:
remaining = int(interval - (now - float(last_run)))
return {'skipped': True, 'reason': f'间隔未到,还需等待 {remaining}', 'interval': interval}
# 标记正在运行(TTL 10分钟防止异常时永久卡住)
r.set(_RUNNING_KEY, '1', ex=600)
db = SessionLocal()
self.update_state(state='PROGRESS', meta={'current': 0, 'total': 0, 'status': '获取OLT列表...'})
try:
@@ -68,5 +111,66 @@ def check_all_devices(self):
'error': str(e),
'traceback': traceback.format_exc()
}
finally:
# 任务完成后记录时间、清除运行标记
r.set(_LAST_RUN_KEY, str(time.time()))
r.delete(_RUNNING_KEY)
db.close()
@celery_app.task
def aggregate_daily_snapshot():
"""聚合昨日设备状态快照(每天凌晨执行)"""
from app.models.device import DeviceStatusHistory, DeviceDailySnapshot, ONUDevice
db = SessionLocal()
try:
yesterday = (datetime.utcnow() - timedelta(days=1)).date()
date_str = yesterday.strftime('%Y-%m-%d')
# 如果已存在则跳过(幂等)
exists = db.query(DeviceDailySnapshot).filter_by(snapshot_date=date_str).first()
if exists:
return {'skipped': True, 'date': date_str}
# 昨天每台设备的最后一次检查状态
day_start = datetime.combine(yesterday, datetime.min.time())
day_end = datetime.combine(yesterday, datetime.max.time())
daily_latest_subq = (
db.query(
DeviceStatusHistory.onu_device_id,
func.max(DeviceStatusHistory.checked_at).label("max_checked_at"),
)
.filter(DeviceStatusHistory.checked_at.between(day_start, day_end))
.group_by(DeviceStatusHistory.onu_device_id)
.subquery()
)
row = (
db.query(
func.count().label("total"),
func.sum(case((DeviceStatusHistory.status == 'online', 1), else_=0)).label("online"),
func.sum(case((DeviceStatusHistory.status == 'offline', 1), else_=0)).label("offline"),
)
.join(
daily_latest_subq,
(DeviceStatusHistory.onu_device_id == daily_latest_subq.c.onu_device_id) &
(DeviceStatusHistory.checked_at == daily_latest_subq.c.max_checked_at)
)
.one()
)
snapshot = DeviceDailySnapshot(
snapshot_date=date_str,
total=int(row.total or 0),
online=int(row.online or 0),
offline=int(row.offline or 0),
)
db.add(snapshot)
db.commit()
return {'success': True, 'date': date_str, 'total': snapshot.total, 'online': snapshot.online}
except Exception as e:
db.rollback()
return {'success': False, 'error': str(e), 'traceback': traceback.format_exc()}
finally:
db.close()