FastAPI 后端实战:企业审核功能设计(审核记录 + 状态流转)
FastAPI 后端实战企业审核功能设计审核记录 状态流转记录今天在招聘类项目仿 BOSS 直聘后端写的「企业审核」功能。技术栈FastAPI Tortoise ORM MySQL Redis 阿里云 OSS。只讲后端不展开前端。一、需求与约定企业提交认证资料后需要后台进行审核通过 / 驳回并且要能查看完整的审核历史时间线展示同时审核状态要驱动账号能否登录。核心约定审核结果用整数枚举0提交申请 /1通过 /2驳回审核记录采用**追加append-only**方式每次操作都新增一条而不是覆盖这样完整历史不会丢账号是否通过审核以最新的那条审核记录为准而不是单独在别处维护一个状态位二、数据模型新增一张「企业审核表」挂在企业主表t_enterprise下面用enterprise_id关联# app/models/enterprise.pyclassEnterpriseReview(Model):企业审核表idfields.IntField(pkTrue,description主键ID)review_resultfields.IntField(nullTrue,description审核结果(1:通过,2:驳回))review_reasonfields.CharField(max_length512,nullTrue,description拒绝原因)remarkfields.TextField(nullTrue,description备注说明)audit_timefields.DatetimeField(nullTrue,description审核时间)audit_userfields.CharField(max_length100,nullTrue,description审核人)enterprise_idfields.IntField(nullTrue,description企业ID)classMeta:tablet_enterprise_reviewtable_description企业审核表对应迁移文件migrations/models/6_20260728193745_update.py里建表CREATE TABLE IF NOT EXISTS t_enterprise_review(id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT主键ID,review_result INT COMMENT审核结果(1:通过,2:驳回),review_reason VARCHAR(512)COMMENT拒绝原因,remark LONGTEXT COMMENT备注说明,audit_time DATETIME(6)COMMENT审核时间,audit_user VARCHAR(100)COMMENT审核人,enterprise_id INT COMMENT企业ID)CHARACTER SET utf8mb4 COMMENT企业审核表;注意enterprise_id是可空的IntField而非硬外键——审核记录本质是「日志」用松散关联比强外键更省心删企业时也不会被外键约束卡住。三、校验模型入参模型只收审核需要的信息不收企业资料本身资料在提交环节已经存过了# app/schemas/enterprise.pyclassEnterpriseReviewCreateRequest(BaseModel):review_result:intField(...,description审核结果(0:提交申请,1:通过,2:驳回))review_reason:str|NoneField(None,description拒绝原因)remark:str|NoneField(None,description备注说明)audit_user:str|NoneField(None,description审核人)enterprise_id:intField(...,description企业ID)review_reason/remark/audit_user都允许为空audit_user在管理端没登录态时由后端默认填充见下review_reason只有在驳回时才需要。四、审核接口与核心逻辑两个接口一个是审核操作一个是查历史# app/apis/enterprise_api.pyenterprise_router.post(/review,summary企业审核,description企业审核)asyncdefenterprise_review(enterpriseReviewCreateRequest:EnterpriseReviewCreateRequest):awaitEnterpriseService.enterprise_review(enterpriseReviewCreateRequest)return{code:1,message:审核完成}enterprise_router.get(/review_records,summary查询企业审核记录)asyncdefselect_enterprise_review_records(enterprise_id:intQuery(...,title企业ID,description企业ID)):resawaitEnterpriseService.get_review_records(enterprise_id)return{code:1,message:查询成功,data:res}4.1 审核操作追加记录 通过时改主表状态# app/services/enterprise_service.pystaticmethodasyncdefenterprise_review(enterpriseReviewCreateRequest:EnterpriseReviewCreateRequest):enterprise_identerpriseReviewCreateRequest.enterprise_id audit_userenterpriseReviewCreateRequest.audit_useror平台管理员# 每次审核操作都追加一条审核记录审核历史便于前端展示历史审核记录awaitEnterpriseReview.create(enterprise_identerprise_id,review_resultenterpriseReviewCreateRequest.review_result,review_reasonenterpriseReviewCreateRequest.review_reason,remarkenterpriseReviewCreateRequest.remark,audit_useraudit_user,audit_timenow(),)ifenterpriseReviewCreateRequest.review_result1:enterpriseawaitEnterprise.get_or_none(identerprise_id)ifenterprise:enterprise.account_statusAccountStatus.NORMALawaitenterprise.save()# TODO 发送短信(手机号)或者邮件(邮箱)给 用户这里有两个设计点追加而非覆盖不管通过还是驳回都EnterpriseReview.create一条新记录。这样提交 → 驳回 → 重新提交 → 通过的完整流转在表里都查得到前端做时间线直接读这张表就行。通过才改主表状态只有review_result 1才把企业主表的account_status从PENDING_AUDIT改成NORMAL。驳回时不改主表状态——因为后续可能重新提交、再审核状态最终由「最新审核记录」决定见第五节登录校验。4.2 查询历史按 id 正序直接给前端时间线staticmethodasyncdefget_review_records(enterprise_id:int):查询企业的审核记录历史按时间正序供前端历史审核记录时间线展示recordsawaitEnterpriseReview.filter(enterprise_identerprise_id).order_by(id)result[]forrinrecords:result.append({id:r.id,enterprise_id:r.enterprise_id,review_result:r.review_result,review_reason:r.review_reason,remark:r.remark,audit_user:r.audit_user,audit_time:r.audit_time,})returnresultorder_by(id)保证时间正序前端拿到就是天然的「提交 → 审核1 → 审核2…」时间线不需要再排序。五、两个联动点审核功能不是孤立的提交认证和登录都要和它挂钩。5.1 提交认证时写第一条审核记录企业提交认证资料saveEnterpriseInfo时在事务里先写一条review_result0的「提交申请」记录保证审核历史从提交那一刻就完整# 写入第一条审核记录企业提交认证申请awaitEnterpriseReview.create(enterprise_identerprise.id,review_result0,review_reasonNone,remark企业提交认证申请,audit_user企业,audit_timenow(),)5.2 登录门槛以「最新审核记录」为准企业登录时不查主表状态位而是取该企业最新一条审核记录判断# 取最新一条审核记录判断是否已通过审核enterprise_reviewawaitEnterpriseReview.filter(enterprise_identerprise_id).order_by(-id).first()ifenterprise_reviewisNoneorenterprise_review.review_result!1:# 审核通过raiseException(账号未审核通过)用order_by(-id).first()拿最新记录而不是维护一个独立的「当前状态」字段。好处是审核被驳回后再重新提交、再通过的流转都能正确反映——状态始终由最后一条记录说了算不会出现「主表已改但历史对不上」的不一致。六、小结今天企业审核这块后端落地了三样东西模型 迁移t_enterprise_review审核记录表enterprise_id松散关联。审核接口 服务POST /enterprise/review追加审核记录、通过时把主表账号状态置为正常GET /enterprise/review_records按 id 正序返回历史供时间线展示。两处联动提交认证写「提交申请」首条记录登录以最新审核记录判定是否放行。整套设计的关键就一句话审核历史用 append-only 记录表承载业务状态能否登录由最新一条记录派生既保留完整审计轨迹又避免了多处状态位不同步的坑。希望对做审核 / 审批类功能的同学有一点参考价值。

相关新闻

Python虚拟环境全解析:Conda与Venv对比及实战指南

Python虚拟环境全解析:Conda与Venv对比及实战指南

1. 项目概述:为什么我们需要虚拟环境?如果你刚开始学Python,或者已经写了几个脚本,可能觉得直接pip install把包装到系统里就完事了。但很快你就会遇到第一个大麻烦:项目A需要Django 3.2,而项目B需要Django…

2026/10/28 15:52:15 阅读更多 →
第1章:Python 术语全景与解释器工作原理

第1章:Python 术语全景与解释器工作原理

1. 项目背景 业务场景 食光集市技术团队刚刚度过第三轮融资后的第一个双十一。订单量从日均500单飙升到3万单,系统虽然没有挂,但技术负责人老张发现一个尴尬的问题:团队里七个开发人员,每个人对Python的理解深度参差不齐。有人以…

2026/10/28 16:34:21 阅读更多 →
信号采样与混叠:从奈奎斯特原理到CAN总线实战避坑指南

信号采样与混叠:从奈奎斯特原理到CAN总线实战避坑指南

1. 信号采样与混叠:从概念到“坑”的深度解析 干信号处理这行,无论是做嵌入式开发、音频处理还是通信系统,信号采样和混叠这两个词,就像吃饭喝水一样常见,但也像暗礁一样,一不小心就会让你翻船。很多人对它…

2026/10/23 22:48:56 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/27 17:33:32 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/28 7:28:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/28 9:02:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/27 18:20:50 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/28 9:00:54 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/28 7:29:02 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 13:55:19 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 9:01:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/27 19:19:37 阅读更多 →