FastAPi初学Day03 今天学的是项目的搭建第一部分企业软件开发流程1.1 软件生命周期SDLCSDLCSoftware Development Life Cycle 软件从想法到上线的完整过程各阶段说明阶段核心问题主要产出需求分析用户要什么边界在哪需求文档、用例系统设计架构怎么搭表怎么建架构图、ER 图、API 设计编码实现按设计写代码源代码、单元测试测试验证对不对稳不稳测试报告、Bug 列表部署上线怎么跑在生产环境部署脚本、环境配置运维迭代上线后怎么改版本计划、Changelog阶段核心问题主要产出需求分析用户要什么边界在哪需求文档、用例系统设计架构怎么搭表怎么建架构图、ER 图、API 设计编码实现按设计写代码源代码、单元测试测试验证对不对稳不稳测试报告、Bug 列表部署上线怎么跑在生产环境部署脚本、环境配置运维迭代上线后怎么改版本计划、Changelog重要原则不要跳过需求和设计直接写代码。 在本项目中前端原型 需求分析 shared-types 已经替你完成了大部分「需求与设计」——你要做的是读懂它们而不是无视它们自己发明字段名。1.2 瀑布 vs 敏捷瀑布模型传统需求 ──→ 设计 ──→ 开发 ──→ 测试 ──→ 上线 每个阶段结束才进入下一阶段变更成本高适合需求极其稳定、合同型项目。敏捷模型现代互联网主流┌── Sprint 1 ──┐ ┌── Sprint 2 ──┐ ┌── Sprint 3 ──┐ │ 登录用户 │ │ 职位认证 │ │ 简历投递 │ ... │ 设计→开发→测 │ │ 设计→开发→测 │ │ 设计→开发→测 │ └──────可演示───┘ └──────可演示───┘ └──────可演示───┘特点短周期迭代本课程建议 12 周一个 Sprint每轮都有可演示成果拥抱变化 — 需求可以微调但契约变更要同步文档本课程采用敏捷迭代 契约先行。1.3 团队角色与分工标准软件团队角色角色职责产品经理需求、优先级、验收架构师技术选型、接口、规范前端工程师页面、交互、对接 API后端工程师API、数据库、业务逻辑测试工程师用例、Bug、回归运维工程师部署、监控角色职责产品经理需求、优先级、验收架构师技术选型、接口、规范前端工程师页面、交互、对接 API后端工程师API、数据库、业务逻辑测试工程师用例、Bug、回归运维工程师部署、监控第二部分项目原型分析git地址:https://gitee.com/2415621370/new_boss_vue.git2.1 求职者端boss-candidate-ui模块清单模块 ID模块名称路由功能描述C-AUTH认证/login,/register,/bind-phone登录、注册、绑手机C-HOME首页/candidate/home推荐职位、热门标签/城市C-JOB职位/candidate/jobs,/jobs/:id列表、筛选、详情、投递C-RESUME简历/candidate/resume在线简历编辑、完整度C-DELIVERY投递/candidate/delivery投递记录与状态C-CHAT消息/candidate/chat,/chat/:hrId会话列表、聊天窗口C-FAV收藏/candidate/favorites收藏职位列表C-PROFILE我的/candidate/profile,/realname-auth个人信息、实名认证账号密码登录输入手机号11 位、密码≥6 位可选记住账号输出JWT、candidateInfophone、name、avatar、resumeStatus验证码登录、钉钉登录职位搜索筛选维度关键词、城市、薪资区间、经验、学历列表字段职位名、公司名、薪资、城市、经验、学历、标签职位详情展示职位描述、任职要求、福利、公司信息操作投递、收藏、立即沟通浏览量 1异步在线简历模块基本信息、工作经历、教育经历、项目经历、附件完整度计算规则与现有前端一致API 字段当前前端约定后端需对齐投递记录按状态筛选已投递、已查看、感兴趣、不合适、待面试、已发 Offer、已接受展示职位、公司、薪资、投递时间、HR 反馈操作查看详情、继续沟通及时沟通会话列表HR 头像、姓名、最后一条消息、时间、未读数聊天窗口文本消息、时间戳、已读状态支持 Enter 发送2.2 企业端boss-company-ui模块清单模块 ID模块名称路由功能描述B-AUTH登录/loginHR 账号登录B-CERT企业认证/company/certification提交认证材料B-DASH首页/company/dashboard今日概览、待办B-POS职位管理/company/positions/*列表、详情、发布B-RES简历中心/company/resumes/*投递简历列表与详情B-CHAT沟通/company/chat/*会话与聊天B-COMP公司主页/company/company-profile企业信息展示/编辑B-DATA数据看板/company/data招聘数据统计B-TEAM团队管理/company/team成员邀请与管理B-ACCT账号设置/company/account-settings改密、个人信息详细功能需求企业认证申请接口GET /api/v1/company/certification/status接口POST /api/v1/company/certification/applymultipart材料营业执照、法人身份证正反面、组织机构代码证可选职位列表筛选关键词、状态、城市字段ID、职位名、部门、城市、薪资、经验、学历、状态、浏览量、沟通数、简历数操作详情、编辑、沟通、查看简历、暂停/关闭、删除分页发布职位必填title、department、city、salaryMin/Max/Month、experience、education、number、description、requirements选填district、address、tags、welfare、gender提交后关联当前 HR 的companyId简历列表本质为 投递记录 的企业视角筛选关键词、期望职位、学历、经验、投递状态展示候选人姓名、期望职位、投递状态、匹配度可 Mock操作查看详情、标记状态、发起聊天简历详情展示完整简历内容快捷操作感兴趣、不合适、待面试、发 Offer沟通管理会话列表需从后端拉取含未读数聊天窗口需 WebSocket 实时通信2.3 平台管理端boss-manage-ui模块清单模块 ID模块名称路由功能描述M-AUTH登录/login管理员登录M-DASH数据概览/admin/dashboard核心指标卡片M-DASH2数据大屏/admin/data-dashboard可视化大屏M-COMP企业管理/admin/companies,/companies/:id列表、详情、封禁M-CERT认证审核/admin/certification-list,/certification-audit/:id审核队列与操作M-POS职位监管/admin/positions,/positions/:id全平台职位、下架M-CAND求职者管理/admin/candidates,/candidates/:id列表、冻结M-CHAT会话质检/admin/chat-reviews,/session-detail/:id风险会话审核M-RPT举报工单/admin/report-tasks举报处理M-OPS内容/活动/admin/content-articles,/activities运营内容M-SYS角色/日志/admin/roles,/operation-logsRBAC、审计M-USER个人中心/admin/personal-center管理员资料详细功能需求数据概览指标今日新增企业、新增职位、新增求职者、待处理举报接口GET /api/admin/dashboard/metrics更新频率可接受 5 分钟缓存企业认证审核展示资质图片放大、下载展示工商核验结果MVP 可 Mock操作通过、拒绝必填拒绝原因通过后CompanyStatus.ACTIVE企业管理筛选企业名、行业、城市、状态操作查看详情、封禁/解封封禁后该企业职位不可被搜索HR 不可登录职位监管查看全平台职位操作下架、封禁封禁后求职者端不可见会话质检展示 flagged 会话列表风险等级、关键词详情完整聊天记录、敏感词高亮处罚警告、限制聊天、临时/永久封禁三部分思维也需搭建框架项目结构规划boss-api/ ├── main.py # FastAPI应用主入口 │ ├── requirements.txt # Python依赖包 │ └── app/ # 应用核心代码 ├── __init__.py │ │ ├── models/ # 数据模型层 (Tortoise ORM) │ ├── __init__.py │ ├── user.py # 用户相关模型 │ ├── apis/ # API路由层[接受参数,返回数据] │ ├── __init__.py │ └── user_api.py # 用户相关API │ └── schemas/ # 数据验证层 (Pydantic) │ ├── __init__.py │ └── user.py # 用户请求/响应模型 │ │ ├── services/ # 业务服务层 (逻辑) │ ├── __init__.py │ ├── user.py # 用户相关的业务代码/方法 │ │ ├── core/ # 核心文件 │ ├── __init__.py │ ├── database.py # 数据库连接信息 ├── config/ # 配置文件 │ ├── __init__.py │ ├── settings.py # 多环境配置数据库配置# app/core/database.py 数据库配置文件 这个文件定义了 Tortoise-ORM 连接 MySQL 数据库所需的所有配置信息 from app.config.settings import settings # TORTOISE_ORM 是 Tortoise-ORM 规定的配置字典变量名 # 后面用 register_tortoise 或 Aerich 时都会引用这个字典 TORTOISE_ORM { # 1. 连接配置 —— 定义数据库连接信息 connections: { # default 是默认连接的名字必须有一个 default default: { # engine指定数据库后端引擎MySQL 使用 tortoise.backends.mysql engine: tortoise.backends.mysql, # credentials数据库连接凭证包含主机、端口、用户名、密码等 credentials: { host: settings.db_host, # MySQL 服务器地址 port: settings.db_port, # MySQL 端口默认 3306 user: settings.db_user, # 数据库用户名 password: settings.db_password, # 数据库密码请根据实际情况修改 database: settings.db_name, # 数据库名称 minsize: settings.db_minsize, # 连接池最小连接数 maxsize: settings.db_maxsize, # 连接池最大连接数 charset: utf8mb4, # 字符集支持 emoji echo: settings.db_echo # 是否打印 SQL 语句开发环境建议开启 } } }, # 2. 应用配置 —— 指定模型所在的模块 apps: { # models 是应用的名字可以自定义但 Aerich 需要使用这个名字 models: { # models 列表指定包含 Tortoise 模型类的 Python 模块路径 # aerich.models 是 Aerich 的内置模型用于记录迁移历史必须包含 models: [app.models, aerich.models], # default_connection指定这个应用使用哪个数据库连接 default_connection: default, } }, # 3. 时区配置 use_tz: False, # 是否使用时区 timezone: Asia/Shanghai, # 时区设置 echo: True # ✅ 关键打开 SQL 打印 }多环境配置依赖pip install pydantic-settingsimport os from typing import Optional from pydantic_settings import BaseSettings, SettingsConfigDict class BaseAppSettings(BaseSettings): 基础配置类所有环境共享 model_config SettingsConfigDict( env_file_encodingutf-8, case_sensitiveFalse, extraignore, # 让子类继承 env 配置不会被覆盖掉这是核心修复 env_file.env, ) # 通用配置 app_title: str BOSS服务端项目 app_version: str V1.0.0 api_prefix: str /api/v1 app_description: str Boss项目的接口文档,包含求职者端,企业端,管理端 class DevAppSettings(BaseAppSettings): 开发环境 model_config SettingsConfigDict(env_file.env.dev) # 服务 server_port: int 8000 debug_mode: bool True # 数据库 db_url: str mysqlpymysql://root:123456localhost:3306/fastApiProject004 db_host: str localhost db_port: int 3306 db_user: str root db_password: str 970523 db_name: str boss-api db_echo: bool True db_minsize: int 1 db_maxsize: int 5 DEBUG: bool True test_abc:str 123 # 安全 secret_key: str dev-secret-key-123456-pydantic jwt_token_secret_key: str dhsjjdkfjdkfrjfrjgr-278783jkdsdhjdhjsds-dsdksjdkajskajieuiwueiwhdshmxzxno9iy token_expire_minutes: int 120 cors_allow_origins: list[str] [*] # 阿里云OSS ALIYUN_OSS_ACCESS_KEY_ID: str KEY ALIYUN_OSS_ACCESS_KEY_SECRET: str KEY ALIYUN_OSS_ENDPOINT: str oss-cn-beijing.aliyuncs.com ALIYUN_OSS_BUCKET_NAME: str fastapi-project-004 # 钉钉 DINGTALK_APP_KEY: str bf8ad584-a6d1-454d-8291-5e0158b4722b DINGTALK_REDIRECT_URI: str http://127.0.0.1:8000/third_party/dingtalk/login/callback DINGTALK_CLIENT_ID: str DINGTALK_CLIENT_SECRET: str REDIS_HOST: str 127.0.0.1 REDIS_PORT: int 6379 REDIS_DB: int 8 DEFAULT_AVATAR: str https://img10.360buyimg.com/pcpubliccms/s1440x1440_jfs/t1/240214/16/3793/62089/65acb64bF35c090ae/4cce5ee81fae5a23.jpg.avif # 高德地图 AMAP_SERVRER_KEY: str d0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0 # 微信支付 V3 MCH_ID: str 1558950191 MCH_SERIAL_NO: str 34345964330B66427E0D3D28826C4993C77E631F PRIVATE_KEY_PATH: str apiclient_key.pem API_V3_KEY: str UDuLFDcmy5Eb6o0nTNZdu6ek4DDh4K8B APP_ID: str wx74862e0dfcf69954 DOMAIN: str https://api.mch.weixin.qq.com NOTIFY_DOMAIN: str https://xxx.ngrok.io PARTNER_KEY: str T6m9iK73b0kn9g5v426MKfHQH7X8rKwb property def NATIVE_ORDER_URL(self): return f{self.DOMAIN}/v3/pay/transactions/native property def QUERY_ORDER_URL(self): return f{self.DOMAIN}/v3/pay/transactions/id/ property def CLOSE_ORDER_URL(self): return f{self.DOMAIN}/v3/pay/transactions/out-trade-no/%s/close property def REFUND_URL(self): return f{self.DOMAIN}/v3/refund/domestic/refunds property def QUERY_REFUND_URL(self): return f{self.DOMAIN}/v3/refund/domestic/refunds/%s class TestAppSettings(BaseAppSettings): 测试环境 model_config SettingsConfigDict(env_file.env.test) server_port: int 8001 debug_mode: bool False db_url: str mysqlpymysql://root:123456localhost:3306/test_db secret_key: str test-secret-key-789012-pydantic cors_allow_origins: list[str] [https://test.yourdomain.com] class ProdAppSettings(BaseAppSettings): 生产环境 敏感配置 无默认值必须从环境变量或 .env.prod 读取 更安全、更规范 model_config SettingsConfigDict(env_file.env.prod) server_port: int 80 debug_mode: bool False cors_allow_origins: list[str] [https://yourdomain.com] # 生产必须配置不能为空 db_url: str secret_key: str jwt_token_secret_key: str db_host: str db_port: int db_user: str db_password: str db_name: str # 支付/OSS/第三方 全部从环境变量读取不写死代码 ALIYUN_OSS_ACCESS_KEY_ID: str ALIYUN_OSS_ACCESS_KEY_SECRET: str API_V3_KEY: str MCH_ID: str APP_ID: str # 环境枚举 SUPPORTED_ENVS [dev, test, prod] def get_app_settings(env: Optional[str] None) - BaseAppSettings: 多环境配置工厂标准写法 优先级传入参数 系统环境变量 默认 dev env env or os.getenv(FASTAPI_ENV, dev) if env not in SUPPORTED_ENVS: raise ValueError(f环境错误支持{SUPPORTED_ENVS}) env_map { dev: DevAppSettings, test: TestAppSettings, prod: ProdAppSettings, } return env_map[env]() # 全局唯一配置实例 settings get_app_settings()在项目的根目录 创建.envAPP_TITLEBOSS服务端项目 app_versionV1.0.0 api_prefix/api/v1 app_descriptionBoss项目的接口文档,包含求职者端,企业端,管理端.env.dev# 开发环境 —— 对应 DevAppSettingsFASTAPI_ENVdev默认时加载 SERVER_PORT8000 DEBUG_MODETrue test_abc123 DB_HOST127.0.0.1 DB_PORT3306 DB_USERroot DB_PASSWORD970523 DB_NAMEboss-api DB_ECHOTrue DB_MINSIZE1 DB_MAXSIZE5 SECRET_KEYdev-secret-key JWT_TOKEN_SECRET_KEYdev-jwt-secret-key TOKEN_EXPIRE_MINUTES120 CORS_ALLOW_ORIGINS[*] REDIS_HOST127.0.0.1 REDIS_PORT6379 REDIS_DB8 # 第三方密钥课堂演示可填正式项目请用各自申请的密钥且不要提交公开仓库 ALIYUN_OSS_ACCESS_KEY_ID ALIYUN_OSS_ACCESS_KEY_SECRET ALIYUN_OSS_BUCKET_NAME DINGTALK_CLIENT_ID DINGTALK_CLIENT_SECRET MCH_ID API_V3_KEY APP_ID.env.prod.env.test配置启动日志# app/core/logging.py from loguru import logger import sys import logging from pathlib import Path from app.config.settings import settings # 移除默认处理器 logger.remove() # 控制台输出开发环境 logger.add( sys.stdout, formatgreen{time:YYYY-MM-DD HH:mm:ss}/green | level{level: 8}/level | cyan{name}/cyan:cyan{function}/cyan:cyan{line}/cyan - level{message}/level, levelDEBUG if settings.DEBUG else INFO, colorizeTrue ) # 创建日志目录 log_dir Path(logs) log_dir.mkdir(exist_okTrue) # INFO 级别及以上日志文件 logger.add( log_dir / info_{time:YYYY-MM-DD}.log, rotation00:00, # 每天午夜轮转 retention30 days, # 保留 30 天 compressionzip, # 压缩旧日志 levelINFO, format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} - {message}, encodingutf-8, filterlambda record: record[level].name INFO ) # WARNING 级别日志文件 logger.add( log_dir / warning_{time:YYYY-MM-DD}.log, rotation00:00, # 每天午夜轮转 retention30 days, # 保留 30 天 compressionzip, # 压缩旧日志 levelWARNING, format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} - {message}, encodingutf-8, filterlambda record: record[level].name WARNING ) # ERROR 级别及以上日志文件 logger.add( log_dir / error_{time:YYYY-MM-DD}.log, rotation00:00, # 每天午夜轮转 retention90 days, # 保留 90 天错误日志保留更长时间 compressionzip, # 压缩旧日志 levelERROR, format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} - {message}, encodingutf-8, filterlambda record: record[level].name in [ERROR, CRITICAL] ) # 全量日志文件包含所有级别 logger.add( log_dir / all_{time:YYYY-MM-DD}.log, rotation00:00, # 每天午夜轮转 retention7 days, # 保留 7 天 compressionzip, # 压缩旧日志 levelDEBUG, format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name}:{function}:{line} - {message}, encodingutf-8 ) # SQL 日志文件专门记录数据库 SQL 语句 def sql_filter(record): 过滤 SQL 相关的日志 name record[name].lower() message record[message].lower() # 捕获 tortoise 后端相关的日志特别是包含 SQL 语句的日志 return ( tortoise.backends in name or sql in message or select in message or insert in message or update in message or delete in message or create in message or alter in message ) logger.add( log_dir / sql_{time:YYYY-MM-DD}.log, rotation00:00, # 每天午夜轮转 retention30 days, # 保留 30 天 compressionzip, # 压缩旧日志 levelDEBUG, format{time:YYYY-MM-DD HH:mm:ss} | {level: 8} | {name} - {message}, encodingutf-8, filtersql_filter ) # 配置标准 logging 模块将 Tortoise ORM 的日志转发到 loguru class InterceptHandler(logging.Handler): 拦截标准 logging 的输出转发到 loguru def emit(self, record): # 获取对应的 loguru 级别 try: level logger.level(record.levelname).name except ValueError: level record.levelno # 找到调用者信息 frame, depth sys._getframe(6), 6 while frame and frame.f_code.co_filename logging.__file__: frame frame.f_back depth 1 logger.opt(depthdepth, exceptionrecord.exc_info).log(level, record.getMessage()) # 配置 Tortoise ORM 的 logger def setup_tortoise_logging(): 配置 Tortoise ORM 的日志输出 # 拦截所有 tortoise 相关的 logger logging_loggers [ asyncmy, tortoise, tortoise.backends, tortoise.backends.mysql, tortoise.backends.asyncpg, tortoise.backends.sqlite, ] for logger_name in logging_loggers: logging_logger logging.getLogger(logger_name) logging_logger.handlers [InterceptHandler()] logging_logger.setLevel(logging.DEBUG if settings.db_echo else logging.INFO) logging_logger.propagate False # 初始化 Tortoise 日志配置 if settings.db_echo: setup_tortoise_logging() # 导出 logger __all__ [logger, setup_tortoise_logging]在main.py里asynccontextmanager async def lifespan(app:FastAPI): logger.info(正要启动数据库连接) await Tortoise.init(configTORTOISE_ORM,_enable_global_fallbackTrue) logger.info(数据库链接启动成功) yield await Tortoise.close_connections() logger.error(数据库连接已关闭)在core里创建middleware全局异常from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint from starlette.requests import Request from starlette.responses import Response from app.core.logging import logger class LoggingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next:RequestResponseEndpoint) - Response: method request.method qp request.query_params url request.url logger.info(f请求开始:{method} {url} {qp}) response await call_next(request) return response收获反思多表开发一定要理清外键关联规则 尽量提前做好业务校验不要让数据库抛出原始错误直接返回前端。