You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

13 KiB

岗位与企业展示字段 · 前端交接说明

批次:job-company-display-fields 后端基线:1cbadc3e18a786ef642b0d329adde6b173c7b2ac(分支 fix/mysql57-module-migrations-20261009) 数据库迁移:V57__job_company_display_fields.sql 本文档面向前端工程师,描述本批岗位展示字段的接入参考,不改变既有字段语义。 更新:2026-10-19。本地后端验收已通过,但尚未提交、推送或发布;目标服务器是否具备这些字段必须按实际部署版本确认。本文不是前端已经完成接入的证明。


1. 新字段在哪些端点返回

端点 新增顶层字段 company 摘要
GET /jobs(公开列表) ✅ 5 个 ✅ 新增
GET /jobs/search(公开搜索) ✅ 5 个 ✅ 新增(兼容扩展)
GET /jobs/public/{jobId}(公开详情) ✅ 5 个 ✅ 新增
GET /companies/{companyId}/jobs ✅ 5 个 ✅ 新增
GET /jobs/{jobId}(企业端自己看) ✅ 5 个 ❌ 不返回
POST /jobs、PUT /jobs/{jobId}、PATCH /jobs/{jobId}/status ✅ 5 个(响应同企业端视图) ❌ 不返回
GET /companies/me/jobs(企业端岗位管理) ✅ 5 个 ❌ 不返回
GET /companies/{companyId}(公开企业详情) companySize —
GET/POST/PUT /companies/me(企业端自己看) companySize —
GET /jobs/favorites(我的收藏) ✅ 5 个 ❌ 不返回

「5 个」= experience、education、benefits、salaryMonths、workLocation。

说明:收藏列表复用公开视图组装,因此自动带上 5 个新增顶层字段;本批未为收藏域增加 company 嵌套。若前端需要企业名,请先用岗位的 companyId 调 GET /companies/{companyId}。


2. 字段类型、单位、缺省值与更新语义

2.1 岗位新增字段

字段 类型 缺省值 写入语义
experience string | null null null/缺省 = 保持原值;空白串 = 清空为 null;非空 = 去两端空白后保存;最长 40
education string | null null 同 experience,最长 40
benefits string[] [](永不返回 null) null/缺省 = 保持原值;[] = 清空;非空 = 逐项 trim → 拒绝空白项 → 单项 ≤40 → 去重且保留首次出现顺序 → 最多 20 项;违反任一条返回 400
salaryMonths int | null null null/缺省 = 保持原值(本批不提供显式清空);非 null 必须 1—24,0 视为非法返回 400;只接受 JSON 整数——小数(1.9/1.0)、数字字符串("12"/"")、布尔、数组、对象、超出 int 范围一律 400,绝不截断或四舍五入
workLocation string | null null 同 experience,最长 255

2.2 企业新增字段

字段 类型 缺省值 写入语义
companySize string | null null null/缺省 = 保持原值;空白串 = 清空为 null;非空 = trim 后保存;超过 40 字符返回 400,不截断

上述「缺省/null 保持原值」针对更新;创建时没有旧值,对应可选字段存 NULL。响应中的福利始终为数组,请求中的 benefits 可以为 null。

2.3 福利归一化与 OpenAPI 3.1

  • 去重后的数量上限为 20,不是原始请求数组上限:21 个相同标签会成为 1 项并可接受,21 个不同标签返回 400。
  • 单项长度在 trim 后判断;空白项拒绝,不是自动删去。
  • 更新 benefits: null 保留原值,benefits: [] 清空。不要把这两种请求合并。
  • 静态及运行时契约用 x-normalization 描述 trimItems、deduplicate、rejectBlankItems、maxItems=20、itemMaxLength=40,不对原始请求误加 maxItems/元素 maxLength。
  • 这是项目的业务扩展,通用 OpenAPI 客户端不会自动执行归一化。若前端加校验,应按相同顺序处理;后端仍是最终校验方。
  • 运行时为 OpenAPI 3.1,可空字段使用包含 null 的联合类型,不以缺少 OpenAPI 3.0 的 nullable 判为不可空。

2.4 单位与语义(保持不变)

  • salaryMin / salaryMax:单位仍是「元/月」,本批未改成 K。前端展示 K 时自行 /1000(既有约定)。
  • 行业仍来自 companies.industry,不在岗位表重复存储。

2.5 只读约定

  • benefits 读取时:库中为 NULL → 返回 [](不是 null)。前端可安全地对它做 .length / .map,无需判空。
  • company:企业不存在时返回 null,不会编造数据、也不会 500。前端需容忍 company === null。

3. company 嵌套结构 → 前端类型映射

后端 company 对象(白名单,仅这 7 个字段):

interface BackendJobCompany {
  id: number;
  name: string;
  logoUrl: string | null;
  description: string | null;
  industry: string | null;
  verified: boolean;
  companySize: string | null;
}

建议按此前前端类型映射到 src/types/job.ts;本轮未修改或重新验收前端,具体类型请以接手工程师当前代码为准:

后端字段 前端字段 备注
company.id —(可用于跳企业页) 前端 Job 无对应字段
company.name Job.companyName 已有映射 company.name → companyName
company.logoUrl — 前端暂无对应字段
company.description — 前端暂无对应字段
company.industry Job.industry 本批接通,取代前端写死的空值
company.verified — 前端暂无对应字段
company.companySize JobDetail.companySize 本批接通
job.workLocation (见 §4)
job.city Job.city 既有
job.title Job.positionName 既有
job.salaryMin/salaryMax Job.salaryMin/salaryMax 单位元→K 需 /1000
job.experience Job.experience 本批接通
job.education Job.education 本批接通
job.benefits Job.benefits 本批接通
job.salaryMonths JobDetail.salaryMonths 本批接通(展示为「N 薪」)
job.description / requirements JobDetail.description / requirements 既有,原样保留换行、不截断

逐条要点:

  1. company.industry → 前端 industry:直接取 company.industry,缺失时为 null。
  2. company.companySize → 前端 companySize:直接取 company.companySize,缺失时为 null。
  3. 顶层 experience、education、benefits、salaryMonths 直接映射到同名前端字段。

4. workLocation 的处理

  • workLocation 是岗位明确录入的工作地点,独立于 city。
  • 若 workLocation 缺失(null),前端可退化为展示 city。
  • 但不得据此回写:不要因为展示用了 city,就把 city 当作详细办公地址保存或提交回后端。后端的「企业登记地址」(companies.address)也不会被当作公开招聘地址下发,前端同样不要拿它当工作地点展示。

5. 评分与评价:继续调用既有真实接口

本批没有新增/修改任何评分、评价、排名相关字段或端点。前端请继续使用既有接口:

  • 岗位个性化评分:GET /jobs/{jobId}/personalized-review-score
  • 岗位评价列表 / 汇总:GET /jobs/{jobId}/reviews、GET /jobs/{jobId}/reviews/summary

本批也不补造综合评分、浏览量、排名或员工评价——这些字段若前端仍写死,属前端侧待接,不是后端缺字段。


6. 前端工程师需核对的字段映射

以下字段在本批后端验收制品中可返回。此前交接发现部分前端映射为空;接手工程师需核对当前实现,不能由本文件推断前端仍未接入或服务器已发布:

前端字段 后端来源 备注
Job.experience job.experience 本批新增下发
Job.education job.education 本批新增下发
Job.benefits job.benefits 本批新增下发,恒为数组
Job.industry company.industry 本批新增下发
JobDetail.companySize company.companySize 本批新增下发
JobDetail.salaryMonths job.salaryMonths 本批新增下发

需接入的展示字段应按上述来源映射;JobDetail.views(浏览数)本批未新增提供,不要把本地模拟值当服务器真实数据。


7. 投递 / 沟通按钮

投递、沟通、邀请面试等按钮属于前端入口。本批只是补齐后端岗位与企业资料字段,不会自动生成任何按钮,也不会新增/修改投递与沟通的接口契约。


8. 响应示例

8.1 新增字段完整(GET /jobs/public/{jobId})

{
  "code": 200,
  "message": "ok",
  "data": {
    "id": 101,
    "companyId": 5,
    "title": "前端开发工程师",
    "description": "负责招聘平台 App 的页面开发。",
    "requirements": "1. 熟悉 React Native\n2. 熟悉 TypeScript",
    "city": "北京",
    "employmentType": "FULL_TIME",
    "salaryMin": 18000,
    "salaryMax": 26000,
    "experience": "3-5年",
    "education": "本科",
    "benefits": ["五险一金", "年终奖", "弹性工作"],
    "salaryMonths": 14,
    "workLocation": "北京市海淀区中关村大街 1 号 A 座 12 层",
    "status": "PUBLISHED",
    "publishedAt": "2026-10-01T10:00:00",
    "createdAt": "2026-09-30T18:20:00",
    "company": {
      "id": 5,
      "name": "演示示例科技有限公司",
      "logoUrl": "https://example.invalid/logo.png",
      "description": "非真实招聘,仅用于本地演示。",
      "industry": "互联网/IT",
      "verified": true,
      "companySize": "100-499人"
    }
  },
  "requestId": "app-xxxxxx",
  "timestamp": "2026-10-01T10:00:01Z"
}

8.2 历史数据字段缺失(老岗位,未补录)

{
  "code": 200,
  "message": "ok",
  "data": {
    "id": 7,
    "companyId": 2,
    "title": "后端开发实习生",
    "description": "参与接口开发。",
    "requirements": null,
    "city": "武汉",
    "employmentType": "INTERN",
    "salaryMin": 3000,
    "salaryMax": 5000,
    "experience": null,
    "education": null,
    "benefits": [],
    "salaryMonths": null,
    "workLocation": null,
    "status": "PUBLISHED",
    "publishedAt": "2026-09-01T09:00:00",
    "createdAt": "2026-08-31T20:00:00",
    "company": {
      "id": 2,
      "name": "演示联合信息科技有限公司",
      "logoUrl": null,
      "description": null,
      "industry": null,
      "verified": false,
      "companySize": null
    }
  },
  "requestId": "app-yyyyyy",
  "timestamp": "2026-10-01T10:00:01Z"
}

缺失即为 null / [],后端不会填充「本科」「3—5年」「13薪」等默认值。前端请按缺失处理(隐藏区块或显示「未填写」)。


9. 兼容性提示

  1. 旧请求不受影响:不传新字段的创建/修改请求行为完全不变。
  2. 旧响应字段不变:companyId 等既有字段保留,未删除、未改名、未改类型;GET /jobs/search 原 company 结构为兼容扩展(只在其中新增字段)。
  3. 草稿与已关闭岗位的可见性规则不变:DRAFT 岗位公开详情仍不可见(404)。
  4. error_code 约定不变:本批所有新增字段的校验失败统一返回既有参数错误响应(code=1001)。

10. 发布后联调检查(尚未执行)

以下为待办,不是已完成的验收结果。仅在对应后端版本发布后,使用获得授权的测试账号执行。

  1. 确认响应来自目标服务而非本地示例;企业名、规模、行业和岗位五字段按实际值显示,缺失时隐藏或提示未填写。
  2. 月薪元到 K 的转换只做一次;salaryMonths: null 不默认为 13 或 0,不向更新接口提交 0。
  3. 检查字段缺省/null/空白/空数组语义,福利重复和 trim 边界与后端一致。
  4. 验证公开摘要仅七字段、company: null 可正常渲染;不要用企业登记地址代替工作地点。
  5. 评分和员工评价继续使用真实接口,不能用演示值补齐;投递和沟通入口按已有业务权限处理。
  6. 对参数错误展示可理解提示,失败后重新读取确认没有部分更新;不伪造成功。

11. 验收与相关说明

  • 最新本地全量 clean verify:3464 项通过,失败/错误/跳过均为 0。真实 HTTP 黑盒合计 106 项,其中岗位企业展示 48 项;重启合计 6 项。
  • 静态/运行时契约对照 129 项通过,契约反向测试 13 项通过。不是前端端到端验收结果。
  • 交付报告:docs/batch-job-company-display-fields-report.md;简历权限与文件名规则:docs/resume-ownership-hardening-20261016.md。
  • 最新证据:D:/Acceptance/qsp-final-20261019/evidence/acceptance-summary.json。未发布、未连接生产库、未向线上导入演示企业岗位。