AI接入逻辑完善

This commit is contained in:
2025-12-29 08:57:46 +08:00
parent ff49f1c0c1
commit 5663e8abfd
60 changed files with 2666 additions and 108 deletions

View File

@@ -0,0 +1,173 @@
package com.vetti.socket.agents;
import okhttp3.*;
import okio.ByteString;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
*
*/
public class ElevenLabsAgentClient extends TextWebSocketHandler{
// 存储Vue会话与ElevenLabs WebSocket的映射多客户端隔离
private static final Map<WebSocketSession, WebSocket> SESSION_MAP = new ConcurrentHashMap<>();
// ElevenLabs配置
private static final String ELEVEN_LABS_API_KEY = "你的ElevenLabs API Key";
private static final String AGENT_ID = "你的ElevenLabs Agent ID";
private static final String ELEVEN_LABS_WSS_URL = "wss://api.elevenlabs.io/v1/agents/" + AGENT_ID + "/stream";
// OkHttp客户端
private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder()
.readTimeout(0, TimeUnit.MILLISECONDS) // WebSocket长连接取消读超时
.build();
// ==================== 1. 处理Vue前端连接 ====================
@Override
public void afterConnectionEstablished(WebSocketSession vueSession) throws Exception {
super.afterConnectionEstablished(vueSession);
System.out.println("Vue前端连接成功" + vueSession.getId());
// 建立与ElevenLabs Agents的WSS连接
buildElevenLabsWssConnection(vueSession);
}
// ==================== 2. 处理Vue前端发送的文本消息如语音指令、配置信息 ====================
@Override
protected void handleTextMessage(WebSocketSession vueSession, TextMessage message) throws Exception {
super.handleTextMessage(vueSession, message);
System.out.println("接收Vue文本消息" + message.getPayload());
// 获取对应ElevenLabs WebSocket连接转发消息
WebSocket elevenLabsWs = SESSION_MAP.get(vueSession);
if (elevenLabsWs != null && elevenLabsWs.queueSize() == 0) {
elevenLabsWs.send(message.getPayload());
System.out.println("转发文本消息到ElevenLabs成功");
}
}
// ==================== 3. 处理Vue前端发送的二进制消息核心语音流数据 ====================
@Override
protected void handleBinaryMessage(WebSocketSession vueSession, BinaryMessage message) {
super.handleBinaryMessage(vueSession, message);
byte[] voiceData = message.getPayload().array();
System.out.println("接收Vue语音流数据字节长度" + voiceData.length);
// 获取对应ElevenLabs WebSocket连接转发语音流二进制
WebSocket elevenLabsWs = SESSION_MAP.get(vueSession);
if (elevenLabsWs != null && elevenLabsWs.queueSize() == 0) {
elevenLabsWs.send(ByteString.of(voiceData));
System.out.println("转发语音流到ElevenLabs成功");
}
// 释放二进制消息资源
// message.isLast();
}
// ==================== 4. 处理Vue前端连接关闭 ====================
@Override
public void afterConnectionClosed(WebSocketSession vueSession, org.springframework.web.socket.CloseStatus status) throws Exception {
super.afterConnectionClosed(vueSession, status);
System.out.println("Vue前端连接关闭" + vueSession.getId() + ",原因:" + status.getReason());
// 关闭对应的ElevenLabs WSS连接
WebSocket elevenLabsWs = SESSION_MAP.remove(vueSession);
if (elevenLabsWs != null) {
elevenLabsWs.close(1000, "Vue客户端断开连接");
System.out.println("关闭ElevenLabs WSS连接成功");
}
}
// ==================== 5. 建立与ElevenLabs Agents的WSS连接 ====================
private void buildElevenLabsWssConnection(WebSocketSession vueSession) {
// 1. 构建ElevenLabs WSS请求携带认证头
Request request = new Request.Builder()
.url(ELEVEN_LABS_WSS_URL)
.header("xi-api-key", ELEVEN_LABS_API_KEY) // 必选认证头
.build();
// 2. 构建ElevenLabs WSS监听器处理响应并回流到Vue
WebSocketListener elevenLabsListener = new WebSocketListener() {
// ElevenLabs WSS连接建立
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
System.out.println("与ElevenLabs Agents WSS连接成功");
// 存储Vue会话与ElevenLabs WS的映射
SESSION_MAP.put(vueSession, webSocket);
}
// 接收ElevenLabs文本消息如状态、错误提示回流到Vue
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
System.out.println("接收ElevenLabs文本消息" + text);
try {
// 回流到Vue前端
if (vueSession.isOpen()) {
vueSession.sendMessage(new TextMessage(text));
}
} catch (Exception e) {
System.err.println("回流文本消息到Vue失败" + e.getMessage());
}
}
// 接收ElevenLabs二进制消息核心音频流响应回流到Vue
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
byte[] audioData = bytes.toByteArray();
System.out.println("接收ElevenLabs音频流数据字节长度" + audioData.length);
try {
// 回流二进制音频流到Vue前端
if (vueSession.isOpen()) {
vueSession.sendMessage(new BinaryMessage(audioData));
}
} catch (Exception e) {
System.err.println("回流音频流到Vue失败" + e.getMessage());
}
}
// ElevenLabs WSS连接关闭
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
System.out.println("ElevenLabs WSS连接关闭" + reason + ",状态码:" + code);
// 移除映射关闭Vue连接
SESSION_MAP.remove(vueSession);
try {
if (vueSession.isOpen()) {
vueSession.close(org.springframework.web.socket.CloseStatus.NORMAL);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// ElevenLabs WSS连接异常
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
super.onFailure(webSocket, t, response);
System.err.println("ElevenLabs WSS连接异常" + t.getMessage());
// 移除映射关闭Vue连接
SESSION_MAP.remove(vueSession);
try {
if (vueSession.isOpen()) {
vueSession.close(org.springframework.web.socket.CloseStatus.SERVER_ERROR);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
// 3. 建立ElevenLabs WSS连接
OK_HTTP_CLIENT.newWebSocket(request, elevenLabsListener);
}
}

View File

@@ -9,10 +9,7 @@ import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.AjaxResult;
import com.vetti.common.core.domain.R;
import com.vetti.common.core.domain.dto.RealtimeClientSecretDto;
import com.vetti.common.enums.MinioBucketNameEnum;
import com.vetti.common.utils.readFile.FileContentUtil;
import com.vetti.web.service.ICommonService;
import io.minio.GetObjectArgs;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,10 +18,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* AI 共通测试接口处理
*

View File

@@ -0,0 +1,85 @@
package com.vetti.web.controller.ai;
import com.vetti.common.core.controller.BaseController;
import com.vetti.common.core.domain.R;
import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo;
import com.vetti.hotake.domain.dto.HotakeCvOptimizeDto;
import com.vetti.hotake.domain.dto.HotakeInitialQuestionEliminationScoreDto;
import com.vetti.hotake.domain.dto.HotakeJobDescriptionGeneratorDto;
import com.vetti.hotake.domain.vo.HotakeInitialScreeningQuestionsVo;
import com.vetti.hotake.domain.vo.HotakeJobDescriptionGeneratorVo;
import com.vetti.hotake.domain.vo.HotakeResumeJobMatchingScoreVo;
import com.vetti.hotake.service.IHotakeAiCommonToolsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* AI共通工具 信息Controller
*
* @author wangxiangshun
* @date 2025-12-14
*/
@Api(tags ="AI共通工具信息")
@RestController
@RequestMapping("/hotake/aiCommonTools")
public class HotakeAiCommonToolsController extends BaseController {
@Autowired
private IHotakeAiCommonToolsService hotakeAiCommonToolsService;
/**
* 职位描述生成器
*/
@ApiOperation("职位描述生成器")
@PostMapping(value = "/jobDescriptionGenerator")
public R<HotakeJobDescriptionGeneratorDto> handleJobDescriptionGenerator(@RequestBody HotakeJobDescriptionGeneratorVo jobDescriptionGeneratorVo)
{
return R.ok(hotakeAiCommonToolsService.getJobDescriptionGenerator(jobDescriptionGeneratorVo.getRoleId()));
}
/**
* 初筛问题生成
*/
@ApiOperation("初筛问题生成")
@PostMapping(value = "/initialScreeningQuestionsGenerator")
public R<List<HotakeInitialScreeningQuestionsInfo>> handleInitialScreeningQuestions(@RequestBody HotakeInitialScreeningQuestionsVo questionsVo)
{
return R.ok(hotakeAiCommonToolsService.getInitialScreeningQuestionsGenerator(questionsVo));
}
/**
* 简历岗位匹配度评分
*/
@ApiOperation("简历岗位匹配度评分")
@GetMapping(value = "/resumeJobMatchingScore")
public R<?> handleResumeJobMatchingScore(@RequestBody HotakeResumeJobMatchingScoreVo scoreVo)
{
return R.ok(hotakeAiCommonToolsService.getResumeJobMatchingScore(scoreVo));
}
/**
* 简历分析优化器
*/
@ApiOperation("简历分析优化器")
@GetMapping(value = "/resumeAnalysisOptimizer")
public R<HotakeCvOptimizeDto> handleResumeAnalysisOptimizer()
{
return R.ok(hotakeAiCommonToolsService.getResumeAnalysisOptimizer(""));
}
/**
* 初步筛选问题淘汰评分
*/
@ApiOperation("初步筛选问题淘汰评分")
@GetMapping(value = "/initialQuestionEliminationScore")
public R<HotakeInitialQuestionEliminationScoreDto> handleInitialQuestionEliminationScore()
{
return R.ok(hotakeAiCommonToolsService.getInitialQuestionEliminationScore(null));
}
}

View File

@@ -12,6 +12,7 @@ import com.vetti.hotake.domain.HotakeCvInfo;
import com.vetti.hotake.service.IHotakeCvInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@@ -60,7 +61,7 @@ public class HotakeCvInfoController extends BaseController
*/
@ApiOperation("获取简历信息详细信息")
@GetMapping(value = "/{id}")
public R<HotakeCvInfo> getInfo(@PathVariable("id") Long id)
public R<HotakeCvInfo> getInfo(@ApiParam("简历ID") @PathVariable("id") Long id)
{
return R.ok(hotakeCvInfoService.selectHotakeCvInfoById(id),"");
}

View File

@@ -7,6 +7,7 @@ import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.core.page.TableWebDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.hotake.domain.HotakeInitialScreeningQuestionsInfo;
import com.vetti.hotake.domain.vo.HotakeInitialScreeningQuestionsInfoVo;
import com.vetti.hotake.service.IHotakeInitialScreeningQuestionsInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -94,4 +95,16 @@ public class HotakeInitialScreeningQuestionsInfoController extends BaseControlle
{
return R.ok(hotakeInitialScreeningQuestionsInfoService.deleteHotakeInitialScreeningQuestionsInfoByIds(ids));
}
/**
* 新增初步筛选问题信息
*/
@ApiOperation("批量新增初步筛选问题信息")
@Log(title = "批量新增初步筛选问题信息", businessType = BusinessType.INSERT)
@PostMapping("/batchAdd")
public R<?> batchAdd(@RequestBody HotakeInitialScreeningQuestionsInfoVo questionsInfoVo)
{
hotakeInitialScreeningQuestionsInfoService.batchInsertHotakeInitialScreeningQuestionsInfo(questionsInfoVo);
return R.ok();
}
}

View File

@@ -7,6 +7,7 @@ import com.vetti.common.core.page.TableDataInfo;
import com.vetti.common.core.page.TableWebDataInfo;
import com.vetti.common.enums.BusinessType;
import com.vetti.hotake.domain.HotakeMeetingCalendarInfo;
import com.vetti.hotake.domain.dto.HotakeMeetingCalendarDataDto;
import com.vetti.hotake.domain.vo.HotakeMeetingCalendarVo;
import com.vetti.hotake.service.IHotakeMeetingCalendarInfoService;
import io.swagger.annotations.Api;
@@ -47,12 +48,12 @@ public class HotakeMeetingCalendarInfoController extends BaseController
/**
* 查询会议日历记录主列表(无分页)
*/
@ApiOperation("查询会议日历记录列表(无分页)")
@ApiOperation("候选人-查询会议日历记录列表(无分页)")
@GetMapping("/getList")
public R<List<HotakeMeetingCalendarInfo>> list(HotakeMeetingCalendarInfo hotakeMeetingCalendarInfo)
{
List<HotakeMeetingCalendarInfo> list = hotakeMeetingCalendarInfoService.selectHotakeMeetingCalendarInfoList(hotakeMeetingCalendarInfo);
return R.ok(list);
List<HotakeMeetingCalendarInfo> list = hotakeMeetingCalendarInfoService.selectHotakeMeetingCalendarInfoCandidateList(hotakeMeetingCalendarInfo);
return R.ok(list,"");
}
/**
@@ -110,4 +111,15 @@ public class HotakeMeetingCalendarInfoController extends BaseController
hotakeMeetingCalendarInfoService.saveHotakeMeetingCalendarInfo(calendarVo);
return R.ok();
}
/**
* 查询当前候选者会议日历数据
*/
@ApiOperation("查询当前候选者会议日历数据")
@GetMapping("/getCandidateCalendarList")
public R<List<HotakeMeetingCalendarDataDto>> listCandidateAll()
{
List<HotakeMeetingCalendarDataDto> list = hotakeMeetingCalendarInfoService.selectHotakeMeetingCalendarDataDtoList();
return R.ok(list,"");
}
}

View File

@@ -90,7 +90,7 @@ public class HotakeRolesApplyInfoController extends BaseController
* 删除候选人岗位申请信息
*/
@ApiOperation("删除候选人岗位申请信息")
@Log(title = "候选人岗位申请信息", businessType = BusinessType.DELETE)
@Log(title = "删除候选人岗位申请信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public R<?> remove(@PathVariable Long[] ids)
{

View File

@@ -2,6 +2,8 @@ package com.vetti.web.controller.system;
import java.util.*;
import com.vetti.common.core.domain.R;
import com.vetti.common.core.domain.dto.LoginDto;
import com.vetti.common.utils.MessageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -58,11 +60,11 @@ public class SysLoginController
*/
@ApiOperation("登录方法")
@PostMapping("/login")
public AjaxResult login(@RequestBody LoginBody loginBody)
public R<LoginDto> login(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
LoginDto loginDto = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
loginBody.getUuid());
return ajax;
return R.ok(loginDto,"");
}
/**

View File

@@ -123,6 +123,7 @@ public class SysProfileController extends BaseController
//个人展示数据存储
currentUser.setBestSideJson(JSONUtil.toJsonStr(user.getBestSideDtoList()));
}
currentUser.setUserOperStatus(user.getUserOperStatus());
currentUser.setSteps(user.getSteps());
if (userService.updateUserProfile(currentUser) > 0)
{

View File

@@ -173,6 +173,11 @@ chatGpt:
modelQuestion: ft:gpt-4o-mini-2024-07-18:vetti:interview-corpus:ChvLmzLu
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-full:CYT0C8JG
modelJxCv: gpt-4o-mini
modelJd: gpt-4o-mini
modelIsq: ft:gpt-4o-mini-2024-07-18:vetti:question-gen-expanded:CncFPHBB
modelRoleCv: ft:gpt-4o-mini-2024-07-18:vetti:resume-scoring-v2:CnbgEHQQ
modelCvJx: gpt-4o-mini
modelCbqpf: gpt-4o-mini
role: system
http:

View File

@@ -173,6 +173,11 @@ chatGpt:
modelQuestion: ft:gpt-4o-mini-2024-07-18:vetti:interview-corpus:ChvLmzLu
modelCV: ft:gpt-3.5-turbo-0125:vetti:vetti-resume-full:CYT0C8JG
modelJxCv: gpt-4o-mini
modelJd: gpt-4o-mini
modelIsq: ft:gpt-4o-mini-2024-07-18:vetti:question-gen-expanded:CncFPHBB
modelRoleCv: ft:gpt-4o-mini-2024-07-18:vetti:resume-scoring-v2:CnbgEHQQ
modelCvJx: gpt-4o-mini
modelCbqpf: gpt-4o-mini
role: system
http: