fix: 修复一事一流程元数据添加失败,添加语音转文字接口
This commit is contained in:
parent
85393210fa
commit
919ab51570
|
|
@ -1,6 +1,7 @@
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
import requestStream from '@/utils/requestStream'
|
import requestStream from '@/utils/requestStream'
|
||||||
|
|
||||||
|
import { fileUploadRequest } from '@/utils/request'
|
||||||
|
|
||||||
|
|
||||||
// 聊天消息相关接口定义
|
// 聊天消息相关接口定义
|
||||||
|
|
@ -85,3 +86,11 @@ export const stopMessagesStream = (chatType: Number,taskId: string, userId: stri
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const voiceToText = (data: FormData) => {
|
||||||
|
return fileUploadRequest({
|
||||||
|
url: '/brichat-service/chat/voiceToText',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,32 @@
|
||||||
:autosize="{ minRows: 1, maxRows: 4 }"
|
:autosize="{ minRows: 1, maxRows: 4 }"
|
||||||
>
|
>
|
||||||
</el-input>
|
</el-input>
|
||||||
|
<!-- 语音录音按钮 -->
|
||||||
|
<button
|
||||||
|
class="voice-button"
|
||||||
|
@mousedown="startRecording"
|
||||||
|
@mouseup="stopRecording"
|
||||||
|
@mouseleave="stopRecording"
|
||||||
|
@touchstart="startRecording"
|
||||||
|
@touchend="stopRecording"
|
||||||
|
:disabled="isLoading"
|
||||||
|
:class="{ 'recording': isRecording }"
|
||||||
|
>
|
||||||
|
<template v-if="isRecording">
|
||||||
|
<div class="recording-animation">
|
||||||
|
<svg viewBox="0 0 24 24" class="voice-icon recording">
|
||||||
|
<path fill="currentColor" d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template v-else-if="isProcessingVoice">
|
||||||
|
<span class="loading-icon voice-loading"></span>
|
||||||
|
</template>
|
||||||
|
<svg v-else viewBox="0 0 24 24" class="voice-icon">
|
||||||
|
<path fill="currentColor" d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
class="send-button"
|
class="send-button"
|
||||||
|
|
@ -45,8 +71,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue'
|
import { ref, watch, onUnmounted } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { voiceToText } from '@/api/chat'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
|
@ -69,6 +97,12 @@ const emit = defineEmits<Emits>()
|
||||||
|
|
||||||
const inputText = ref(props.modelValue)
|
const inputText = ref(props.modelValue)
|
||||||
|
|
||||||
|
// 语音录制相关状态
|
||||||
|
const isRecording = ref(false)
|
||||||
|
const isProcessingVoice = ref(false)
|
||||||
|
let mediaRecorder: MediaRecorder | null = null
|
||||||
|
let audioChunks: Blob[] = []
|
||||||
|
|
||||||
// 监听输入值变化
|
// 监听输入值变化
|
||||||
watch(inputText, (newValue) => {
|
watch(inputText, (newValue) => {
|
||||||
emit('update:modelValue', newValue)
|
emit('update:modelValue', newValue)
|
||||||
|
|
@ -78,6 +112,119 @@ watch(inputText, (newValue) => {
|
||||||
watch(() => props.modelValue, (newValue) => {
|
watch(() => props.modelValue, (newValue) => {
|
||||||
inputText.value = newValue
|
inputText.value = newValue
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 开始录音
|
||||||
|
const startRecording = async () => {
|
||||||
|
if (isRecording.value || isProcessingVoice.value) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia({
|
||||||
|
audio: {
|
||||||
|
echoCancellation: true,
|
||||||
|
noiseSuppression: true,
|
||||||
|
autoGainControl: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 检查浏览器支持的音频格式
|
||||||
|
const mimeTypes = [
|
||||||
|
'audio/webm;codecs=opus',
|
||||||
|
'audio/webm',
|
||||||
|
'audio/mp4',
|
||||||
|
'audio/wav'
|
||||||
|
]
|
||||||
|
|
||||||
|
let selectedMimeType = 'audio/webm'
|
||||||
|
for (const mimeType of mimeTypes) {
|
||||||
|
if (MediaRecorder.isTypeSupported(mimeType)) {
|
||||||
|
selectedMimeType = mimeType
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaRecorder = new MediaRecorder(stream, { mimeType: selectedMimeType })
|
||||||
|
audioChunks = []
|
||||||
|
|
||||||
|
mediaRecorder.ondataavailable = (event) => {
|
||||||
|
if (event.data.size > 0) {
|
||||||
|
audioChunks.push(event.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaRecorder.onstop = async () => {
|
||||||
|
const audioBlob = new Blob(audioChunks, { type: selectedMimeType })
|
||||||
|
await processVoiceToText(audioBlob)
|
||||||
|
|
||||||
|
// 停止所有音频轨道
|
||||||
|
stream.getTracks().forEach(track => track.stop())
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaRecorder.start()
|
||||||
|
isRecording.value = true
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取麦克风权限失败:', error)
|
||||||
|
ElMessage.error('无法访问麦克风,请检查权限设置')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止录音
|
||||||
|
const stopRecording = () => {
|
||||||
|
if (!isRecording.value || !mediaRecorder) return
|
||||||
|
|
||||||
|
isRecording.value = false
|
||||||
|
mediaRecorder.stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理语音转文字
|
||||||
|
const processVoiceToText = async (audioBlob: Blob) => {
|
||||||
|
if (audioBlob.size === 0) {
|
||||||
|
ElMessage.warning('录音时间过短,请重新录制')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isProcessingVoice.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const formData = new FormData()
|
||||||
|
|
||||||
|
// 根据录音格式设置文件扩展名
|
||||||
|
const fileExtension = audioBlob.type.includes('wav') ? 'wav' :
|
||||||
|
audioBlob.type.includes('mp4') ? 'mp3' : 'webm'
|
||||||
|
|
||||||
|
formData.append('file', audioBlob, `recording.${fileExtension}`)
|
||||||
|
|
||||||
|
const response = await voiceToText(formData)
|
||||||
|
|
||||||
|
if (response && response.data) {
|
||||||
|
const transcribedText = response.data
|
||||||
|
if (transcribedText.trim()) {
|
||||||
|
// 将转换的文字添加到输入框中
|
||||||
|
const currentText = inputText.value
|
||||||
|
const newText = currentText ? `${currentText} ${transcribedText}` : transcribedText
|
||||||
|
inputText.value = newText
|
||||||
|
emit('update:modelValue', newText)
|
||||||
|
ElMessage.success('语音转换成功')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning('未识别到有效语音内容')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ElMessage.error('语音转换失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('语音转文字失败:', error)
|
||||||
|
ElMessage.error('语音转换失败,请重试')
|
||||||
|
} finally {
|
||||||
|
isProcessingVoice.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件卸载时清理资源
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (mediaRecorder && mediaRecorder.state !== 'inactive') {
|
||||||
|
mediaRecorder.stop()
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
@ -188,7 +335,7 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.el-textarea__inner) {
|
:deep(.el-textarea__inner) {
|
||||||
padding: 1rem 4rem 1rem 1.5rem;
|
padding: 1rem 6rem 1rem 1.5rem;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
border: 1px solid #e5e7eb;
|
border: 1px solid #e5e7eb;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|
@ -274,6 +421,127 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 语音按钮样式 */
|
||||||
|
.voice-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 4rem;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
height: 2.5rem;
|
||||||
|
width: 2.5rem;
|
||||||
|
min-height: 2.5rem;
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #6b7280;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 8px rgba(107, 114, 128, 0.25);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-button:not(:disabled) {
|
||||||
|
background: linear-gradient(135deg, #6b7280 0%, #4b5563 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-button:disabled {
|
||||||
|
opacity: 0.7;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-button:hover:not(:disabled) {
|
||||||
|
transform: translateY(-50%) scale(1.05);
|
||||||
|
box-shadow: 0 4px 12px rgba(107, 114, 128, 0.3);
|
||||||
|
background: linear-gradient(135deg, #9ca3af 0%, #6b7280 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 录音状态样式 */
|
||||||
|
.voice-button.recording {
|
||||||
|
background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||||||
|
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||||
|
animation: recordingPulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-button.recording:hover {
|
||||||
|
background: linear-gradient(135deg, #f87171 0%, #ef4444 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 录音脉冲动画 */
|
||||||
|
@keyframes recordingPulse {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(-50%) scale(1);
|
||||||
|
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.4);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-50%) scale(1.1);
|
||||||
|
box-shadow: 0 6px 20px rgba(239, 68, 68, 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-icon {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-icon.recording {
|
||||||
|
animation: microphoneWave 0.8s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 麦克风波动动画 */
|
||||||
|
@keyframes microphoneWave {
|
||||||
|
from {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 录音动画容器 */
|
||||||
|
.recording-animation {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recording-animation::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
animation: ripple 1.5s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 波纹动画 */
|
||||||
|
@keyframes ripple {
|
||||||
|
0% {
|
||||||
|
transform: scale(0.8);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(2);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 语音处理加载图标 */
|
||||||
|
.voice-loading {
|
||||||
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||||
|
border-radius: 50%;
|
||||||
|
border-top-color: #ffffff;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.input-footer {
|
.input-footer {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
@ -301,6 +569,24 @@ watch(() => props.modelValue, (newValue) => {
|
||||||
border-width: 1.5px;
|
border-width: 1.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端语音按钮适配 */
|
||||||
|
.voice-button {
|
||||||
|
right: 3rem;
|
||||||
|
height: 2.25rem;
|
||||||
|
width: 2.25rem;
|
||||||
|
min-height: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-icon,
|
||||||
|
.voice-loading {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.voice-loading {
|
||||||
|
border-width: 1.5px;
|
||||||
|
}
|
||||||
|
|
||||||
/* 移动端中止按钮适配 */
|
/* 移动端中止按钮适配 */
|
||||||
.stop-button-container {
|
.stop-button-container {
|
||||||
top: -2rem;
|
top: -2rem;
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,17 @@ import io.github.guoshiqiufeng.dify.chat.dto.request.ChatMessageSendRequest;
|
||||||
import io.github.guoshiqiufeng.dify.chat.dto.response.ChatMessageSendCompletionResponse;
|
import io.github.guoshiqiufeng.dify.chat.dto.response.ChatMessageSendCompletionResponse;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.io.ByteArrayResource;
|
||||||
|
import org.springframework.http.*;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
@Api(tags = "对话聊天接口")
|
@Api(tags = "对话聊天接口")
|
||||||
|
|
@ -22,6 +29,17 @@ public class ChatController {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ChatService chatService;
|
private ChatService chatService;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private RestTemplate restTemplate;
|
||||||
|
|
||||||
|
@Value("${voice2text.url}")
|
||||||
|
private String voice2textUrl;
|
||||||
|
|
||||||
|
@Value("${voice2text.model}")
|
||||||
|
private String voice2textModel;
|
||||||
/***
|
/***
|
||||||
* 故障诊断对话接口
|
* 故障诊断对话接口
|
||||||
* @param
|
* @param
|
||||||
|
|
@ -50,6 +68,65 @@ public class ChatController {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* 语音转文字
|
||||||
|
* @param file 音频文件
|
||||||
|
* @return 转换后的文字
|
||||||
|
*/
|
||||||
|
@PostMapping("/voiceToText")
|
||||||
|
public String voiceToText(@RequestParam("file") MultipartFile file){
|
||||||
|
try {
|
||||||
|
log.info("开始语音转文字,文件名: {}, 文件大小: {} bytes",
|
||||||
|
file.getOriginalFilename(), file.getSize());
|
||||||
|
|
||||||
|
// 1. 参数验证
|
||||||
|
if (file == null || file.isEmpty()) {
|
||||||
|
log.error("语音文件为空");
|
||||||
|
throw new IllegalArgumentException("语音文件不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 构建请求头
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
|
||||||
|
|
||||||
|
// 3. 构建请求体
|
||||||
|
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
|
||||||
|
body.add("model", voice2textModel);
|
||||||
|
|
||||||
|
// 添加文件数据
|
||||||
|
ByteArrayResource fileResource = new ByteArrayResource(file.getBytes()) {
|
||||||
|
@Override
|
||||||
|
public String getFilename() {
|
||||||
|
return file.getOriginalFilename();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
body.add("file", fileResource);
|
||||||
|
|
||||||
|
// 4. 发送请求
|
||||||
|
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
|
||||||
|
ResponseEntity<Map> response = restTemplate.postForEntity(voice2textUrl, requestEntity, Map.class);
|
||||||
|
|
||||||
|
// 5. 处理响应
|
||||||
|
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
|
||||||
|
Map<String, Object> responseBody = response.getBody();
|
||||||
|
String text = (String) responseBody.get("text");
|
||||||
|
log.info("语音转文字成功,结果: {}", text);
|
||||||
|
return text;
|
||||||
|
} else {
|
||||||
|
log.error("语音转文字失败,响应状态: {}, 响应体: {}",
|
||||||
|
response.getStatusCode(), response.getBody());
|
||||||
|
throw new RuntimeException("语音转文字服务调用失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("语音转文字异常: {}", e.getMessage(), e);
|
||||||
|
throw new RuntimeException("语音转文字失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,17 +94,17 @@ public class KnowledgeBaseController {
|
||||||
return ResultUtils.error("索引删除失败: " + e.getMessage());
|
return ResultUtils.error("索引删除失败: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ApiOperation("创建单个文件的索引")
|
// @ApiOperation("创建单个文件的索引")
|
||||||
@PostMapping("/createIndex")
|
// @PostMapping("/createIndex")
|
||||||
public ResultUtils createIndex(@RequestParam("documentId") String documentId) throws Exception {
|
// public ResultUtils createIndex(@RequestParam("documentId") String documentId) throws Exception {
|
||||||
|
//
|
||||||
try{
|
// try{
|
||||||
esTDatasetFilesImporter.importDocumentId(documentId);
|
// esTDatasetFilesImporter.importDocumentId(documentId);
|
||||||
return ResultUtils.success("索引创建成功");
|
// return ResultUtils.success("索引创建成功");
|
||||||
} catch (IOException e) {
|
// } catch (IOException e) {
|
||||||
return ResultUtils.error("索引创建失败: " + e.getMessage());
|
// return ResultUtils.error("索引创建失败: " + e.getMessage());
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
// @ApiOperation("创建所有文件的索引")
|
// @ApiOperation("创建所有文件的索引")
|
||||||
// @PostMapping("/createAllIndex")
|
// @PostMapping("/createAllIndex")
|
||||||
// public ResultUtils createAllIndex() throws Exception {
|
// public ResultUtils createAllIndex() throws Exception {
|
||||||
|
|
|
||||||
|
|
@ -59,5 +59,5 @@ public interface MetadataService {
|
||||||
* @param file 文件
|
* @param file 文件
|
||||||
* @return 处理后的元数据列表
|
* @return 处理后的元数据列表
|
||||||
*/
|
*/
|
||||||
List<DifyMetadata> handleYsylcMetadata(String datasetId, String documentId, MultipartFile file);
|
List<DifyMetadata> handleYsylcMetadata(String datasetId, String documentId, MultipartFile file,String metadataId);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -229,15 +229,12 @@ public class DifyDatasetApiServiceImpl implements DifyDatasetApiService {
|
||||||
stepStartTime = System.currentTimeMillis();
|
stepStartTime = System.currentTimeMillis();
|
||||||
Map<String, String> document = (Map<String, String>) res.getBody().get("document");
|
Map<String, String> document = (Map<String, String>) res.getBody().get("document");
|
||||||
|
|
||||||
long wordCount = Long.parseLong(document.get("word_count"));
|
//long wordCount = Long.parseLong(document.get("word_count"));
|
||||||
//批次号
|
//批次号
|
||||||
String batch= (String) res.getBody().get("batch");
|
String batch= (String) res.getBody().get("batch");
|
||||||
String documentId = document.get("id");
|
String documentId = document.get("id");
|
||||||
logger.info("获取文档ID完成: {}, 耗时: {} ms", documentId, System.currentTimeMillis() - stepStartTime);
|
logger.info("获取文档ID完成: {}, 耗时: {} ms", documentId, System.currentTimeMillis() - stepStartTime);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 8. 记录文件信息到助手服务数据库
|
// 8. 记录文件信息到助手服务数据库
|
||||||
stepStartTime = System.currentTimeMillis();
|
stepStartTime = System.currentTimeMillis();
|
||||||
TDatasetFiles datasetFiles = new TDatasetFiles();
|
TDatasetFiles datasetFiles = new TDatasetFiles();
|
||||||
|
|
@ -432,7 +429,7 @@ public class DifyDatasetApiServiceImpl implements DifyDatasetApiService {
|
||||||
List<DifyMetadata> docMetadatas = res.getBody().getDocMetadatas();
|
List<DifyMetadata> docMetadatas = res.getBody().getDocMetadatas();
|
||||||
for (DifyMetadata metadata : docMetadatas) {
|
for (DifyMetadata metadata : docMetadatas) {
|
||||||
if (DifyConstants.METADATA_YSYLC_JSON_URL.equals(metadata.getName())) {
|
if (DifyConstants.METADATA_YSYLC_JSON_URL.equals(metadata.getName())) {
|
||||||
currentMetadatas = metadataService.handleYsylcMetadata(datasetId, documentId, file);
|
currentMetadatas = metadataService.handleYsylcMetadata(datasetId, documentId, file,metadata.getId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -152,7 +152,7 @@ public class MetadataServiceImpl implements MetadataService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DifyMetadata> handleYsylcMetadata(String datasetId, String documentId, MultipartFile file) {
|
public List<DifyMetadata> handleYsylcMetadata(String datasetId, String documentId, MultipartFile file,String metadataId) {
|
||||||
// 异步执行
|
// 异步执行
|
||||||
CompletableFuture.runAsync(() -> {
|
CompletableFuture.runAsync(() -> {
|
||||||
String apiKey = tApiKeyService.getApiKeyFromCache(DifyConstants.API_KEY_YSYLC_DATA_PROC);
|
String apiKey = tApiKeyService.getApiKeyFromCache(DifyConstants.API_KEY_YSYLC_DATA_PROC);
|
||||||
|
|
@ -160,6 +160,7 @@ public class MetadataServiceImpl implements MetadataService {
|
||||||
});
|
});
|
||||||
|
|
||||||
DifyMetadata newMetadata = new DifyMetadata();
|
DifyMetadata newMetadata = new DifyMetadata();
|
||||||
|
newMetadata.setId(metadataId);
|
||||||
newMetadata.setType(DifyConstants.METADATA_TYPE_STRING);
|
newMetadata.setType(DifyConstants.METADATA_TYPE_STRING);
|
||||||
newMetadata.setName(DifyConstants.METADATA_YSYLC_JSON_URL);
|
newMetadata.setName(DifyConstants.METADATA_YSYLC_JSON_URL);
|
||||||
newMetadata.setValue(ysylcJsonPath + file.getOriginalFilename() + ".json");
|
newMetadata.setValue(ysylcJsonPath + file.getOriginalFilename() + ".json");
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.bjtds.brichat.util;
|
||||||
import com.bjtds.brichat.entity.dataset.TDatasetFiles;
|
import com.bjtds.brichat.entity.dataset.TDatasetFiles;
|
||||||
import com.bjtds.brichat.service.DatasetFilesService;
|
import com.bjtds.brichat.service.DatasetFilesService;
|
||||||
import com.bjtds.brichat.service.EsTDatasetFilesService;
|
import com.bjtds.brichat.service.EsTDatasetFilesService;
|
||||||
|
import io.swagger.models.auth.In;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
@ -28,9 +29,9 @@ public class EsTDatasetFilesImporter {
|
||||||
@Autowired
|
@Autowired
|
||||||
private StringRedisTemplate redisTemplate;
|
private StringRedisTemplate redisTemplate;
|
||||||
|
|
||||||
public void importDocumentId(String documentId) throws IOException {
|
public void importDocumentId(Integer fileId) throws IOException {
|
||||||
// 一次性获取完整的文档信息
|
// 一次性获取完整的文档信息
|
||||||
TDatasetFiles datasetFiles = datasetFilesService.getFileById(Integer.valueOf(documentId));
|
TDatasetFiles datasetFiles = datasetFilesService.getFileById(fileId);
|
||||||
if (datasetFiles == null) {
|
if (datasetFiles == null) {
|
||||||
throw new IllegalArgumentException("datasetFiles 不存在");
|
throw new IllegalArgumentException("datasetFiles 不存在");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,3 +85,8 @@ elasticsearch:
|
||||||
#是否删除索引,重新构建索引
|
#是否删除索引,重新构建索引
|
||||||
deleteIndex: ${es-deleteIndex:false}
|
deleteIndex: ${es-deleteIndex:false}
|
||||||
|
|
||||||
|
voice2text:
|
||||||
|
url: ${voice2text-url:http://192.168.1.211:11023/v1/audio/transcriptions}
|
||||||
|
model: ${voice2text-model:whisper-1}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,3 +81,6 @@ elasticsearch:
|
||||||
#是否删除索引,重新构建索引
|
#是否删除索引,重新构建索引
|
||||||
deleteIndex: ${es-deleteIndex:false}
|
deleteIndex: ${es-deleteIndex:false}
|
||||||
|
|
||||||
|
voice2text:
|
||||||
|
url: ${voice2text-url:http://192.168.8.253:11023/v1/audio/transcriptions}
|
||||||
|
model: ${voice2text-model:whisper-1}
|
||||||
Loading…
Reference in New Issue