bea288e414
== 已部署上线 (9项) == - 代办事项真实数据源集成 (企微审批API 8bug修复链) - H5/坐席端 Logo样式统一+绿色背景 - 视频引导页修复 (localStorage key v2) - 坐席端 v9 Vue版本修复 (ElMessage._context) - 截图按钮 v10 修复 (getDisplayMedia user gesture) - 扫码样式恢复+H5扫码登录跳转修复 - H5截图快捷键提示 == 代码完成待部署 (3项) == - 知识迭代3Bug修复 (#8 POST端点/#7 MERGE幂等/#6 过期检查) - 会议室预定-小鱼易联终端 (40文件, 40/40测试通过) - IT资产升级审批推送 (asset_service.py) == 需求文档 (2项) == - 坐席端AI辅助消息框-PRD (4项新功能确认) - 坐席端布局优化建议 v2.0 (7天计划) == 新增文档 == - 日报-2026-07-11.md - 知识迭代Bug修复报告-20260711.md - 会议室预定-部署指南.md - CHANGELOG.md 更新 == 测试 == - test_todo_integration.py: 40/40 - test_meetingroom.py: 40/40 - test_bugfix_ki_suggestions.py: 21/21
143 lines
4.3 KiB
Python
143 lines
4.3 KiB
Python
# =============================================================================
|
||
# 企微IT智能服务台 — 登录日志模型
|
||
# =============================================================================
|
||
# 说明:对应数据库 login_logs 表,记录所有登录尝试(成功/失败),用于安全审计和故障排查。
|
||
# 三端认证重构:统一认证后,所有登录方式(oauth/qrcode/bind)都记录到此表。
|
||
# =============================================================================
|
||
|
||
import uuid
|
||
from datetime import datetime
|
||
|
||
from sqlalchemy import DateTime, Index, String
|
||
from sqlalchemy.orm import Mapped, mapped_column
|
||
|
||
from app.database import Base
|
||
|
||
|
||
class LoginLog(Base):
|
||
"""登录日志模型 — 对应 login_logs 表。
|
||
|
||
记录所有登录尝试(成功/失败),用于安全审计和故障排查。
|
||
|
||
Attributes:
|
||
id: 主键(UUID)
|
||
employee_id: 员工UserID(成功登录时有值)
|
||
corp_id: 企业ID
|
||
login_method: 登录方式(oauth/qrcode/bind)
|
||
login_source: 登录来源(h5/agent/admin)
|
||
ip_address: 客户端IP
|
||
user_agent: 客户端User-Agent
|
||
status: 登录状态(success/failed/cancelled)
|
||
fail_reason: 失败原因
|
||
created_at: 登录时间
|
||
"""
|
||
|
||
# 表名
|
||
__tablename__ = "login_logs"
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 字段定义
|
||
# --------------------------------------------------------------------------
|
||
|
||
# 主键:UUID
|
||
id: Mapped[str] = mapped_column(
|
||
String(36),
|
||
primary_key=True,
|
||
default=lambda: str(uuid.uuid4()),
|
||
comment="登录日志唯一标识",
|
||
)
|
||
|
||
# 员工UserID(成功登录时有值)
|
||
employee_id: Mapped[str] = mapped_column(
|
||
String(64),
|
||
nullable=True,
|
||
comment="企微员工UserID(成功登录时有值)",
|
||
)
|
||
|
||
# 企业微信企业ID
|
||
corp_id: Mapped[str] = mapped_column(
|
||
String(64),
|
||
nullable=False,
|
||
comment="企业微信企业ID",
|
||
)
|
||
|
||
# 登录方式
|
||
# oauth: 企微内OAuth静默授权
|
||
# qrcode: 企微外扫码登录
|
||
# bind: 互联企业账号绑定
|
||
login_method: Mapped[str] = mapped_column(
|
||
String(20),
|
||
nullable=False,
|
||
comment="登录方式: oauth/qrcode/bind",
|
||
)
|
||
|
||
# 登录来源
|
||
# h5: 员工H5端
|
||
# agent: 坐席端
|
||
# admin: 管理后台
|
||
login_source: Mapped[str] = mapped_column(
|
||
String(20),
|
||
nullable=False,
|
||
comment="登录来源: h5/agent/admin",
|
||
)
|
||
|
||
# 客户端IP(IPv6支持)
|
||
ip_address: Mapped[str] = mapped_column(
|
||
String(45),
|
||
nullable=True,
|
||
comment="客户端IP地址",
|
||
)
|
||
|
||
# 客户端User-Agent
|
||
user_agent: Mapped[str] = mapped_column(
|
||
String(512),
|
||
nullable=True,
|
||
comment="客户端User-Agent",
|
||
)
|
||
|
||
# 登录状态
|
||
# success: 登录成功
|
||
# failed: 登录失败
|
||
# cancelled: 用户取消/授权超时
|
||
status: Mapped[str] = mapped_column(
|
||
String(20),
|
||
nullable=False,
|
||
comment="登录状态: success/failed/cancelled",
|
||
)
|
||
|
||
# 失败原因
|
||
fail_reason: Mapped[str] = mapped_column(
|
||
String(256),
|
||
nullable=True,
|
||
comment="失败原因",
|
||
)
|
||
|
||
# 登录时间
|
||
created_at: Mapped[datetime] = mapped_column(
|
||
DateTime(timezone=True),
|
||
nullable=False,
|
||
default=datetime.now,
|
||
comment="登录时间",
|
||
)
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 索引和约束定义
|
||
# --------------------------------------------------------------------------
|
||
__table_args__ = (
|
||
# 按员工ID查询登录历史
|
||
Index("idx_login_logs_employee_id", "employee_id"),
|
||
# 按企业ID查询
|
||
Index("idx_login_logs_corp_id", "corp_id"),
|
||
# 按登录时间倒序查询(常用)
|
||
Index("idx_login_logs_created_at", "created_at"),
|
||
# 按状态查询
|
||
Index("idx_login_logs_status", "status"),
|
||
)
|
||
|
||
def __repr__(self) -> str:
|
||
"""登录日志对象的字符串表示。"""
|
||
return (
|
||
f"<LoginLog(id={self.id}, employee_id={self.employee_id}, "
|
||
f"login_method={self.login_method}, status={self.status})>"
|
||
)
|