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
+52
View File
@@ -0,0 +1,52 @@
"""审计日志数据库模型"""
from sqlalchemy import Column, Integer, String, Text, TIMESTAMP, Index
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.sql import func
from app.core.database import Base
class AuditLog(Base):
__tablename__ = "audit_logs"
id = Column(Integer, primary_key=True, index=True)
# 用户信息
user_id = Column(String(100), nullable=False)
username = Column(String(100), nullable=False)
user_role = Column(String(50))
# 操作信息
action_time = Column(TIMESTAMP, nullable=False, server_default=func.now())
action_type = Column(String(50), nullable=False) # auth/device/olt/user/system/inventory
action_subtype = Column(String(50)) # create/update/delete/login/...
# 请求信息
ip_address = Column(String(45))
user_agent = Column(Text)
request_method = Column(String(10))
request_path = Column(String(500))
# 操作结果
status = Column(String(20), nullable=False) # success/failed/error
status_code = Column(Integer)
# 资源信息
resource_type = Column(String(50))
resource_id = Column(String(100))
resource_name = Column(String(200))
# 日志内容
description = Column(Text, nullable=False)
request_params = Column(JSONB)
response_data = Column(JSONB)
error_message = Column(Text)
created_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
__table_args__ = (
Index("idx_audit_logs_action_time", "action_time"),
Index("idx_audit_logs_user_id", "user_id"),
Index("idx_audit_logs_action_type", "action_type"),
Index("idx_audit_logs_resource_type", "resource_type"),
Index("idx_audit_logs_status", "status"),
)
+28
View File
@@ -13,6 +13,7 @@ class OLTDevice(Base):
username = Column(String(100), nullable=False)
password = Column(Text, nullable=False)
slot_command = Column(String(50), nullable=False)
region = Column(String(100), index=True)
location = Column(String(200))
description = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
@@ -72,6 +73,18 @@ class DuplicateMac(Base):
last_seen_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
class DeviceDailySnapshot(Base):
"""设备每日状态快照(用于趋势图,避免全量扫描历史表)"""
__tablename__ = "device_daily_snapshots"
id = Column(BigInteger, primary_key=True, index=True)
snapshot_date = Column(String(10), nullable=False, index=True) # YYYY-MM-DD
total = Column(Integer, nullable=False, default=0)
online = Column(Integer, nullable=False, default=0)
offline = Column(Integer, nullable=False, default=0)
created_at = Column(TIMESTAMP, server_default=func.now())
class NewDevice(Base):
"""新发现设备(OLT 扫描到但尚未补全信息的设备)"""
__tablename__ = "new_devices"
@@ -80,3 +93,18 @@ class NewDevice(Base):
onu_device_id = Column(BigInteger, ForeignKey("onu_devices.id"), nullable=False, unique=True)
olt_id = Column(BigInteger, ForeignKey("olt_devices.id"), nullable=False)
discovered_at = Column(TIMESTAMP, server_default=func.now())
class DeviceReplacement(Base):
"""设备更换记录"""
__tablename__ = "device_replacements"
id = Column(BigInteger, primary_key=True, index=True)
onu_device_id = Column(BigInteger, ForeignKey("onu_devices.id"), nullable=False, index=True)
old_mac = Column(String(17), nullable=False)
new_mac = Column(String(17), nullable=False)
reason = Column(Text)
operator_id = Column(String(100)) # 操作人 user_id
operator_name = Column(String(100))
replaced_at = Column(TIMESTAMP, nullable=False, server_default=func.now())
created_at = Column(TIMESTAMP, server_default=func.now())
+117
View File
@@ -0,0 +1,117 @@
"""库存管理数据模型"""
from sqlalchemy import Column, BigInteger, String, Integer, Text, TIMESTAMP, ForeignKey, Boolean, Numeric, Date
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from app.core.database import Base
class MaterialCategory(Base):
__tablename__ = "material_categories"
id = Column(BigInteger, primary_key=True, index=True)
name = Column(String(100), nullable=False)
code = Column(String(50), unique=True, nullable=False)
description = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
materials = relationship("Material", back_populates="category")
class Material(Base):
__tablename__ = "materials"
id = Column(BigInteger, primary_key=True, index=True)
category_id = Column(BigInteger, ForeignKey("material_categories.id"))
name = Column(String(200), nullable=False)
model = Column(String(100))
specification = Column(Text)
brand = Column(String(100))
unit = Column(String(20), default="")
safe_quantity = Column(Integer, default=0)
notes = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
category = relationship("MaterialCategory", back_populates="materials")
batches = relationship("InventoryBatch", back_populates="material")
serial_devices = relationship("SerialDevice", back_populates="material")
class InventoryBatch(Base):
__tablename__ = "inventory_batches"
id = Column(BigInteger, primary_key=True, index=True)
material_id = Column(BigInteger, ForeignKey("materials.id"))
batch_no = Column(String(50), nullable=False)
quantity = Column(Integer, nullable=False)
available_quantity = Column(Integer, nullable=False, default=0)
supplier = Column(String(200))
purchase_date = Column(Date)
purchase_price = Column(Numeric(10, 2))
expiry_date = Column(Date)
location = Column(String(100))
status = Column(String(20), default="in_stock") # in_stock, reserved, out_of_stock
created_at = Column(TIMESTAMP, server_default=func.now())
material = relationship("Material", back_populates="batches")
serial_devices = relationship("SerialDevice", back_populates="batch")
class SerialDevice(Base):
__tablename__ = "serial_devices"
id = Column(BigInteger, primary_key=True, index=True)
material_id = Column(BigInteger, ForeignKey("materials.id"))
batch_id = Column(BigInteger, ForeignKey("inventory_batches.id"))
serial_no = Column(String(100), unique=True, nullable=False)
mac_address = Column(String(17), index=True)
asset_no = Column(String(50))
status = Column(String(20), default="in_stock", index=True)
# in_stock, allocated, installed, in_use, returned, repairing, scrapped
current_location = Column(String(200))
installed_info = Column(JSONB)
onu_device_id = Column(BigInteger, ForeignKey("onu_devices.id"), nullable=True)
notes = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
material = relationship("Material", back_populates="serial_devices")
batch = relationship("InventoryBatch", back_populates="serial_devices")
class InventoryTransaction(Base):
__tablename__ = "inventory_transactions"
id = Column(BigInteger, primary_key=True, index=True)
transaction_no = Column(String(50), unique=True, nullable=False)
transaction_type = Column(String(20), nullable=False)
# purchase_in, allocate_out, return_in, scrap_out, adjust
material_id = Column(BigInteger, ForeignKey("materials.id"))
batch_id = Column(BigInteger, ForeignKey("inventory_batches.id"), nullable=True)
serial_device_id = Column(BigInteger, ForeignKey("serial_devices.id"), nullable=True)
quantity = Column(Integer, nullable=False)
from_status = Column(String(20))
to_status = Column(String(20))
operator_id = Column(BigInteger, ForeignKey("users.id"))
project_name = Column(String(200))
installation_info = Column(JSONB)
notes = Column(Text)
created_at = Column(TIMESTAMP, server_default=func.now())
class InventoryCheck(Base):
__tablename__ = "inventory_checks"
id = Column(BigInteger, primary_key=True, index=True)
check_no = Column(String(50), unique=True, nullable=False)
check_date = Column(Date, nullable=False)
checker_id = Column(BigInteger, ForeignKey("users.id"))
material_id = Column(BigInteger, ForeignKey("materials.id"))
batch_id = Column(BigInteger, ForeignKey("inventory_batches.id"), nullable=True)
book_quantity = Column(Integer)
actual_quantity = Column(Integer)
difference = Column(Integer)
reason = Column(Text)
adjusted = Column(Boolean, default=False)
created_at = Column(TIMESTAMP, server_default=func.now())
+13
View File
@@ -0,0 +1,13 @@
"""系统设置模型"""
from sqlalchemy import Column, String, Text, TIMESTAMP
from sqlalchemy.sql import func
from app.core.database import Base
class SystemSetting(Base):
__tablename__ = "system_settings"
key = Column(String(100), primary_key=True)
value = Column(Text, nullable=False)
description = Column(Text)
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())