当前位置: 首页 > news >正文

给 FastApiAdmin 加个“会议纪要”模块,我把后端二次开发的坑踩了个遍

本文能帮你解决什么

✅ 看懂 FastapiAdmin 后端的真实目录结构(和你想的不一样)

✅ 手把手新增一个完整的业务模块(model → schema → crud → service → controller)

✅ 避开路由注册、权限集成和前端联调的深坑

🧭 主要内容脉络

真实项目结构一览

➡️ 二次开发标准流程

➡️ 实战:增加“会议纪要”模块

➡️ 常见翻车现场与避坑指南

1. 先搞懂真实的项目结构,不然代码都不知道放哪

我当初 git clone 下来,看到的是这样的:

FastapiAdmin/ ├── backend/ # 后端工程,我们的主战场 │ └── app/ │ ├── core/ # 核心工具库 │ ├── config/ # Settings │ ├── utils/ # 通用工具类 │ ├── scripts/# 启动脚本 │ ├── plugin/ # 动态路由 │ └── api/ # 静态路由 │ └── v1/ │ ├── module_system/ │ ├── module_monitor/ │ ├── module_common/ │ ├── module_application/ │ └── portal/ # 一个完整的模块示例 │ ├── controller.py # 路由与请求处理 │ ├── crud.py # 数据库增删改查 │ ├── model.py # SQLAlchemy 模型 │ ├── schema.py # Pydantic 校验 │ └── service.py # 业务逻辑 ├── frontend/ # Vue3 前端工程 └── docker/ # Docker部署相关

看到没,它是把一个业务模块的所有东西打成一个小包,放在一个文件夹里,跟常见的那种 models/ apis/ services/ 分开平铺的结构完全不同。

你可能会问:“那我要新增一个模块怎么办?”照着 portal 复制一份,改吧改吧就行了,后面我一步步说。

2. 二次开发的标准流程:五个文件,一个都不能少

捋一下每个文件的职责,心里先有个谱:

🔹model.py— 定义数据库表结构,就是 SQLAlchemy 的模型类。

🔹schema.py— 接口的请求/响应数据结构,用 Pydantic 定义。

🔹crud.py— 只管和数据库打交道,增删改查全都放这里。

🔹service.py— 业务逻辑层,比如创建纪要前要校验会议时间是否冲突。

🔹controller.py— API 路由,接收请求、调 service、返回响应。

这个分法很干净,维护起来特别舒服。我一开始还想把逻辑全部塞到 controller 里,后来改需求改到崩溃,千万别学我当初偷懒

当然,说是一个都不能少,如果你只是个简单的接口响应返回,只有一个 controller 也是Ok的!

3. 实战演示:手把手增加“会议纪要”模块

📋 需求:

增删改查会议纪要,字段:标题、参会人员、纪要内容、会议日期。

🔹 第1步:新建模块文件夹

在 module_application 下复制 portal 文件夹,重命名为 meeting,里面原有文件清空,咱们从头写。

🔹 第2步:写 model.py

from sqlalchemy import Column, Integer, String, Date, Text from app.core.base_model import ModelMixin, UserMixin # 注意这个导入路径,根据实际情况调整 class MeetingMinutes(ModelMixin, UserMixin): __tablename__ = "meeting_minutes" title = Column(String(200), nullable=False, comment="会议标题") attendees = Column(String(500), nullable=False, comment="参会人员") content = Column(Text, nullable=True, comment="纪要内容") meeting_date = Column(Date, nullable=False, comment="会议日期")

这里有个坑:一定要继承项目自己的 base_model,它把 id、create_time 这些通用字段全封装好了,别自己再定义一遍,不然字段冲突搞得你怀疑人生。

🔹 第3步:写 schema.py

from app.core.base_schema import BaseSchema from datetime import date class MeetingCreate(BaseSchema): title: str attendees: str content: str | None = None meeting_date: date class MeetingUpdate(MeetingCreate): pass class MeetingOut(MeetingCreate): id: int create_time: str class Config: from_attributes = True

🔹 第4步:写 crud.py

from app.core.base_crud import CRUDBase from .model import MeetingMinutes from .schema import MeetingCreate, MeetingUpdate, MeetingOut class MeetingCRUD(CRUDBase[MeetingMinutes, MeetingCreate, MeetingUpdate]): def __init__(self, auth: AuthSchema) -> None: """ 初始化CRUD数据层,在CRUDBase中已封装了数据库的常用操作 """ super().__init__(model=MeetingMinutes) async def get_list( self, search: dict | None = None, order_by: list[dict] | None = None, preload: list[str] | None = None, ) -> Sequence[MeetingMinutes]: """ 列表查询 参数: - search (dict | None): 查询参数 - order_by (list[dict] | None): 排序参数 - preload (list[str] | None): 预加载关系,未提供时使用模型默认项 返回: - Sequence[MeetingMinutes]: 模型实例序列 """ return await self.list(search=search, order_by=order_by, preload=preload) async def create(self, data: MeetingCreate) -> MeetingMinutes | None: return await self.create(data=data) async def update(self, id: int, data: MeetingUpdate) -> MeetingMinutes | None: return await self.update(id=id, data=data)

这要要注意:如果遇到要操作数据库,先去 CRUDBase 里面看看有没有已经封装好的方法,如果有,就不要再造轮子了,直接传参调用即可!

🔹 第5步:写 service.py

from .crud import MeetingCRUD from .schema import MeetingCreate, MeetingUpdate, MeetingOut class MeetingService: @classmethod async def create_meeting(cls, data: MeetingCreate): # 这里可以加业务校验,比如会议时间不能早于今天 return await MeetingCRUD.create(data=data) @classmethod async def update_meeting(cls, meeting_id: int, data: MeetingUpdate): return await MeetingCRUD.update(id=meeting_id, data=data)

🔹 第6步:写 controller.py

from fastapi import APIRouter from .service import MeetingService from .schema import MeetingCreate, MeetingUpdate, MeetingOut from app.common.response import ResponseSchema, SuccessResponse MeetingRouter = APIRouter(route_class=OperationLogRoute, prefix="/meeting", tags=["会议纪要"]) @MeetingRouter.post("/", response_model=ResponseSchema[MeetingOut]) async def create_meeting(data: MeetingCreate): result_dict = await MeetingService.create_metting(data=data) log.info(f"创建成功: {result_dict.get('title')}") return SuccessResponse(data=result_dict, msg="创建成功")

🔹 第7步:注册路由(最容易漏!)

去 module_application 下的初始化包文件 __init_.py 里,加上:

from .metting.controller import MettingRouter application_router.include_router(MettingRouter)

我当初写好 controller 启动服务,结果 404,查了半天才发现路由压根没注册

但不知道你有没有注意到项目目录结构里有个plugin目录,我在 scripts/init_app.py 里的 register_routers() 方法里看到了这句代码:

# 先将动态路由注册到应用,使用速率限制器 from app.core.discover import get_dynamic_router # 获取动态路由实例 app.include_router( router=get_dynamic_router(), dependencies=[Depends(RateLimiter(times=5, seconds=10))], )

进入方法里面看细节,发现如果把整个自定义应用包放到 plugin 目录里,在初始化应用时,会自动查找包里的 controller 里的 Router 定义并自动载入到应用中,这妥妥的插件化开发呀!

4. 常见翻车现场与避坑指南

🔴数据库迁移别手动改表:FastapiAdmin 用了 Alembic,写完 model 记得跑 uv run main.py revision --env=dev,不然上线后表结构对不上,哭都来不及。

🔴权限校验别忘加:新模块接口默认不挂权限,得去 RBAC 菜单管理里配上,否则用户连 403 都报不出来,直接 404 让你找半天。

🔴前端菜单要手动配:后端只管接口,左侧菜单栏的入口得去前端菜单管理页面手动添加,不然数据能查但用户找不到入口,还以为你没做。

5. 我的血泪总结

FastapiAdmin 这种全栈脚手架,最大的价值不是代码多厉害,而是它逼着你按一套清晰的套路出牌。model → schema → crud → service → controller 这条链走顺了,后面加再多的模块都不怕。

http://www.jsqmd.com/news/1092437/

相关文章:

  • EMI滤波电感差异化选型设计要点
  • 如何高效管理Windows窗口:3种简单方法释放任务栏空间
  • TAS5756M数字音频放大器:BD调制、零检测与miniDSP实战解析
  • MSP430X地址指令与FLL+时钟模块:20位寻址与低功耗时钟管理实战
  • 5步构建企业级数据治理平台:Datavines实战指南
  • 白宫前脚下了限制令,OpenAI 后脚就把 GPT-5.6 发了重磅事件 #1 OpenAI 正式发布 GPT-5.6“
  • 终极Android Git客户端:随时随地高效管理代码仓库的完整指南
  • DownKyi视频管理方案:解决B站内容本地化存储的技术工作流
  • Linux时区修改为CST
  • 深入解析I2C控制器与目标模式:从协议到UNICOMM-I2C硬件实现
  • 芝麻粒TK版:蚂蚁森林自动化工具的高效配置与使用指南
  • DedeCMS文件上传漏洞复现与防御:从代码审计到安全加固实战
  • 如何在macOS上使用OBS虚拟摄像头:提升视频会议品质的完整指南
  • C++实现Diffie-Hellman密钥交换:从数学原理到代码实战
  • TI ESP430CE1电能计量模块寄存器配置与单相电表应用实战
  • ProperTree终极指南:掌握跨平台Plist编辑器的完整使用技巧
  • 3步搞定!免费开源的微信聊天记录永久备份工具WeChatExporter终极指南
  • Java面试神册:2026下半年程序员面试必刷!
  • 零成本自建PikPak网页版:手把手教你用GitHub与Cloudflare Workers搭建私有磁力网盘
  • Awesome IPFS:IPFS 生态项目合集
  • 沈阳零基础入行解读:穿越机为什么成为低空经济新蓝海?
  • 电源调试工具的革命性突破:3大功能解决AMD处理器系统稳定性难题
  • 构建基础设施
  • 打造AI时代不可替代的高语境资产
  • 从PV、EV、AC到EAC、ETC:一文掌握项目成本绩效的核心公式与实战解读
  • YimMenu终极指南:免费GTA5菜单工具完整使用教程与安全防护
  • 喜保宁与氯巴占联用还是单用,难治性局灶发作治疗策略解析
  • 一键修复Windows软件兼容性问题:VisualCppRedist AIO完整解决方案指南
  • Xilinx FPGA LVDS接口设计:从IBUFDS到自环测试的工程实践
  • 正负样本比例不平衡