Merge branch 'claude/sweet-swanson-772e08'

This commit is contained in:
2026-06-12 14:35:27 +08:00
8 changed files with 125 additions and 17 deletions

View File

@@ -37,8 +37,9 @@ public class OaAiReviewController extends BaseController {
@PostMapping(value = "/analyze", consumes = "multipart/form-data")
public R<OaAiReviewVo> analyze(@RequestParam("file") MultipartFile file,
@RequestParam("reviewType") String reviewType,
@RequestParam(value = "position", required = false) String position) {
return R.ok("审核完成", service.analyze(file, reviewType, position));
@RequestParam(value = "position", required = false) String position,
@RequestParam(value = "requirements", required = false) String requirements) {
return R.ok("审核完成", service.analyze(file, reviewType, position, requirements));
}
/**
@@ -48,8 +49,9 @@ public class OaAiReviewController extends BaseController {
@PostMapping(value = "/analyzeStream", consumes = "multipart/form-data", produces = "text/event-stream;charset=UTF-8")
public SseEmitter analyzeStream(@RequestParam("file") MultipartFile file,
@RequestParam("reviewType") String reviewType,
@RequestParam(value = "position", required = false) String position) {
return service.analyzeStream(file, reviewType, position);
@RequestParam(value = "position", required = false) String position,
@RequestParam(value = "requirements", required = false) String requirements) {
return service.analyzeStream(file, reviewType, position, requirements);
}
@GetMapping("/list")
@@ -57,7 +59,8 @@ public class OaAiReviewController extends BaseController {
return service.queryPageList(bo, pageQuery);
}
@GetMapping("/{id}")
// 限定为数字,避免 /analyzeStream 等子路径被 {id} 误匹配(否则 POST 会得到 405
@GetMapping("/{id:\\d+}")
public R<OaAiReviewVo> getInfo(@NotNull @PathVariable Long id) {
return R.ok(service.queryById(id));
}

View File

@@ -35,6 +35,9 @@ public class OaAiReview extends BaseEntity {
/** 简历审核的目标岗位 */
private String position;
/** 本次审核的附加要求/审核重点 */
private String requirements;
/** 简历匹配度评分 0-100合同为空 */
private Integer matchScore;

View File

@@ -20,6 +20,7 @@ public class OaAiReviewVo implements Serializable {
private Long ossId;
private String fileUrl;
private String position;
private String requirements;
private Integer matchScore;
private String riskLevel;
private String summary;

View File

@@ -14,16 +14,17 @@ public interface IOaAiReviewService {
/**
* 上传合同/简历并进行 AI 审核,落库并返回结果
*
* @param file PDF / Word 文件
* @param reviewType contract / resume
* @param position 简历审核的目标岗位(合同可空)
* @param file PDF / Word 文件
* @param reviewType contract / resume
* @param position 简历审核的目标岗位(合同可空)
* @param requirements 附加审核要求/重点(可空)
*/
OaAiReviewVo analyze(MultipartFile file, String reviewType, String position);
OaAiReviewVo analyze(MultipartFile file, String reviewType, String position, String requirements);
/**
* 流式审核边生成边推送SSE结束后落库
*/
SseEmitter analyzeStream(MultipartFile file, String reviewType, String position);
SseEmitter analyzeStream(MultipartFile file, String reviewType, String position, String requirements);
TableDataInfo<OaAiReviewVo> queryPageList(OaAiReviewBo bo, PageQuery pageQuery);

View File

@@ -57,8 +57,8 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
private static final Pattern RISK_PATTERN = Pattern.compile("风险评级[:\\s]*([高中低])");
@Override
public OaAiReviewVo analyze(MultipartFile file, String reviewType, String position) {
Prepared p = prepareSync(file, reviewType, position);
public OaAiReviewVo analyze(MultipartFile file, String reviewType, String position, String requirements) {
Prepared p = prepareSync(file, reviewType, position, requirements);
buildPrompt(p);
String result = p.images != null
? miMoClient.chatMultimodal(p.system, p.userText, p.images)
@@ -70,11 +70,11 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
}
@Override
public SseEmitter analyzeStream(MultipartFile file, String reviewType, String position) {
public SseEmitter analyzeStream(MultipartFile file, String reviewType, String position, String requirements) {
// 只在同步阶段做轻量校验 + 读取字节multipart 必须在请求线程内消费);
// 文档解析、渲染、大模型调用全部放到异步线程,任何异常都以 SSE error 事件返回,
// 避免在返回 SseEmitter 之前抛异常被全局处理器包成 JSON前端按流读取会“静默失败”
Prepared p = prepareSync(file, reviewType, position);
Prepared p = prepareSync(file, reviewType, position, requirements);
String username = currentUsername();
long timeoutMs = ((miMoProps.getTimeout() == null ? 180 : miMoProps.getTimeout()) + 60) * 1000L;
SseEmitter emitter = new SseEmitter(timeoutMs);
@@ -129,7 +129,7 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
}
/** 同步阶段:校验 + 读取字节(不解析,快) */
private Prepared prepareSync(MultipartFile file, String reviewType, String position) {
private Prepared prepareSync(MultipartFile file, String reviewType, String position, String requirements) {
if (file == null || file.isEmpty()) {
throw new ServiceException("请上传文件");
}
@@ -153,6 +153,7 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
Prepared p = new Prepared();
p.reviewType = reviewType;
p.position = position;
p.requirements = StringUtils.trimToNull(requirements);
p.fileName = fileName;
p.bytes = bytes;
return p;
@@ -160,7 +161,12 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
/** 解析文档 + 构建提示词(耗时/可能抛异常的部分) */
private void buildPrompt(Prepared p) {
p.system = "contract".equals(p.reviewType) ? contractSystemPrompt() : resumeSystemPrompt(p.position);
String base = "contract".equals(p.reviewType) ? contractSystemPrompt() : resumeSystemPrompt(p.position);
if (StringUtils.isNotBlank(p.requirements)) {
base += "\n\n【本次额外审核要求 / 重点】请在上述固定结构基础上,务必额外重点关注并逐条回应以下要求:\n"
+ p.requirements;
}
p.system = base;
String text = truncate(DocumentParseUtil.extractText(p.fileName, p.bytes));
if (StringUtils.length(text) >= MIN_TEXT_LEN) {
p.userText = "contract".equals(p.reviewType)
@@ -189,6 +195,7 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
entity.setFileUrl(oss.getUrl());
}
entity.setPosition(p.position);
entity.setRequirements(p.requirements);
entity.setResultMd(result);
entity.setSummary(buildSummary(result));
entity.setModel(miMoProps.getModel());
@@ -242,6 +249,7 @@ public class OaAiReviewServiceImpl implements IOaAiReviewService {
private static class Prepared {
String reviewType;
String position;
String requirements;
String fileName;
byte[] bytes;
String system;