# V40 企业评价管理与官方回复 API ## 1. 范围 V40 为企业提供自有岗位评价查询、官方回复提交和管理员确定性预审。评价本身仍由既有学生评价模块维护;本模块不提供评价删除、隐藏、编辑、申诉、通知或运营统计。 Flyway 迁移为 `V38__company_review_replies.sql`(功能版本 V40),新增 `company_review_replies` 与 `company_review_reply_history` 两表,不修改 V1–V37。所有身份从 JWT subject 获取,不能从请求体或查询参数注入 `companyId`、`studentUserId` 或 `ownerUserId`。 ## 2. 接口 | 方法 | 路径 | 权限 | | --- | --- | --- | | GET | `/companies/me/job-reviews` | COMPANY,且仅返回本人公司岗位 | | GET | `/companies/me/job-reviews/{reviewId}` | COMPANY,且必须属于本人公司 | | POST | `/companies/me/job-reviews/{reviewId}/reply` | 所属 COMPANY | | PUT | `/companies/me/job-reviews/{reviewId}/reply` | 所属 COMPANY,仅 REJECTED 回复 | | GET | `/admin/review-replies/pending` | ADMIN | | POST | `/admin/review-replies/{replyId}/decision` | ADMIN | 未登录返回 401;角色不匹配或跨公司访问返回 403/2002。V40 不增加其他公开路径、ErrorCode 或 feature flag。 ## 3. 回复生命周期 ```text 无回复 -> 企业创建 -> AUTO_APPROVED(公开) -> PENDING -> APPROVED(公开) -> REJECTED -> 企业 PUT 修订 -> AUTO_APPROVED/PENDING ``` 每条评价只有一个当前回复。`AUTO_APPROVED`、`APPROVED` 不可修改或删除;`REJECTED` 必须带 `expectedVersion` 修订。管理员决策使用 `version` 条件更新,并发时只有一个请求成功。 ## 4. 请求与响应 创建: ```json { "content": "感谢您的反馈,我们会持续优化招聘流程。", "idempotencyKey": "合法 UUID" } ``` 正文去首尾空白后长度为 1–500;幂等键必须为 UUID 且不超过 64 字符。同企业、同评价、同正文的重放返回原回复;同幂等键但评价或正文不同返回 `409/10106`。 修订: ```json { "content": "修订后的回复内容", "expectedVersion": 1 } ``` 管理员决策: ```json {"decision":"APPROVE","reason":null} ``` 或: ```json {"decision":"REJECT","reason":"请移除不适合公开的联系方式。"} ``` 企业响应白名单包括评价文本、星级、标签、维度聚合、岗位标题、回复正文/状态/版本/更新时间及驳回的人类说明;不返回 `studentUserId`、学生姓名、手机号、邮箱、IP、UA、内容哈希、评价审核内部原因、`moderationScore`、`reasonCodes` 或管理员 ID。 管理员待审响应可包含岗位/公司、评价和回复正文、风险等级及安全原因码,但不包含学生联系方式、IP/UA 明文或凭据。 ## 5. 公开评价回复 既有 `GET /jobs/{jobId}/reviews` 和 `GET /jobs/{jobId}/reviews/{reviewId}` 保持不变。公开载荷移除 `studentUserId`,增加固定 `reviewerLabel: "匿名求职者"`。当评价为公开状态且回复为 `AUTO_APPROVED` 或 `APPROVED` 时,返回: ```json {"officialReply":{"content":"感谢反馈。","repliedAt":"2026-09-04T10:05:00"}} ``` `officialReply` 不含 replyId、companyId、status、version、原因码或审核人信息;`PENDING`/`REJECTED` 回复不公开。公开列表先分页取评价,再批量取回复和维度分,禁止逐条查询。 ## 6. 当前验收状态 V40 专项与全量自动化测试已通过(全量 1445/1445,Failures=0、Errors=0、Skipped=0),`mvn clean verify` 已通过,静态与运行时 OpenAPI 均为 159 paths / 196 operations。真实 MySQL/Docker 黑盒已通过:Flyway V38 success=1、主链路/权限/幂等/状态机/并发创建与审核/脱敏/队列终态全部通过,汇总 `PASS=62 FAIL=0 SKIP=0`。V40 不写 `candidate_dimension_scores`,未提交 Git。 ## 7. 错误码 | 错误码 | HTTP | 含义 | | --- | --- | --- | | 10101 | 404 | 回复不存在(或按归属策略隐藏) | | 10102 | 409 | 评价已有当前回复 | | 10103 | 409 | 回复状态不允许当前操作 | | 10104 | 409 | 乐观锁/条件更新冲突 | | 10105 | 400 | 回复正文或幂等键非法 | | 10106 | 409 | 幂等键对应的请求内容冲突 | | 10107 | 400 | 非法决策或驳回原因缺失 | ## 8. 审计与数据安全 成功状态变化写入既有 `audit_logs` 和回复历史。详情只含 `reviewId`、`replyId`、`status`、`requestId` 等白名单字段,不写回复正文、评价正文、学生 ID、联系方式、IP/UA、哈希、风控原文、token、password 或 secret。V40 不写 `candidate_dimension_scores`。 企业列表和管理员待审列表均采用批量加载岗位、维度分和回复,测试断言查询次数不随条目数线性增长。 ## 9. 验收 ```powershell mvn -Dtest="*Review*Test,*CompanyReview*Test,*AdminReview*Test" test mvn test mvn clean verify python docs/verify_openapi.py ``` 本轮自动化验收不启动/停止 Docker;真实 MySQL/Docker 黑盒须单独执行并如实记录。