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/9/19 14:48:08 阅读更多 →
DeepSeek结合ArcGIS Pro批量建库

DeepSeek结合ArcGIS Pro批量建库

要实现基于Excel提供的字段结构模板批量添加字段到GDB数据库要素类的功能,可以使用Python结合ArcPy库来完成。以下是实现步骤和代码示例: 1. 脚本工具箱设计 输入参数: 输入Excel文件路径(包含字段结构模板) 目标GDB数据库路径 目标要素类名称 输出: 在目标GDB数据库中…

2026/9/23 18:01:13 阅读更多 →
【RT-DETR多模态创新改进】ACM 2025顶会 | 全网独家、特征融合创新篇 | 引入DAAttn差异感知注意力融合模块,通过动态调整注意力,使模型更准确地识别关键内容,提高精度、并减少冗余计算

【RT-DETR多模态创新改进】ACM 2025顶会 | 全网独家、特征融合创新篇 | 引入DAAttn差异感知注意力融合模块,通过动态调整注意力,使模型更准确地识别关键内容,提高精度、并减少冗余计算

一、本文介绍 🔥本文给大家介绍使用 DAAttn 差异感知注意力融合模块改进RT-DETR多模态网络模型,模型能够在变化检测任务中更精确地识别目标,尤其是在复杂背景和微小变化的情况下。它能够提高RT-DETR多模态网络模型的精度、鲁棒性,并减少无关噪声的影响,提升小目标和细节…

2026/9/19 12:54:34 阅读更多 →

最新新闻

高并发下缓存穿透与击穿的防御实践:基于Redis的封装方案

高并发下缓存穿透与击穿的防御实践:基于Redis的封装方案

做了这么多年后端,缓存穿透和缓存击穿这个问题我几乎在每个高并发项目里都要重新讲一遍。最近我把这两类问题的防御逻辑统一封装成了一个可复用的工具包,基于Redis实现,核心围绕布隆过滤器、分布式锁、本地缓存和空值缓存这套组合拳。这篇就是…

2026/9/25 13:14:41 阅读更多 →
ax:面向智能体的Kubernetes声明式调度原语

ax:面向智能体的Kubernetes声明式调度原语

1. 项目概述:从“ax”这个极简标题切入,我们到底在谈什么?“ax”——两个字母,没有空格,没有标点,没有上下文。放在搜索引擎里,它像一粒投入深水的石子,激起的不是涟漪,而…

2026/9/25 13:14:41 阅读更多 →
openEuler 上 Intel 虚拟化实战:KVM、VT-d 直通与性能调优

openEuler 上 Intel 虚拟化实战:KVM、VT-d 直通与性能调优

虚拟化这摊事儿,说简单也简单,说复杂能让人折腾一整天。openEuler 作为企业级服务器操作系统,在 Intel 平台上跑虚拟化,底子其实是现成的——Linux 内核自带 KVM,Intel 又贡献了 VT-x、VT-d、SR-IOV 这一整套硬件辅助虚…

2026/9/25 13:14:41 阅读更多 →
Meta主动记忆干预长程智能体:TaoToken统一Key下的配置骨架与验证

Meta主动记忆干预长程智能体: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/9/25 13:14:41 阅读更多 →
Atlas 300V Pro 24GB部署YOLO实战:从硬件选型到推理调优完整记录

Atlas 300V Pro 24GB部署YOLO实战:从硬件选型到推理调优完整记录

Atlas 300V Pro 24GB部署YOLO实战:从硬件选型到推理调优的完整记录如果你最近在关注边缘端的AI推理部署,大概率刷到过Atlas这个系列的名号。但说实话,很多刚接触昇腾生态的朋友第一反应都是:Atlas 300V 24G到底是不是一张运算加速…

2026/9/25 13:14:41 阅读更多 →
OpenCode 与 OpenCLAW 的 AI 模型配置:用 TaoToken 统一 Key 打通多工具调用

OpenCode 与 OpenCLAW 的 AI 模型配置:用 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/9/25 13:13:40 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/25 11:15:26 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →