Merge branch 'claude/sweet-swanson-772e08'
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ public class OaAiReview extends BaseEntity {
|
||||
/** 简历审核的目标岗位 */
|
||||
private String position;
|
||||
|
||||
/** 本次审核的附加要求/审核重点 */
|
||||
private String requirements;
|
||||
|
||||
/** 简历匹配度评分 0-100(合同为空) */
|
||||
private Integer matchScore;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user