BOSS 直聘仿写项目实战:管理端企业列表与认证审核(前后端闭环)
本文是 Boss-API 系列的「企业审核篇」承接前两篇三端架构总览、求职者管理与忘记密码。前两篇把「人」侧的业务跑通了本篇聚焦「企业」侧最核心的一块管理端怎么拉企业列表、怎么审核认证、审核结果又怎么回流到企业端。涉及今天真实改过的代码后端enterprise_api.py/enterprise_service.py/models/enterprise.py前端boss-manage-ui的request.js/api/enterprise.js/Companies.vue/CertificationList.vue/CertificationAudit.vue以及企业端boss-company-ui的认证页。所有代码均来自联调现场含踩坑记录。一、这个 feature 要解决什么一个招聘平台得让企业来「认证」。整条链路是企业端提交认证资料 ── 写「企业信息表」(t_enterprise_info) │ ▼ 管理端看到待审企业列表 ── 审核员点开详情、看资质材料 │ ▼ 审核通过 / 驳回 ── 写「企业审核表」(t_enterprise_review) 更新企业主表状态 │ ▼ 审核结果回流企业端 ── 企业登录后在认证页看到「已通过 / 已驳回 原因」这里有两张关键表职责完全不同一定要分清楚这也是今天联调时反复澄清的点表谁写什么时候写作用t_enterprise_info企业信息表企业端企业提交认证申请时存营业执照、法人、注册资本等工商资料t_enterprise_review企业审核表管理端审核员点「通过 / 驳回」时存审核结果、驳回原因、审核时间前端页面和它们的关系管理端「企业管理 / 企业认证审核」列表展示的是审核视角的数据管理端审核操作落库到审核表企业端只负责填资料、查自己这份审核结果。二、后端四张表与账号状态枚举1. 模型app/models/enterprise.py一张企业主表 三张扩展表主表用enterprise_id关联其余三张当前实现是IntField关联不是外键约束灵活但要求代码里自己判空classAccountStatus(IntEnum):账号状态NORMAL0# 正常PENDING_AUDIT1# 待审核BANNED2# 封禁REJECTED3# 已驳回审核不通过classEnterprise(Model):企业表主表idfields.IntField(pkTrue)enterprise_namefields.CharField(max_length255)enterprise_codefields.CharField(max_length100,uniqueTrue)# 企业编码account_statusfields.IntEnumField(enum_typeAccountStatus)# 0/1/2/3audit_typefields.IntEnumField(enum_typeAuditType)# 1新认证/2资质更新/3信息变更auth_timefields.DatetimeField(nullTrue)submit_timefields.DatetimeField(nullTrue)# 企业提交认证时间# ... 其余字段classEnterpriseInfo(Model):企业信息表营业执照 / 法人 / 注册资本 / 行业 / 总部地址 ...enterprise_idfields.IntField(nullTrue)unified_social_credit_codefields.CharField(max_length50,uniqueTrue)legal_representativefields.CharField(max_length100)industryfields.ForeignKeyField(models.IndustryPosition,related_nameenterprise_info_list)headquarters_addressfields.CharField(max_length512)# ...classEnterpriseQualification(Model):资质材料表营业执照图片 / 法人身份证正反面 / 联系人 ...enterprise_idfields.IntField(nullTrue)business_license_urlfields.CharField(max_length512,nullTrue)legal_id_front_urlfields.CharField(max_length512,nullTrue)legal_id_back_urlfields.CharField(max_length512,nullTrue)# ...classEnterpriseReview(Model):企业审核表审核结果 / 驳回原因 / 审核时间 / 审核人enterprise_idfields.IntField(nullTrue)review_resultfields.IntField(nullTrue)# 1通过 / 2驳回review_reasonfields.CharField(max_length512,nullTrue)audit_timefields.DatetimeField(nullTrue)audit_userfields.CharField(max_length100,nullTrue)重点AccountStatus有 4 个值0/1/2/3。今天联调时踩过一个大坑——审核驳回后前端枚举只画了 0/1/2 三态导致「已驳回」的企业在列表/详情里状态显示成「未知」。枚举一加全前端映射也要同步加3: 已驳回。2. 三个核心接口app/apis/enterprise_api.pyenterprise_routerAPIRouter(prefix/enterprise,tags[企业管理])enterprise_router.get(/list)# 企业列表管理端/审核页共用asyncdefselect_enterprise_list(page:intQuery(1,ge1),page_size:intQuery(10,ge10,le50),# 注意约束最小 10、最大 50enterprise_name:strQuery(None),submit_time_start:strQuery(None),submit_time_end:strQuery(None),):resawaitEnterpriseService.select_enterprise_list(...)return{code:1,message:查询成功,data:res}enterprise_router.get(/detail/{enterprise_id})# 企业详情含审核记录asyncdefselect_enterprise_by_id(enterprise_id:int):...enterprise_router.post(/review)# 企业审核通过/驳回asyncdefenterprise_review(req:EnterpriseReviewCreateRequest):awaitEnterpriseService.enterprise_review(req)return{code:1,message:审核完成}返回统一用code: 1表示成功——和求职者端一致但和前两篇里管理端求职者查询用的code: 200不一样。这点下文明坑。3. 列表查询enterprise_service.py列表接口要做三件事分页、按需关联三张扩展表、把行业名算出来。关键在industry必须判空否则缺资料的企业会 500staticmethodasyncdefselect_enterprise_list(page,page_size,enterprise_name,submit_time_start,submit_time_end):queryEnterprise.all()ifenterprise_name:queryquery.filter(enterprise_name__icontainsenterprise_name)ifsubmit_time_start:queryquery.filter(submit_time__gtesubmit_time_start)ifsubmit_time_end:queryquery.filter(submit_time__ltesubmit_time_end)total_countawaitquery.count()total_pagemath.ceil(total_count/page_size)rowsawaitquery.offset((page-1)*page_size).limit(page_size)enterprise_list[]forenterpriseinrows:infoawaitEnterpriseInfo.get_or_none(enterprise_identerprise.id).prefetch_related(industry)qualawaitEnterpriseQualification.get_or_none(enterprise_identerprise.id)# 行业名info 或 info.industry 任一为空都不能直接 .nameindustry_nameinfo.industry.nameif(infoandgetattr(info,industry,None))elseNoneenterprise_list.append({enterprise:enterprise,enterpriseinfo:info,enterprise_qualification:qual,industry:industry_name,})return{total_count:total_count,total_page:total_page,page:page,page_size:page_size,enterprise_list:enterprise_list,}前端真正用到的就这几个字段total_count / total_page / page / page_size分页enterprise_name / enterprise_code主表industry行业名enterpriseinfo.headquarters_address总部地址。4. 审核落库enterprise_review—— 写入「企业审核表」这是「审核成功后将数据写企业审核表」的那一步。注意它同时维护审核表和企业主表状态staticmethodasyncdefenterprise_review(req:EnterpriseReviewCreateRequest):enterpriseawaitEnterprise.get_or_none(idreq.enterprise_id)ifenterpriseisNone:raiseException(f企业不存在(enterprise_id{req.enterprise_id}))reviewawaitEnterpriseReview.get_or_none(enterprise_idreq.enterprise_id)ifreviewisNone:awaitEnterpriseReview.create(enterprise_idreq.enterprise_id,review_resultreq.review_result,review_reasonreq.review_reason,remarkreq.remark,audit_timenow(),)else:review.review_resultreq.review_resultorreview.review_result review.review_reasonreq.review_reasonorreview.review_reason review.remarkreq.remarkorreview.remark review.audit_timenow()awaitreview.save()ifreq.review_result1:# 通过enterprise.account_statusAccountStatus.NORMAL enterprise.auth_timenow()elifreq.review_result2:# 驳回enterprise.account_statusAccountStatus.REJECTEDawaitenterprise.save()审核通过 → 主表变「正常」且写认证时间审核驳回 → 主表变「已驳回」。审核结果本身落在t_enterprise_review。前端拿detail接口里的review字段就能知道这家企业处于什么审核状态。三、前端从「写死 mock」到「接真实接口」管理端是 Vue3 Element Plus Vite4请求统一走封装好的request.js由 Vite 的/api代理转发到后端 8000前两篇已讲这里不再展开代理配置。1. 第一步永远是「成功码对齐」utils/request.js今天第一坑响应拦截器原来写if (res.code ! 200) reject(...)但企业接口返回的是code: 1于是所有企业相关请求都被当成失败列表永远拿不到数据。改成和后端约定一致service.interceptors.response.use((response){constresresponse.data// 后端业务约定code 1 表示成功if(res.code!1){if(res.code401){/* 跳登录 */}elseElMessage.error(res.message||请求失败)returnPromise.reject(newError(res.message||Error))}returnres})经验前后端成功码必须一对一定死。本项目里求职者接口、企业接口用code:1前两篇的管理端求职者查询用code:200——同一套前端不同模块约定不同全靠request.js拦截器统一兜底别在业务页面里各写各的。2. 抽一层 APIapi/enterprise.js所有企业接口集中在一个文件页面只调语义化方法不关心 URLimportrequestfrom/utils/requestexportfunctiongetEnterpriseList(params){returnrequest({url:/enterprise/list,method:get,params})}exportfunctiongetEnterpriseDetail(enterpriseId){returnrequest({url:/enterprise/detail/${enterpriseId},method:get})}exportfunctionenterpriseReview(data){returnrequest({url:/enterprise/review,method:post,data})}3. 企业列表页Companies.vue/CertificationList.vue「企业管理」和「企业认证审核」两个列表页都接入同一个/enterprise/list。进页面onMounted就拉数据映射后端返回的嵌套结构constfetchListasync(){loading.valuetruetry{constresawaitgetEnterpriseList({page:query.page,page_size:query.page_size,enterprise_name:query.enterprise_name||undefined,submit_time_start:query.submit_time_start||undefined,submit_time_end:query.submit_time_end||undefined,})constdatares.data tableData.valuedata.enterprise_list||[]// 嵌套数组pagination.totaldata.total_count pagination.currentPagedata.page pagination.pageSizedata.page_size}finally{loading.valuefalse}}onMounted(fetchList)表格列直接吃后端的字段el-table-columnlabel企业名称template#default{ row }{{ row.enterprise.enterprise_name }}/template/el-table-columnel-table-columnlabel企业编码template#default{ row }{{ row.enterprise.enterprise_code }}/template/el-table-columnel-table-columnlabel行业template#default{ row }{{ row.industry || - }}/template/el-table-columnel-table-columnlabel总部地址template#default{ row }{{ row.enterpriseinfo?.headquarters_address || - }}/template/el-table-column注意row.enterpriseinfo?.headquarters_address用了可选链——因为enterpriseinfo可能为null企业没填资料时。这和后端industry判空是一对前后端都要兜底。4. 审核详情页CertificationAudit.vue—— 审核操作 回执点列表里的「审核」路由带enterprise_id跳到详情页先拉detail接口再展示资质材料营业执照 / 法人身份证正反面用el-image预览和「当前审核记录」横幅来自review字段。通过 / 驳回后不再直接跳走而是展示一张回执卡consthandleApproveasync(){awaitElMessageBox.confirm(确定要通过企业${detail.enterprise.enterprise_name}的认证申请吗,审核通过确认)awaitenterpriseReview({enterprise_id:Number(route.params.id),review_result:1,// 通过remark:auditForm.remark||undefined,})buildReceipt(通过)// 展示回执操作/企业/时间 已通知企业}驳回类似多带review_reason。四、打通「管理端审核 → 企业端查看」闭环审核不是管理端自嗨结果得让企业端看到。链路是企业端提交认证 →t_enterprise_info落库主表account_status 1待审核管理端审核通过/驳回 →t_enterprise_review落库 主表状态变0/3企业端登录后认证页用 JWT 里的enterprise_id调GET /enterprise/detail/{id}读review.review_result和account_status决定显示什么// boss-company-ui/.../Certification.vueconstfetchAuthStatusasync(){constidgetEnterpriseIdFromToken()// JWT payload.user_id enterprise_idconst{data}awaitgetEnterpriseDetail(id)constrdata.reviewif(rr.review_result2)showRejected(r.review_reason)// 已驳回 原因elseif(data.enterprise.account_status0||(rr.review_result1))showApproved(data.enterprise.auth_time)elseif(data.enterprise.account_status1)showPending()// 待审核}这样企业被驳回后重新提交、被通过后立即看到认证时间整条审核业务就闭环了。五、本期踩坑合集全是真事成功码不一致所有请求被当失败拦截器按code ! 200判错但企业接口返回code: 1列表永不渲染。前后端成功码必须约定一致本项目企业/求职者接口用1管理端另一查询用200靠拦截器统一兜底。关联查询取到 None 还取属性 → 500enterpriseinfo.industry.name在「缺企业信息 / 行业没关联」时直接AttributeError。列表和详情都要先判空industry_name info.industry.name if (info and info.industry) else None。前端对应用row.enterpriseinfo?.xxx可选链。审核驳回状态没进枚举 → 前端显示「未知」AccountStatus加了REJECTED 3但前端状态映射只画了 0/1/2已驳回企业状态列变「未知」。枚举一改前端三处映射列表 / 详情 / 企业端都要同步加3: 已驳回。孤儿数据导致审核接口 500企业主表记录丢失只剩审核表孤儿记录Enterprise.get_or_none(id)返回None后面enterprise.account_status ...直接AttributeError。修复在enterprise_review/detail里把get_or_none提前、企业不存在就raise Exception(企业不存在)不再触发空指针并清理了孤儿审核记录。列表页残留 mock 数据数据「全部不对」CertificationList.vue企业认证审核列表一度还是写死的假数组auditList里 “某某信息科技有限公司” 之类从不调接口所以页面数据全是假的。排查顺序先看页面用的是不是真实接口、再看接口成功码、再看字段映射、最后看是否 mock 没清。分页参数约束后端page_size约束ge10, le50前端默认 10、可选 [10,20,50]。前端若传 1 或 100 会被后端Query校验直接拒列表拉不到分页。六、整条链路串一遍【企业提交】企业端填表 → POST /enterprise/save → t_enterprise_info t_enterprise_qualification主表 account_status1(待审核) ↓ 【管理端看列表】GET /enterprise/list?pageenterprise_namesubmit_time_* → 分页列表真实数据 ↓ 【管理端审核】点「审核」→ GET /enterprise/detail/{id} 看资质材料 → POST /enterprise/review {review_result:1|2} ↓ 后端写 t_enterprise_review 主表 account_status0(通过)/3(驳回) ↓ 【企业端查看】企业登录 → GET /enterprise/detail/{id} → 读 review.review_result / account_status ↓ 展示已通过(认证时间) / 已驳回(原因) / 待审核七、小结企业侧审核功能本质是「两表分工 一个状态机」两表分工企业信息表t_enterprise_info由企业端写、存资料企业审核表t_enterprise_review由管理端写、存结果。一个状态机企业主表account_status在待审核(1) → 正常(0) / 已驳回(3)之间流转封禁(2) 是运营后续动作。落到代码上还是前两篇反复说的那套范式接口成功码对齐 → 关联查询判空兜底 → 枚举前后端同步 → mock 数据必须清零 → 孤儿/不一致数据提前拦。把企业列表和审核这个闭环吃透一个招聘平台「企业侧」最硬的一块骨头就啃下来了。本文基于真实 FastAPI Vue3 项目整理敏感密钥已脱敏。FastAPI / Tortoise-ORM / Element Plus / Vite 文档以官方最新版为准。

相关新闻

E语言函数参数详解:从基础到高级应用

E语言函数参数详解:从基础到高级应用

1. E语言函数参数基础概念解析E语言作为一门面向中文开发者的编程语言,其函数参数设计充分考虑了中文思维习惯。与主流编程语言相比,E语言的参数传递机制既有相似之处也有独特设计。我们先从最基础的参数定义开始:函数 加法运算(参数1 数值型…

2026/7/29 2:20:17阅读更多 →
狗凯源码库学习资源真实价值评测

狗凯源码库学习资源真实价值评测

在技术选型的关键节点,面对海量开源资源时,开发者往往容易陷入“参数迷茫”。我们常常看到各种项目标榜着极高的并发处理能力或极低的资源占用,但一旦上手复现,却发现文档语焉不详,核心指标与实际表现大相径庭。这种落…

2026/7/29 2:20:17阅读更多 →
172.2026年国家级科研瓶颈 | 机床热误差实时建模与补偿(温度场-变形场)

172.2026年国家级科研瓶颈 | 机床热误差实时建模与补偿(温度场-变形场)

2026年国家级科研瓶颈 | 机床热误差实时建模与补偿(温度场-变形场)【前置断言】 旧路线试图靠堆叠测温节点与加深时序网络硬扛。这已经用完了所有可调参数的自由度——再调就是过拟合现场噪声,再改就是依赖进口高精度传感单元。它的上限不是算…

2026/7/29 2:20:17阅读更多 →
Maven Surefire与Failsafe插件实现单元测试与集成测试分离的完整指南

Maven Surefire与Failsafe插件实现单元测试与集成测试分离的完整指南

1. 项目概述:为什么我们需要分离单元测试与集成测试?在Java后端开发中,测试是保证代码质量的基石。但很多团队,尤其是项目初期,往往会把所有测试一股脑地扔给Maven Surefire插件去执行。这会导致一个典型问题&#xff…

2026/7/29 3:26:31阅读更多 →
自注意力机制原理与Transformer模型实践指南

自注意力机制原理与Transformer模型实践指南

1. 从生活场景理解自注意力机制想象你正在阅读一本侦探小说,主角在犯罪现场发现了几十个线索。普通人的做法可能是按顺序逐个查看每个线索,但侦探会怎么做?他会快速扫视所有线索,然后:重点关注带血的匕首(相…

2026/7/29 3:26:31阅读更多 →
Qt6自定义QML组件加载失败:从原理到CMake配置的完整解决方案

Qt6自定义QML组件加载失败:从原理到CMake配置的完整解决方案

1. 从一次“文件找不到”的报错说起 最近在把项目从 Qt5 升级到 Qt6 的过程中,我遇到了一个挺典型的坑:在尝试加载一个自定义的 QML 文件时,程序直接崩溃了,控制台抛出的错误信息是 file:///.../MyCustomComponent.qml: No such …

2026/7/29 3:26:31阅读更多 →
Brocade 交换机进入 EOS 后,为什么有些命令不能使用?

Brocade 交换机进入 EOS 后,为什么有些命令不能使用?

写在前面近期越来越多的用户反馈在维护 Brocade 光纤交换机时,会发现原来可以执行的命令突然失败,例如:This product has passed its End of Support date and functionality has been restricted.Please contact your Service Provider for …

2026/7/29 3:26:31阅读更多 →
黄仁勋牵头组建Open Secure AI Alliance:一场AI自主攻击事件如何倒逼全球科技巨头联手

黄仁勋牵头组建Open Secure AI Alliance:一场AI自主攻击事件如何倒逼全球科技巨头联手

2026年7月下旬,硅谷发生了一件足以改写网络安全史的事件。OpenAI在一次内部模型能力评估中,其前沿AI系统竟自主突破了隔离环境,对开源AI平台Hugging Face发起了一场完整的网络入侵。这场"自己人打自己人"的意外,没有演变…

2026/7/29 3:26:31阅读更多 →
庭院设计中硬景和软景的基本概念与分类

庭院设计中硬景和软景的基本概念与分类

前阵子有个业主朋友发来一张照片,是他在自家院子里铺完石材后的效果。整个院子看起来硬邦邦的,像个小广场,完全没有花园该有的生机。他问我:“我是不是买错材料了?”其实不是材料的问题,是他对庭院设计里两…

2026/7/29 3:24:30阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“!

28. Agent 执行到一半想暂停?用 interrupt 给它设个“关卡“! 在构建复杂的 Agent 系统时,我们经常会遇到这样的场景:Agent 正在执行一个多步骤的任务,比如“下单购买商品”,但执行到一半时,我们…

2026/7/29 0:01:46阅读更多 →
自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

自律同行,突破无界!NANK南卡正式官宣曾舜晞成为品牌代言人

近日,国际专注开放式技术研发的声学品牌Nank南卡,正式官宣实力艺人曾舜晞担任品牌代言人。消息一经发出便轰动全网。为什么耳机品牌不选择流量明星、老牌歌手?而且是选择曾舜晞?让我们一起来探索一下!比起短期的流量&a…

2026/7/29 0:01:46阅读更多 →
【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:01:46阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →