mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-25 05:50:23 +00:00
本次提交包含多项优化和修复: 1. 修复CRUD初始化参数传递、搜索参数处理逻辑 2. 更新环境配置中的大模型相关参数 3. 重构部分服务方法命名,统一代码风格 4. 新增多个枚举类型,补充模型关联关系和加载选项 5. 优化查询参数类实现,完善字段校验逻辑 6. 调整Pydantic模型字段注释和类型定义 7. 简化并移除冗余的CRUD方法实现 8. 新增超级管理员权限装饰器 9. 修复邮件日志模型的租户关联和字段定义
105 lines
6.1 KiB
Python
105 lines
6.1 KiB
Python
from datetime import datetime
|
||
from typing import TYPE_CHECKING
|
||
|
||
from sqlalchemy import Boolean, DateTime, ForeignKey, Integer, String, Text
|
||
from sqlalchemy.orm import Mapped, mapped_column, relationship, validates
|
||
|
||
from app.core.base_model import ModelMixin, TenantMixin, UserMixin
|
||
|
||
if TYPE_CHECKING:
|
||
from app.api.v1.module_platform.email.model import EmailConfigModel
|
||
|
||
|
||
class EmailConfigModel(ModelMixin):
|
||
"""
|
||
邮件 SMTP 配置表
|
||
|
||
每条记录对应一套 SMTP 配置(平台级,tenant_id=1)。
|
||
通过 is_default 标记当前生效的配置;只能有一条 is_default=True。
|
||
"""
|
||
|
||
__tablename__: str = "platform_email_config"
|
||
__table_args__: dict = {"comment": "邮件 SMTP 配置表"}
|
||
|
||
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment="配置名称")
|
||
smtp_host: Mapped[str] = mapped_column(String(255), nullable=False, comment="SMTP 服务器地址")
|
||
smtp_port: Mapped[int] = mapped_column(Integer, nullable=False, default=465, comment="SMTP 端口(465=SSL, 587=TLS)")
|
||
smtp_user: Mapped[str] = mapped_column(String(255), nullable=False, comment="SMTP 登录用户名(发件邮箱)")
|
||
smtp_password: Mapped[str] = mapped_column(String(255), nullable=False, comment="SMTP 授权密码(AES 加密存储)")
|
||
from_name: Mapped[str] = mapped_column(String(100), nullable=False, default="FastapiAdmin", comment="发件人显示名")
|
||
use_tls: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True, comment="是否启用 SSL/TLS")
|
||
is_default: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, comment="是否为默认配置")
|
||
timeout: Mapped[int] = mapped_column(Integer, nullable=False, default=30, comment="连接超时(秒)")
|
||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
|
||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||
|
||
@validates("smtp_port")
|
||
def validate_port(self, key: str, value: int) -> int:
|
||
if value < 1 or value > 65535:
|
||
raise ValueError("SMTP 端口范围为 1-65535")
|
||
return value
|
||
|
||
@validates("timeout")
|
||
def validate_timeout(self, key: str, value: int) -> int:
|
||
if value < 5 or value > 120:
|
||
raise ValueError("超时时间应在 5-120 秒之间")
|
||
return value
|
||
|
||
|
||
class EmailTemplateModel(ModelMixin):
|
||
"""
|
||
邮件模板表
|
||
|
||
支持变量插值(Jinja2 语法),如 {{ username }}、{{ reset_link }}。
|
||
template_code 是唯一业务键,代码中通过 code 引用,不依赖自增 ID。
|
||
"""
|
||
|
||
__tablename__: str = "platform_email_template"
|
||
__table_args__: dict = {"comment": "邮件模板表"}
|
||
|
||
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment="模板名称")
|
||
template_code: Mapped[str] = mapped_column(String(100), nullable=False, unique=True, comment="模板编码(业务键)")
|
||
subject: Mapped[str] = mapped_column(String(255), nullable=False, comment="邮件主题(可含变量)")
|
||
body_html: Mapped[str] = mapped_column(Text, nullable=False, comment="邮件正文 HTML(Jinja2 模板)")
|
||
body_text: Mapped[str | None] = mapped_column(Text, nullable=True, default=None, comment="邮件纯文本版本(降级用)")
|
||
variables: Mapped[str | None] = mapped_column(Text, nullable=True, default=None, comment='模板变量说明(JSON 格式,如 {"username": "用户名", "link": "链接"})')
|
||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
|
||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||
|
||
@validates("template_code")
|
||
def validate_code(self, key: str, value: str) -> str:
|
||
value = value.strip()
|
||
if not value:
|
||
raise ValueError("模板编码不能为空")
|
||
return value
|
||
|
||
|
||
class EmailLogModel(ModelMixin, TenantMixin, UserMixin):
|
||
"""
|
||
邮件发送日志表
|
||
|
||
记录每次邮件发送的结果,支持重试追踪。
|
||
status 覆盖 ModelMixin:0=待发送 1=成功 2=失败。
|
||
tenant_id 为可空:平台主动发送邮件(注册/重置密码等)无租户归属。
|
||
"""
|
||
|
||
__tablename__: str = "platform_email_log"
|
||
__table_args__: dict = {"comment": "邮件发送日志表"}
|
||
__loader_options__: list[str] = ["created_by", "updated_by", "deleted_by", "tenant_by"]
|
||
|
||
config_id: Mapped[int | None] = mapped_column(Integer, ForeignKey("platform_email_config.id", ondelete="SET NULL", onupdate="CASCADE"), nullable=True, index=True, comment="使用的 SMTP 配置 ID")
|
||
template_code: Mapped[str | None] = mapped_column(String(100), nullable=True, comment="模板编码(冗余存储,模板删除后仍可追溯)")
|
||
to_email: Mapped[str] = mapped_column(String(255), nullable=False, index=True, comment="收件人邮箱")
|
||
to_name: Mapped[str | None] = mapped_column(String(100), nullable=True, comment="收件人姓名")
|
||
subject: Mapped[str] = mapped_column(String(255), nullable=False, comment="邮件主题(渲染后)")
|
||
biz_type: Mapped[str] = mapped_column(String(50), nullable=False, default="other", comment="业务类型(register/reset_password/invite/expiry_warning/ticket_reply/other)")
|
||
error_msg: Mapped[str | None] = mapped_column(Text, nullable=True, default=None, comment="失败原因")
|
||
retry_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0, comment="重试次数")
|
||
tenant_id: Mapped[int | None] = mapped_column(Integer, nullable=True, index=True, comment="关联租户 ID(可为空,如平台注册邮件)")
|
||
sent_time: Mapped[datetime | None] = mapped_column(DateTime, nullable=True, default=None, comment="实际发送时间")
|
||
status: Mapped[int] = mapped_column(Integer, default=0, nullable=False, comment="状态(0:启动 1:停用)", index=True)
|
||
description: Mapped[str | None] = mapped_column(Text, default=None, nullable=True, comment="备注")
|
||
|
||
# 关联关系
|
||
config: Mapped["EmailConfigModel | None"] = relationship("EmailConfigModel", lazy="selectin")
|