Ver código fonte

附件上传AI简历解析功能

zjc 9 meses atrás
pai
commit
127b00813e

+ 77 - 2
ruoyi-admin/src/main/java/com/ruoyi/web/controller/moonshot/MoonshotFileController.java

@@ -1,15 +1,24 @@
 package com.ruoyi.web.controller.moonshot;
 
 
+import cn.moonshot.platform.util.MoonshotAiUtils;
+import com.google.gson.Gson;
 import com.ruoyi.common.config.RuoYiConfig;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.enums.FileType;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.file.FileUploadUtils;
 import com.ruoyi.common.utils.file.FileUtils;
 import com.ruoyi.common.utils.moonshot.Client;
+import com.ruoyi.common.utils.moonshot.vo.ChatCompletionRequest;
+import com.ruoyi.common.utils.moonshot.vo.ChatCompletionStreamChoice;
 import com.ruoyi.common.utils.moonshot.vo.FileUploadResult;
+import com.ruoyi.common.utils.uuid.IdUtils;
 import com.ruoyi.framework.config.ServerConfig;
+import com.ruoyi.system.domain.resume.Resume;
+import com.ruoyi.system.service.resume.IResumeService;
 import com.ruoyi.tool.domain.TUnifyFile;
 import com.ruoyi.tool.service.ITUnifyFileService;
 import com.ruoyi.web.controller.common.CommonController;
@@ -37,6 +46,9 @@ public class MoonshotFileController extends BaseController {
     @Value("${moonshot.api_key}")
     private String API_KEY;
 
+    @Autowired
+    private IResumeService resumeService;
+
     /**
      * 通用上传请求(单个)
      */
@@ -44,6 +56,10 @@ public class MoonshotFileController extends BaseController {
     public AjaxResult uploadFile(MultipartFile file) throws Exception
     {
         AjaxResult ajax = AjaxResult.success();
+        String corpId = "";
+        if (StringUtils.isNotEmpty(SecurityUtils.getLoginUser().getCorpid())) {
+            corpId = SecurityUtils.getLoginUser().getCorpid();
+        }
         try
         {
             if (API_KEY == null) {
@@ -61,24 +77,72 @@ public class MoonshotFileController extends BaseController {
             String fileName = FileUploadUtils.upload(filePath, file);
             String url = serverConfig.getUrl() + fileName;
             TUnifyFile tUnifyFile = new TUnifyFile();
+            tUnifyFile.setId(IdUtils.fastSimpleUUID());
             tUnifyFile.setUploadPath(url);
             tUnifyFile.setUploadName(fileName);
             tUnifyFile.setNewUploadName(FileUtils.getName(fileName));
             tUnifyFile.setUploadFormat(file.getOriginalFilename());
             tUnifyFile.setUploadType(String.valueOf(FileType.RESUME.ordinal()));//简历附件
             tUnifyFile.setCreateBy(getNickName());
+            tUnifyFile.setCorpId(corpId);
 
             //moonshot AI 附件上传返回附件信息
             if(null != resuat.getError()){
-                ajax.put("message", resuat.getError().getMessage());
                 tUnifyFile.setStatus_details(resuat.getError().getMessage());
             }else if(resuat.getStatus().equals("ok")) {
                 tUnifyFile.copyFrom(resuat,true);
+                /**
+                 * 根据moonshot获取附件解析内容
+                 */
+                if(StringUtils.isNotEmpty(tUnifyFile.getFileId())){
+                    tUnifyFile.setContent(MoonshotAiUtils.getFileContent(tUnifyFile.getFileId(),client.getApiKey()));
+                    //读取内容后删除AI存储附件
+                    MoonshotAiUtils.deleteFile(tUnifyFile.getFileId(),client.getApiKey());
+                }
+            }
+
+            // todo 开始简历解析
+            if(StringUtils.isNotEmpty(tUnifyFile.getContent())){
+
+                StringBuilder str = new StringBuilder();
+
+                client.ChatCompletionStream(new ChatCompletionRequest(tUnifyFile.getContent())).subscribe(
+                        streamResponse -> {
+                            if (streamResponse.getChoices().isEmpty()) {
+                                return;
+                            }
+                            for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
+                                String finishReason = choice.getFinishReason();
+                                if (finishReason != null) {
+                                    continue;
+                                }
+                                // 将每个choice的delta内容添加到StringBuilder中
+                                str.append(choice.getDelta().getContent());
+                            }
+                        },
+                        error -> {
+                            error.printStackTrace();
+                        },
+                        () -> {
+                            System.out.println(str);
+                            // todo 格式解析完成-存储简历内容整理并返回
+                            if(isValidJson((str.toString()))){
+                                Gson gson = new Gson();
+                                Resume resume = gson.fromJson(str.toString(), Resume.class); // 使用 Gson 转换 JSON 字符串为 Resume 对象
+                                resumeService.insertResume(resume);
+                                tUnifyFile.setFileBusinessId(resume.getResumeId().toString());
+                            }else{
+                                // 返回值不是json字符串
+                                ajax.put("error","解析失败,AI解析返回格式有误,请重试!");
+                            }
+                        }
+                );
             }
 
             tUnifyFileService.insertTUnifyFile(tUnifyFile);
 
-            ajax.put("FileUploadResult", resuat);
+            //根据上传附件已经moonshot返回上传内容
+            ajax.put(tUnifyFile.getFileId(),tUnifyFile);
             return ajax;
         }
         catch (Exception e)
@@ -88,4 +152,15 @@ public class MoonshotFileController extends BaseController {
     }
 
 
+
+    public static boolean isValidJson(String test) {
+        try {
+            new Gson().fromJson(test, Resume.class);
+            return true;
+        } catch (com.google.gson.JsonSyntaxException ex) {
+            // 如果捕获到JsonSyntaxException异常,则字符串不是有效的JSON
+            return false;
+        }
+    }
+
 }

+ 46 - 4
ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TUnifyFileController.java

@@ -7,13 +7,19 @@ import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
 import com.ruoyi.common.core.page.TableDataInfo;
 import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.enums.ChatMessageRole;
 import com.ruoyi.common.enums.FileType;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.file.FileUploadUtils;
 import com.ruoyi.common.utils.file.FileUtils;
 import com.ruoyi.common.utils.moonshot.Client;
+import com.ruoyi.common.utils.moonshot.vo.ChatCompletionMessage;
+import com.ruoyi.common.utils.moonshot.vo.ChatCompletionRequest;
+import com.ruoyi.common.utils.moonshot.vo.ChatCompletionStreamChoice;
 import com.ruoyi.common.utils.moonshot.vo.FileUploadResult;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.common.utils.uuid.IdUtils;
 import com.ruoyi.framework.config.ServerConfig;
 import com.ruoyi.tool.domain.TUnifyFile;
 import com.ruoyi.tool.service.ITUnifyFileService;
@@ -32,6 +38,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.nio.file.StandardCopyOption;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -99,6 +106,7 @@ public class TUnifyFileController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody TUnifyFile tUnifyFile)
     {
+        tUnifyFile.setId(IdUtils.fastSimpleUUID());
         tUnifyFile.setCreateBy(getNickName());
         return toAjax(tUnifyFileService.insertTUnifyFile(tUnifyFile));
     }
@@ -126,7 +134,7 @@ public class TUnifyFileController extends BaseController
     }
 
     /**
-     * 上传附件
+     * 简历附件上传附件
      */
     @ApiOperation("上传附件")
     @Log(title = "附件", businessType = BusinessType.INSERT)
@@ -134,6 +142,10 @@ public class TUnifyFileController extends BaseController
     public AjaxResult uploadFiles(List<MultipartFile> files)
     {
         AjaxResult ajax = AjaxResult.success();
+        String corpId = "";
+        if (StringUtils.isNotEmpty(SecurityUtils.getLoginUser().getCorpid())) {
+            corpId = SecurityUtils.getLoginUser().getCorpid();
+        }
         try
         {
             // 上传文件路径
@@ -154,30 +166,60 @@ public class TUnifyFileController extends BaseController
                 String fileName = FileUploadUtils.upload(filePath, file);
                 String url = serverConfig.getUrl() + fileName;
                 TUnifyFile tUnifyFile = new TUnifyFile();
+                tUnifyFile.setId(IdUtils.fastSimpleUUID());
                 tUnifyFile.setUploadPath(url);
                 tUnifyFile.setUploadName(fileName);
                 tUnifyFile.setNewUploadName(FileUtils.getName(fileName));
                 tUnifyFile.setUploadFormat(file.getOriginalFilename());
                 tUnifyFile.setUploadType(String.valueOf(FileType.RESUME.ordinal()));//简历附件
                 tUnifyFile.setCreateBy(getNickName());
+                tUnifyFile.setCorpId(corpId);
 
                 //moonshot AI 附件上传返回附件信息
                 if(null != resuat.getError()){
                     tUnifyFile.setStatus_details(resuat.getError().getMessage());
                 }else if(resuat.getStatus().equals("ok")) {
                     tUnifyFile.copyFrom(resuat,true);
-
                     /**
                      * 根据moonshot获取附件解析内容
                      */
                     if(StringUtils.isNotEmpty(tUnifyFile.getFileId())){
                         tUnifyFile.setContent(MoonshotAiUtils.getFileContent(tUnifyFile.getFileId(),client.getApiKey()));
                     }
-
                 }
-
                 tUnifyFileService.insertTUnifyFile(tUnifyFile);
 
+                // todo 开始简历解析
+                if(StringUtils.isNotEmpty(tUnifyFile.getContent())){
+
+                    StringBuilder str = new StringBuilder();
+
+                    client.ChatCompletionStream(new ChatCompletionRequest(tUnifyFile.getContent())).subscribe(
+                        streamResponse -> {
+                            if (streamResponse.getChoices().isEmpty()) {
+                                return;
+                            }
+                            for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
+                                String finishReason = choice.getFinishReason();
+                                if (finishReason != null) {
+                                    continue;
+                                }
+                                // 将每个choice的delta内容添加到StringBuilder中
+                                str.append(choice.getDelta().getContent());
+                            }
+                        },
+                        error -> {
+                            error.printStackTrace();
+                        },
+                        () -> {
+                            System.out.println(str);
+                            // todo 格式解析完成-存储简历内容整理并返回
+
+
+                        }
+                    );
+                }
+
                 //根据上传附件已经moonshot返回上传内容
                 ajax.put(tUnifyFile.getFileId(),tUnifyFile);
             }

+ 69 - 12
ruoyi-common/src/main/java/com/ruoyi/common/utils/moonshot/MoonshotDemoMain.java

@@ -23,29 +23,81 @@ public class MoonshotDemoMain {
         }
         Client client = new Client(apiKey);
 
-        File file = new File("/D:/szty/test.pdf");
+        File file = new File("/D:/szty/695b2905-dda7-4e7d-b321-641ea3b06c74.pdf");
         FileUploadResult resuat = client.uploadFile(file);
-        System.out.println(resuat.getError());
         if(null != resuat.getError()){
 
         }else if(resuat.getStatus().equals("ok")) {
             String fileId = resuat.getId();
-
+            System.out.println(resuat);
             String fileContent = MoonshotAiUtils.getFileContent(fileId,client.getApiKey());
             System.out.println(fileContent);
 
             MoonshotAiUtils.deleteFile(fileId,client.getApiKey());
 
-
             final List<ChatCompletionMessage> messages = Arrays.asList(
                     new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
-                            "你是计算机行业专业面试官,提供一套面试题,并根据回复的答案进行分析和人员的能力程度"),
+                            "你是简历整理AI,同个标准格式生成简历统一的简历相信内容.简历格式如下"),
                     new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
-                            fileContent),
+                            "基本信息\n" +
+                                    "\n" +
+                                    "姓名:[你的全名]\n" +
+                                    "联系方式:\n" +
+                                    "年龄:[你的年龄]\n" +
+                                    "电话:[你的电话号码]\n" +
+                                    "邮箱:[你的电子邮箱地址]\n" +
+                                    "地址:[你的居住地址]\n" +
+                                    "工作年限:[你的工作时间经验]\n" +
+                                    "链接:\n" +
+                                    "LinkedIn:[你的LinkedIn个人资料链接]\n" +
+                                    "个人网站/博客:[如果有,提供个人网站或博客链接]\n" +
+                                    "个人简介\n" +
+                                    "\n" +
+                                    "简短介绍自己的职业目标和核心技能。\n" +
+                                    "教育背景\n" +
+                                    "\n" +
+                                    "学位:[学位名称]\n" +
+                                    "专业:[专业名称]\n" +
+                                    "学校:[学校名称]\n" +
+                                    "毕业时间:[毕业年份]\n" +
+                                    "工作经验\n" +
+                                    "\n" +
+                                    "[公司名称]\n" +
+                                    "[职位名称]\n" +
+                                    "工作时间:[开始时间] - [结束时间]\n" +
+                                    "工作职责:\n" +
+                                    "职责1\n" +
+                                    "职责2\n" +
+                                    "...\n" +
+                                    "技能\n" +
+                                    "\n" +
+                                    "列出与职位相关的专业技能,例如编程语言、软件操作、外语能力等。\n" +
+                                    "项目经验\n" +
+                                    "\n" +
+                                    "[项目名称]\n" +
+                                    "项目描述:[简短介绍项目内容]\n" +
+                                    "角色:[你在项目中的角色]\n" +
+                                    "主要贡献:[你在项目中的主要贡献]\n" +
+                                    "使用技术:[项目中使用的技术或工具]\n" +
+                                    "荣誉和奖项\n" +
+                                    "\n" +
+                                    "[奖项名称] - [颁发机构] - [颁发时间]\n" +
+                                    "证书\n" +
+                                    "\n" +
+                                    "[证书名称] - [颁发机构] - [获得时间]\n" +
+                                    "语言能力\n" +
+                                    "\n" +
+                                    "[语言] - [熟练程度]\n" +
+                                    "兴趣爱好\n" +
+                                    "\n" +
+                                    "简要介绍你的个人兴趣或爱好。\n" +
+                                    "其他\n" +
+                                    "\n" +
+                                    "任何其他可能对求职有帮助的信息。"),
                     new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
-                            ""),
+                            fileContent),
                     new ChatCompletionMessage(ChatMessageRole.USER.value(),
-                            "请描述一下回答情况,水平")
+                            "请整理简历内容,并按照标准格式返回")
             );
 
 //        try {
@@ -63,11 +115,13 @@ public class MoonshotDemoMain {
 //            e.printStackTrace();
 //        }
 
+            StringBuilder str = new StringBuilder();
+
             try {
                 client.ChatCompletionStream(new ChatCompletionRequest(
                         "moonshot-v1-128k",
                         messages,
-                        1024,
+                        8196,
                         0.3f,
                         1
                 )).subscribe(
@@ -78,17 +132,20 @@ public class MoonshotDemoMain {
                             for (ChatCompletionStreamChoice choice : streamResponse.getChoices()) {
                                 String finishReason = choice.getFinishReason();
                                 if (finishReason != null) {
-                                    System.out.print("finish reason: " + finishReason);
                                     continue;
                                 }
-                                System.out.print(choice.getDelta().getContent());
+                                // 将每个choice的delta内容添加到StringBuilder中
+                                str.append(choice.getDelta().getContent());
                             }
                         },
                         error -> {
                             error.printStackTrace();
                         },
                         () -> {
-                            System.out.println("complete");
+                            System.out.println(str);
+                            // todo 格式解析完成-存储简历内容整理并返回
+                            //处理标识分段,并判断内容情况
+
                         }
                 );
             } catch (Exception e) {

+ 6 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/moonshot/vo/ChatCompletionMessage.java

@@ -1,5 +1,10 @@
 package com.ruoyi.common.utils.moonshot.vo;
 
+import com.ruoyi.common.enums.ChatMessageRole;
+
+import java.util.Arrays;
+import java.util.List;
+
 public class ChatCompletionMessage {
     public String role;
     public String name;
@@ -29,4 +34,5 @@ public class ChatCompletionMessage {
     public Boolean getPartial() {
         return partial;
     }
+
 }

+ 66 - 0
ruoyi-common/src/main/java/com/ruoyi/common/utils/moonshot/vo/ChatCompletionRequest.java

@@ -1,7 +1,9 @@
 package com.ruoyi.common.utils.moonshot.vo;
 
 import com.google.gson.annotations.SerializedName;
+import com.ruoyi.common.enums.ChatMessageRole;
 
+import java.util.Arrays;
 import java.util.List;
 
 public class ChatCompletionRequest {
@@ -39,4 +41,68 @@ public class ChatCompletionRequest {
         this.n = n;
     }
 
+    public ChatCompletionRequest(String fileContent){
+        List<ChatCompletionMessage> messages = Arrays.asList(
+                new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
+                        "你是简历整理AI,同个标准格式生成简历统一的简历详细内容.简历格式如下"),
+                new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
+                        "{\n" +
+                                "  \"resume\": {\n" +
+                                "    \"userName\": \"你的全名\",\n" +
+                                "    \"gender\": \"你的性别\",\n" +
+                                "    \"age\": \"你的年龄\",\n" +
+                                "    \"degree\": \"你的学历\",\n" +
+                                "    \"mobile\": \"你的电话号码\",\n" +
+                                "    \"email\": \"你的电子邮箱地址\",\n" +
+                                "    \"address\": \"你的居住地址\"\n" +
+                                "  },\n" +
+                                "  \"selfIntroduce\": \"简短介绍自己的职业目标和核心技能。\",\n" +
+                                "  \"eduList\": [\n" +
+                                "    {\n" +
+                                "      \"degree\": \"学位名称\",\n" +
+                                "      \"major\": \"专业名称\",\n" +
+                                "      \"schoolName\": \"学校名称\",\n" +
+                                "      \"graduationYear\": \"毕业年份\"\n" +
+                                "    }\n" +
+                                "  ],\n" +
+                                "  \"workList\": [\n" +
+                                "    {\n" +
+                                "      \"companyName\": \"公司名称\",\n" +
+                                "      \"postionName\": \"职位名称\",\n" +
+                                "      \"iinductionStartDate\": \"开始时间\",\n" +
+                                "      \"iinductionEndDate\": \"结束时间\",\n" +
+                                "      \"workDetail\": \"职责\"\n" +
+                                "    }\n" +
+                                "  ],\n" +
+                                "  \"keyWords\": \"与职位相关的专业技能1\",\n" +
+                                "  \"projectList\": [\n" +
+                                "    {\n" +
+                                "      \"projectName\": \"项目名称\",\n" +
+                                "      \"projectDetail\": \"简短介绍项目内容\",\n" +
+                                "      \"partIn\": \"你在项目中的角色\",\n" +
+                                "      \"contributions\": \"主要贡献\",\n" +
+                                "      \"developTools\": \"使用技术或工具\"\n" +
+                                "    }\n" +
+                                "  ],\n" +
+                                "  \"certificate\": \"name\": \"获得证书信息\",\n" +
+                                "  \"languageAbility\": \"language\": \"语言能力\",\n" +
+                                "  \"selfEvaluation\": \"简要介绍你的个人兴趣或爱好。\",\n" +
+                                "  \"otherInformation\": \"任何其他可能对求职有帮助的信息。\"\n" +
+                                "}"),
+                new ChatCompletionMessage(ChatMessageRole.SYSTEM.value(),
+                        fileContent),
+                new ChatCompletionMessage(ChatMessageRole.USER.value(),
+                        "请整理简历内容,并按照标准格式返回")
+        );
+
+        this.model = "moonshot-v1-128k";
+        this.messages = messages;
+        this.maxTokens = 8196;
+        this.temperature = 0.3f;
+        this.n = 1;
+
+    }
+
+
+
 }

+ 10 - 0
ruoyi-system/src/main/java/com/ruoyi/tool/domain/TUnifyFile.java

@@ -73,6 +73,8 @@ public class TUnifyFile extends BaseEntity
      */
     private String content;
 
+    private String corpId;
+
 
     public void setId(String id) 
     {
@@ -220,6 +222,14 @@ public class TUnifyFile extends BaseEntity
         this.content = content;
     }
 
+    public String getCorpId() {
+        return corpId;
+    }
+
+    public void setCorpId(String corpId) {
+        this.corpId = corpId;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 0 - 1
ruoyi-system/src/main/java/com/ruoyi/tool/service/impl/TUnifyFileServiceImpl.java

@@ -56,7 +56,6 @@ public class TUnifyFileServiceImpl implements ITUnifyFileService
     public int insertTUnifyFile(TUnifyFile tUnifyFile)
     {
         tUnifyFile.setCreateTime(DateUtils.getNowDate());
-        tUnifyFile.setId(IdUtils.fastSimpleUUID());
         return tUnifyFileMapper.insertTUnifyFile(tUnifyFile);
     }