Compare commits
No commits in common. "982425e21ea53d9136eb25447970c3610c7f2d0b" and "9c98e670e1875e67a03a885bec4486844cb18044" have entirely different histories.
982425e21e
...
9c98e670e1
|
|
@ -183,11 +183,6 @@
|
||||||
<artifactId>tencentcloud-speech-sdk-java</artifactId>
|
<artifactId>tencentcloud-speech-sdk-java</artifactId>
|
||||||
<version>1.0.67</version>
|
<version>1.0.67</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.tencentcloudapi</groupId>
|
|
||||||
<artifactId>tencentcloud-sdk-java-asr</artifactId>
|
|
||||||
<version>3.1.1470</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
||||||
|
|
@ -956,16 +956,16 @@ public class AiModelServiceImpl implements AiModelService {
|
||||||
}
|
}
|
||||||
Map<String, Object> mediaConfig = dto.getMediaConfig() == null ? Collections.emptyMap() : dto.getMediaConfig();
|
Map<String, Object> mediaConfig = dto.getMediaConfig() == null ? Collections.emptyMap() : dto.getMediaConfig();
|
||||||
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_APP_ID)) == null) {
|
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_APP_ID)) == null) {
|
||||||
throw new RuntimeException("腾讯 ASR 模型必须配置 mediaConfig.tencentAppId");
|
throw new RuntimeException("腾讯实时 ASR 模型必须配置 mediaConfig.tencentAppId");
|
||||||
}
|
}
|
||||||
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_SECRET_ID)) == null) {
|
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_SECRET_ID)) == null) {
|
||||||
throw new RuntimeException("腾讯 ASR 模型必须配置 mediaConfig.tencentSecretId");
|
throw new RuntimeException("腾讯实时 ASR 模型必须配置 mediaConfig.tencentSecretId");
|
||||||
}
|
}
|
||||||
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_SECRET_KEY)) == null) {
|
if (readConfigString(mediaConfig.get(MEDIA_TENCENT_SECRET_KEY)) == null) {
|
||||||
throw new RuntimeException("腾讯 ASR 模型必须配置 mediaConfig.tencentSecretKey");
|
throw new RuntimeException("腾讯实时 ASR 模型必须配置 mediaConfig.tencentSecretKey");
|
||||||
}
|
}
|
||||||
if (dto.getModelCode() == null || dto.getModelCode().isBlank()) {
|
if (dto.getModelCode() == null || dto.getModelCode().isBlank()) {
|
||||||
throw new RuntimeException("腾讯 ASR 模型必须配置 modelCode");
|
throw new RuntimeException("腾讯实时 ASR 模型必须配置 modelCode");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,6 @@ import com.imeeting.support.redis.MeetingLockCache;
|
||||||
import com.unisbase.entity.SysUser;
|
import com.unisbase.entity.SysUser;
|
||||||
import com.unisbase.mapper.SysUserMapper;
|
import com.unisbase.mapper.SysUserMapper;
|
||||||
import com.unisbase.service.SysParamService;
|
import com.unisbase.service.SysParamService;
|
||||||
import com.tencentcloudapi.asr.v20190614.AsrClient;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.CreateRecTaskRequest;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.CreateRecTaskResponse;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.DescribeTaskStatusRequest;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.DescribeTaskStatusResponse;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.SentenceDetail;
|
|
||||||
import com.tencentcloudapi.asr.v20190614.models.TaskStatus;
|
|
||||||
import com.tencentcloudapi.common.Credential;
|
|
||||||
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
|
|
||||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
@ -79,12 +69,6 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
||||||
private static final Duration ASR_QUERY_REQUEST_TIMEOUT = Duration.ofSeconds(30);
|
private static final Duration ASR_QUERY_REQUEST_TIMEOUT = Duration.ofSeconds(30);
|
||||||
private static final String DISPATCH_MODE_PARALLEL = "PARALLEL";
|
private static final String DISPATCH_MODE_PARALLEL = "PARALLEL";
|
||||||
private static final String DISPATCH_MODE_SERIAL = "SERIAL";
|
private static final String DISPATCH_MODE_SERIAL = "SERIAL";
|
||||||
private static final String TENCENT_PROVIDER = "tencent";
|
|
||||||
private static final String TENCENT_TASK_ID_KEY = "taskId";
|
|
||||||
private static final String MEDIA_TENCENT_APP_ID = "tencentAppId";
|
|
||||||
private static final String MEDIA_TENCENT_SECRET_ID = "tencentSecretId";
|
|
||||||
private static final String MEDIA_TENCENT_SECRET_KEY = "tencentSecretKey";
|
|
||||||
private static final String TENCENT_ASR_REGION = "ap-guangzhou";
|
|
||||||
|
|
||||||
private final MeetingMapper meetingMapper;
|
private final MeetingMapper meetingMapper;
|
||||||
private final MeetingTranscriptMapper transcriptMapper;
|
private final MeetingTranscriptMapper transcriptMapper;
|
||||||
|
|
@ -625,14 +609,6 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
||||||
log.info("[ASR-PROC] 解析ASR模型成功: meetingId={}, asrTaskId={}, asrModelId={}, baseUrl={}",
|
log.info("[ASR-PROC] 解析ASR模型成功: meetingId={}, asrTaskId={}, asrModelId={}, baseUrl={}",
|
||||||
meeting.getId(), taskRecord.getId(), asrModelId, asrModel.getBaseUrl());
|
meeting.getId(), taskRecord.getId(), asrModelId, asrModel.getBaseUrl());
|
||||||
|
|
||||||
if ("tencent".equalsIgnoreCase(firstNonBlank(asrModel.getProvider()))) {
|
|
||||||
String transcriptText = processTencentOfflineAsr(meeting, taskRecord, asrModel);
|
|
||||||
log.info("[ASR-PROC] Tencent offline transcript persisted: meetingId={}, asrTaskId={}, transcriptLength={}",
|
|
||||||
meeting.getId(), taskRecord.getId(), transcriptText == null ? 0 : transcriptText.length());
|
|
||||||
meetingPointsService.recordAsrSuccessCharge(meeting, taskRecord);
|
|
||||||
return transcriptText;
|
|
||||||
}
|
|
||||||
|
|
||||||
String submitUrl = appendPath(asrModel.getBaseUrl(), "api/v1/asr/transcriptions");
|
String submitUrl = appendPath(asrModel.getBaseUrl(), "api/v1/asr/transcriptions");
|
||||||
String taskId = taskRecord.getResponseData() != null
|
String taskId = taskRecord.getResponseData() != null
|
||||||
? String.valueOf(taskRecord.getResponseData().getOrDefault("task_id", ""))
|
? String.valueOf(taskRecord.getResponseData().getOrDefault("task_id", ""))
|
||||||
|
|
@ -726,124 +702,6 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
||||||
return transcriptText;
|
return transcriptText;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String processTencentOfflineAsr(Meeting meeting, AiTask taskRecord, AiModelVO asrModel) throws Exception {
|
|
||||||
Long taskId = readTencentOfflineTaskId(taskRecord);
|
|
||||||
if (taskId == null) {
|
|
||||||
taskId = submitTencentOfflineTask(meeting, taskRecord, asrModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskStatus taskStatus = null;
|
|
||||||
for (int i = 0; i < 600; i++) {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
taskStatus = queryTencentOfflineTask(asrModel, taskId);
|
|
||||||
if (taskStatus == null) {
|
|
||||||
throw new RuntimeException("腾讯离线 ASR 查询结果为空");
|
|
||||||
}
|
|
||||||
String status = firstNonBlank(taskStatus.getStatusStr(), "");
|
|
||||||
if ("success".equalsIgnoreCase(status)) {
|
|
||||||
updateAiTaskSuccess(taskRecord, objectMapper.valueToTree(buildTencentTaskStatusSnapshot(taskStatus)));
|
|
||||||
return saveTencentOfflineTranscripts(meeting, taskStatus.getResultDetail());
|
|
||||||
}
|
|
||||||
if ("failed".equalsIgnoreCase(status)) {
|
|
||||||
String errorMsg = firstNonBlank(taskStatus.getErrorMsg(), "腾讯离线 ASR 识别失败");
|
|
||||||
updateAiTaskFail(taskRecord, errorMsg);
|
|
||||||
throw new RuntimeException(errorMsg);
|
|
||||||
}
|
|
||||||
updateProgress(meeting.getId(), 5, "腾讯离线 ASR 识别中...", 0);
|
|
||||||
}
|
|
||||||
throw new RuntimeException("腾讯离线 ASR 轮询超时");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Map<String, Object> buildTencentOfflineCreateRequest(Meeting meeting, AiTask taskRecord, AiModelVO asrModel) {
|
|
||||||
Map<String, Object> req = new HashMap<>();
|
|
||||||
req.put("engineModelType", asrModel.getModelCode());
|
|
||||||
req.put("channelNum", 1L);
|
|
||||||
req.put("resTextFormat", 2L);
|
|
||||||
req.put("sourceType", 0L);
|
|
||||||
req.put("url", resolveTencentOfflineAudioUrl(meeting));
|
|
||||||
req.put("speakerDiarization", resolveTencentSpeakerDiarization(taskRecord));
|
|
||||||
req.put("speakerNumber", 0L);
|
|
||||||
String hotwordList = buildTencentHotwordList(taskRecord);
|
|
||||||
if (hotwordList != null) {
|
|
||||||
req.put("hotwordList", hotwordList);
|
|
||||||
}
|
|
||||||
return req;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Long submitTencentOfflineTask(Meeting meeting, AiTask taskRecord, AiModelVO asrModel) throws Exception {
|
|
||||||
updateProgress(meeting.getId(), 5, "提交腾讯离线 ASR 任务...", 0);
|
|
||||||
meetingPointsService.assertSufficientPointsBeforeAsrSubmit(meeting, taskRecord);
|
|
||||||
|
|
||||||
Map<String, Object> reqSnapshot = buildTencentOfflineCreateRequest(meeting, taskRecord, asrModel);
|
|
||||||
taskRecord.setRequestData(reqSnapshot);
|
|
||||||
this.updateById(taskRecord);
|
|
||||||
|
|
||||||
CreateRecTaskRequest request = new CreateRecTaskRequest();
|
|
||||||
request.setEngineModelType(String.valueOf(reqSnapshot.get("engineModelType")));
|
|
||||||
request.setChannelNum(longValue(reqSnapshot.get("channelNum")));
|
|
||||||
request.setResTextFormat(longValue(reqSnapshot.get("resTextFormat")));
|
|
||||||
request.setSourceType(longValue(reqSnapshot.get("sourceType")));
|
|
||||||
request.setUrl(String.valueOf(reqSnapshot.get("url")));
|
|
||||||
request.setSpeakerDiarization(longValue(reqSnapshot.get("speakerDiarization")));
|
|
||||||
request.setSpeakerNumber(longValue(reqSnapshot.get("speakerNumber")));
|
|
||||||
String hotwordList = stringValue(reqSnapshot.get("hotwordList"));
|
|
||||||
if (hotwordList != null && !hotwordList.isBlank()) {
|
|
||||||
request.setHotwordList(hotwordList);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateRecTaskResponse response = buildTencentOfflineAsrClient(asrModel).CreateRecTask(request);
|
|
||||||
if (response == null || response.getData() == null || response.getData().getTaskId() == null) {
|
|
||||||
throw new RuntimeException("腾讯离线 ASR 提交失败:未返回 TaskId");
|
|
||||||
}
|
|
||||||
Long taskId = response.getData().getTaskId();
|
|
||||||
writeTencentOfflineTaskId(taskRecord, taskId);
|
|
||||||
this.updateById(taskRecord);
|
|
||||||
return taskId;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected TaskStatus queryTencentOfflineTask(AiModelVO asrModel, Long taskId) throws TencentCloudSDKException {
|
|
||||||
DescribeTaskStatusRequest request = new DescribeTaskStatusRequest();
|
|
||||||
request.setTaskId(taskId);
|
|
||||||
DescribeTaskStatusResponse response = buildTencentOfflineAsrClient(asrModel).DescribeTaskStatus(request);
|
|
||||||
return response == null ? null : response.getData();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String saveTencentOfflineTranscripts(Meeting meeting, SentenceDetail[] resultDetail) {
|
|
||||||
transcriptMapper.delete(new LambdaQueryWrapper<MeetingTranscript>().eq(MeetingTranscript::getMeetingId, meeting.getId()));
|
|
||||||
|
|
||||||
if (resultDetail == null || resultDetail.length == 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int order = 0;
|
|
||||||
for (SentenceDetail detail : resultDetail) {
|
|
||||||
if (detail == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String content = firstNonBlank(detail.getFinalSentence(), detail.getWrittenText(), "");
|
|
||||||
if (content == null || content.isBlank()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String speakerId = String.valueOf(detail.getSpeakerId() == null ? 0L : detail.getSpeakerId());
|
|
||||||
String speakerName = "未知说话人" + speakerId;
|
|
||||||
|
|
||||||
MeetingTranscript mt = new MeetingTranscript();
|
|
||||||
mt.setMeetingId(meeting.getId());
|
|
||||||
mt.setSpeakerId(speakerId);
|
|
||||||
mt.setSpeakerName(speakerName);
|
|
||||||
mt.setContent(content.trim());
|
|
||||||
fillTencentTranscriptTime(mt, detail);
|
|
||||||
mt.setSortOrder(order++);
|
|
||||||
transcriptMapper.insert(mt);
|
|
||||||
sb.append(speakerName).append(": ").append(mt.getContent()).append("\n");
|
|
||||||
}
|
|
||||||
if (order > 0) {
|
|
||||||
meetingTranscriptFileService.initializeTranscriptFileIfAbsent(meeting.getId());
|
|
||||||
}
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> buildAsrRequest(Meeting meeting, AiTask taskRecord, AiModelVO asrModel) {
|
private Map<String, Object> buildAsrRequest(Meeting meeting, AiTask taskRecord, AiModelVO asrModel) {
|
||||||
Map<String, Object> req = new HashMap<>();
|
Map<String, Object> req = new HashMap<>();
|
||||||
String rawAudioUrl = meeting.getAudioUrl();
|
String rawAudioUrl = meeting.getAudioUrl();
|
||||||
|
|
@ -1559,93 +1417,6 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AsrClient buildTencentOfflineAsrClient(AiModelVO asrModel) {
|
|
||||||
Map<String, Object> mediaConfig = asrModel.getMediaConfig() == null ? Map.of() : asrModel.getMediaConfig();
|
|
||||||
String secretId = requireTencentMediaConfig(mediaConfig, MEDIA_TENCENT_SECRET_ID);
|
|
||||||
String secretKey = requireTencentMediaConfig(mediaConfig, MEDIA_TENCENT_SECRET_KEY);
|
|
||||||
Credential credential = new Credential(secretId, secretKey);
|
|
||||||
ClientProfile profile = new ClientProfile();
|
|
||||||
return new AsrClient(credential, TENCENT_ASR_REGION, profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String requireTencentMediaConfig(Map<String, Object> mediaConfig, String key) {
|
|
||||||
String value = stringValue(mediaConfig.get(key));
|
|
||||||
if (value == null || value.isBlank()) {
|
|
||||||
throw new RuntimeException("腾讯离线 ASR 缺少配置: " + key);
|
|
||||||
}
|
|
||||||
return value.trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long resolveTencentSpeakerDiarization(AiTask taskRecord) {
|
|
||||||
Object useSpkObj = taskRecord.getTaskConfig() == null ? null : taskRecord.getTaskConfig().get("useSpkId");
|
|
||||||
return "1".equals(String.valueOf(useSpkObj)) ? 1L : 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String buildTencentHotwordList(AiTask taskRecord) {
|
|
||||||
if (taskRecord == null || taskRecord.getTaskConfig() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Object hotWordsObj = taskRecord.getTaskConfig().get("hotWords");
|
|
||||||
if (!(hotWordsObj instanceof List<?> words) || words.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return words.stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(String::valueOf)
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(word -> !word.isBlank())
|
|
||||||
.map(word -> word + "|5")
|
|
||||||
.collect(Collectors.joining(","));
|
|
||||||
}
|
|
||||||
|
|
||||||
private String resolveTencentOfflineAudioUrl(Meeting meeting) {
|
|
||||||
if (meeting == null || meeting.getAudioUrl() == null || meeting.getAudioUrl().isBlank()) {
|
|
||||||
throw new RuntimeException("腾讯离线 ASR 缺少音频地址");
|
|
||||||
}
|
|
||||||
String audioUrl = meeting.getAudioUrl().trim();
|
|
||||||
if (audioUrl.startsWith("http://") || audioUrl.startsWith("https://")) {
|
|
||||||
return audioUrl;
|
|
||||||
}
|
|
||||||
return serverBaseUrl + (audioUrl.startsWith("/") ? "" : "/") + audioUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTencentOfflineTaskId(AiTask taskRecord, Long taskId) {
|
|
||||||
Map<String, Object> responseData = taskRecord.getResponseData() == null
|
|
||||||
? new HashMap<>()
|
|
||||||
: new HashMap<>(taskRecord.getResponseData());
|
|
||||||
responseData.put(TENCENT_TASK_ID_KEY, taskId);
|
|
||||||
taskRecord.setResponseData(responseData);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Long readTencentOfflineTaskId(AiTask taskRecord) {
|
|
||||||
if (taskRecord == null || taskRecord.getResponseData() == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Object taskId = taskRecord.getResponseData().get(TENCENT_TASK_ID_KEY);
|
|
||||||
return longValue(taskId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Map<String, Object> buildTencentTaskStatusSnapshot(TaskStatus taskStatus) {
|
|
||||||
Map<String, Object> snapshot = new HashMap<>();
|
|
||||||
snapshot.put("taskId", taskStatus.getTaskId());
|
|
||||||
snapshot.put("status", taskStatus.getStatus());
|
|
||||||
snapshot.put("statusStr", taskStatus.getStatusStr());
|
|
||||||
snapshot.put("result", taskStatus.getResult());
|
|
||||||
snapshot.put("errorMsg", taskStatus.getErrorMsg());
|
|
||||||
snapshot.put("audioDuration", taskStatus.getAudioDuration());
|
|
||||||
return snapshot;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillTencentTranscriptTime(MeetingTranscript transcript, SentenceDetail detail) {
|
|
||||||
if (transcript == null || detail == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int startTime = detail.getStartMs() == null ? 0 : detail.getStartMs().intValue();
|
|
||||||
int endTime = detail.getEndMs() == null ? startTime : detail.getEndMs().intValue();
|
|
||||||
transcript.setStartTime(startTime);
|
|
||||||
transcript.setEndTime(endTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AiModelVO resolveAsrModelForRevision(AiTask asrTask) {
|
private AiModelVO resolveAsrModelForRevision(AiTask asrTask) {
|
||||||
if (asrTask == null || asrTask.getTaskConfig() == null) {
|
if (asrTask == null || asrTask.getTaskConfig() == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -525,6 +525,7 @@ public class LocalRealtimeAsrChannel implements RealtimeAsrChannel {
|
||||||
log.error("上游 ASR websocket 异常:meetingId={}, sessionId={}, upstream={}",
|
log.error("上游 ASR websocket 异常:meetingId={}, sessionId={}, upstream={}",
|
||||||
context.getMeetingId(), currentConnectionId(context), context.getTargetWsUrl(), error);
|
context.getMeetingId(), currentConnectionId(context), context.getTargetWsUrl(), error);
|
||||||
context.getChannelState().remove(STATE_UPSTREAM_SOCKET);
|
context.getChannelState().remove(STATE_UPSTREAM_SOCKET);
|
||||||
|
context.getCallback().removeMeetingSession(context.getMeetingId());
|
||||||
context.getCallback().sendFrontendError(context.getMeetingId(),
|
context.getCallback().sendFrontendError(context.getMeetingId(),
|
||||||
"REALTIME_UPSTREAM_ERROR",
|
"REALTIME_UPSTREAM_ERROR",
|
||||||
error == null || error.getMessage() == null || error.getMessage().isBlank()
|
error == null || error.getMessage() == null || error.getMessage().isBlank()
|
||||||
|
|
|
||||||
|
|
@ -349,30 +349,6 @@ class AiModelServiceImplTest {
|
||||||
assertNull(captor.getValue().getApiKey());
|
assertNull(captor.getValue().getApiKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void saveModelShouldRejectTencentAsrWithoutAppId() {
|
|
||||||
AiModelServiceImpl service = new AiModelServiceImpl(
|
|
||||||
objectMapper,
|
|
||||||
mock(AsrModelMapper.class),
|
|
||||||
mock(LlmModelMapper.class)
|
|
||||||
);
|
|
||||||
|
|
||||||
AiModelDTO dto = new AiModelDTO();
|
|
||||||
dto.setModelType("ASR");
|
|
||||||
dto.setModelName("tencent-asr");
|
|
||||||
dto.setProvider("tencent");
|
|
||||||
dto.setModelCode("16k_zh");
|
|
||||||
dto.setIsDefault(0);
|
|
||||||
dto.setStatus(1);
|
|
||||||
dto.setMediaConfig(Map.of(
|
|
||||||
"tencentSecretId", "secret-id",
|
|
||||||
"tencentSecretKey", "secret-key"
|
|
||||||
));
|
|
||||||
|
|
||||||
RuntimeException ex = assertThrows(RuntimeException.class, () -> service.saveModel(dto));
|
|
||||||
assertEquals("腾讯 ASR 模型必须配置 mediaConfig.tencentAppId", ex.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void saveModelShouldRejectTencentAsrWithoutSecretKey() {
|
void saveModelShouldRejectTencentAsrWithoutSecretKey() {
|
||||||
AiModelServiceImpl service = new AiModelServiceImpl(
|
AiModelServiceImpl service = new AiModelServiceImpl(
|
||||||
|
|
@ -394,7 +370,7 @@ class AiModelServiceImplTest {
|
||||||
));
|
));
|
||||||
|
|
||||||
RuntimeException ex = assertThrows(RuntimeException.class, () -> service.saveModel(dto));
|
RuntimeException ex = assertThrows(RuntimeException.class, () -> service.saveModel(dto));
|
||||||
assertEquals("腾讯 ASR 模型必须配置 mediaConfig.tencentSecretKey", ex.getMessage());
|
assertEquals("腾讯实时 ASR 模型必须配置 mediaConfig.tencentSecretKey", ex.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
||||||
|
|
@ -1,304 +1,152 @@
|
||||||
package com.imeeting.service.biz.impl;
|
//package com.imeeting.service.biz.impl;
|
||||||
|
//
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
//import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.imeeting.dto.biz.AiModelVO;
|
//import com.imeeting.dto.biz.AiModelVO;
|
||||||
import com.imeeting.entity.biz.AiTask;
|
//import com.imeeting.entity.biz.AiTask;
|
||||||
import com.imeeting.entity.biz.Meeting;
|
//import com.imeeting.entity.biz.HotWord;
|
||||||
import com.imeeting.mapper.biz.AiTaskMapper;
|
//import com.imeeting.entity.biz.Meeting;
|
||||||
import com.imeeting.mapper.biz.MeetingMapper;
|
//import com.imeeting.mapper.biz.MeetingMapper;
|
||||||
import com.imeeting.mapper.biz.MeetingTranscriptMapper;
|
//import com.imeeting.mapper.biz.MeetingTranscriptMapper;
|
||||||
import com.imeeting.service.android.AndroidMeetingPushService;
|
//import com.imeeting.service.biz.AiModelService;
|
||||||
import com.imeeting.service.biz.AiModelService;
|
//import com.imeeting.service.biz.HotWordService;
|
||||||
import com.imeeting.service.biz.HotWordService;
|
//import com.imeeting.service.biz.MeetingProgressService;
|
||||||
import com.imeeting.service.biz.MeetingPointsService;
|
//import com.imeeting.service.biz.MeetingSummaryFileService;
|
||||||
import com.imeeting.service.biz.MeetingProgressService;
|
//import com.imeeting.service.biz.MeetingTranscriptChapterService;
|
||||||
import com.imeeting.service.biz.MeetingSummaryFileService;
|
//import com.imeeting.service.biz.MeetingTranscriptFileService;
|
||||||
import com.imeeting.service.biz.MeetingTranscriptChapterService;
|
//import com.imeeting.support.RedisValueSupport;
|
||||||
import com.imeeting.service.biz.MeetingTranscriptFileService;
|
//import com.imeeting.support.TaskSecurityContextRunner;
|
||||||
import com.imeeting.support.TaskSecurityContextRunner;
|
//import com.unisbase.mapper.SysUserMapper;
|
||||||
import com.imeeting.support.redis.MeetingAsrPermitCache;
|
//import com.unisbase.service.SysParamService;
|
||||||
import com.imeeting.support.redis.MeetingLockCache;
|
//import org.junit.jupiter.api.Test;
|
||||||
import com.unisbase.mapper.SysUserMapper;
|
//import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import com.unisbase.service.SysParamService;
|
//
|
||||||
import com.tencentcloudapi.asr.v20190614.models.SentenceDetail;
|
//import java.util.HashMap;
|
||||||
import com.tencentcloudapi.asr.v20190614.models.TaskStatus;
|
//import java.util.List;
|
||||||
import org.junit.jupiter.api.Test;
|
//import java.util.Map;
|
||||||
import org.springframework.test.util.ReflectionTestUtils;
|
//
|
||||||
|
//import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import java.util.HashMap;
|
//import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import java.util.Map;
|
//import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
//import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
//import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
//import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
//import static org.mockito.Mockito.when;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
//
|
||||||
import static org.mockito.Mockito.doReturn;
|
//class AiTaskServiceImplTest {
|
||||||
import static org.mockito.Mockito.mock;
|
//
|
||||||
import static org.mockito.Mockito.never;
|
// @Test
|
||||||
import static org.mockito.Mockito.spy;
|
// void buildAsrRequestShouldFollowCurrentOfflineAsrContract() {
|
||||||
import static org.mockito.Mockito.verify;
|
// HotWordService hotWordService = mock(HotWordService.class);
|
||||||
import static org.mockito.Mockito.when;
|
// HotWord hotWord = new HotWord();
|
||||||
|
// hotWord.setWord("汇智");
|
||||||
class AiTaskServiceImplTest {
|
// hotWord.setWeight(25);
|
||||||
|
// when(hotWordService.list(any())).thenReturn(List.of(hotWord));
|
||||||
@Test
|
//
|
||||||
void processAsrTaskShouldUseTencentOfflineBranchWhenProviderIsTencent() throws Exception {
|
// AiTaskServiceImpl service = new AiTaskServiceImpl(
|
||||||
MeetingMapper meetingMapper = mock(MeetingMapper.class);
|
// mock(MeetingMapper.class),
|
||||||
MeetingTranscriptMapper transcriptMapper = mock(MeetingTranscriptMapper.class);
|
// mock(MeetingTranscriptMapper.class),
|
||||||
AiModelService aiModelService = mock(AiModelService.class);
|
// mock(AiModelService.class),
|
||||||
HotWordService hotWordService = mock(HotWordService.class);
|
// new ObjectMapper(),
|
||||||
MeetingLockCache meetingLockCache = mock(MeetingLockCache.class);
|
// mock(SysUserMapper.class),
|
||||||
MeetingAsrPermitCache meetingAsrPermitCache = mock(MeetingAsrPermitCache.class);
|
// hotWordService,
|
||||||
MeetingProgressService meetingProgressService = mock(MeetingProgressService.class);
|
// mock(RedisValueSupport.class),
|
||||||
MeetingPointsService meetingPointsService = mock(MeetingPointsService.class);
|
// mock(MeetingProgressService.class),
|
||||||
MeetingSummaryFileService meetingSummaryFileService = mock(MeetingSummaryFileService.class);
|
// mock(MeetingSummaryFileService.class),
|
||||||
MeetingTranscriptFileService meetingTranscriptFileService = mock(MeetingTranscriptFileService.class);
|
// mock(MeetingTranscriptFileService.class),
|
||||||
MeetingTranscriptChapterService meetingTranscriptChapterService = mock(MeetingTranscriptChapterService.class);
|
// mock(MeetingTranscriptChapterService.class),
|
||||||
MeetingSummaryPromptAssembler meetingSummaryPromptAssembler = mock(MeetingSummaryPromptAssembler.class);
|
// mock(MeetingSummaryPromptAssembler.class),
|
||||||
TaskSecurityContextRunner taskSecurityContextRunner = mock(TaskSecurityContextRunner.class);
|
// mock(TaskSecurityContextRunner.class),
|
||||||
MeetingExternalSummaryWebhookTrigger meetingExternalSummaryWebhookTrigger = mock(MeetingExternalSummaryWebhookTrigger.class);
|
// mock(MeetingExternalSummaryWebhookTrigger.class),
|
||||||
SysParamService sysParamService = mock(SysParamService.class);
|
// mock(SysParamService.class)
|
||||||
|
// );
|
||||||
AiTaskServiceImpl service = spy(new AiTaskServiceImpl(
|
// ReflectionTestUtils.setField(service, "serverBaseUrl", "http://localhost:8080");
|
||||||
meetingMapper,
|
//
|
||||||
transcriptMapper,
|
// Meeting meeting = new Meeting();
|
||||||
aiModelService,
|
// meeting.setAudioUrl("/api/static/meetings/12/source audio.mp4");
|
||||||
new ObjectMapper(),
|
//
|
||||||
mock(SysUserMapper.class),
|
// AiTask task = new AiTask();
|
||||||
hotWordService,
|
// Map<String, Object> taskConfig = new HashMap<>();
|
||||||
meetingLockCache,
|
// taskConfig.put("useSpkId", 1);
|
||||||
meetingAsrPermitCache,
|
// taskConfig.put("enableTextRefine", true);
|
||||||
meetingProgressService,
|
// taskConfig.put("hotWords", List.of("汇智"));
|
||||||
meetingPointsService,
|
// task.setTaskConfig(taskConfig);
|
||||||
meetingSummaryFileService,
|
//
|
||||||
meetingTranscriptFileService,
|
// AiModelVO asrModel = new AiModelVO();
|
||||||
meetingTranscriptChapterService,
|
// asrModel.setModelCode("legacy-model-code");
|
||||||
meetingSummaryPromptAssembler,
|
//
|
||||||
taskSecurityContextRunner,
|
// @SuppressWarnings("unchecked")
|
||||||
meetingExternalSummaryWebhookTrigger,
|
// Map<String, Object> request = (Map<String, Object>) ReflectionTestUtils.invokeMethod(
|
||||||
sysParamService
|
// service,
|
||||||
));
|
// "buildAsrRequest",
|
||||||
ReflectionTestUtils.setField(service, "baseMapper", mock(AiTaskMapper.class));
|
// meeting,
|
||||||
ReflectionTestUtils.setField(service, "androidMeetingPushService", mock(AndroidMeetingPushService.class));
|
// task,
|
||||||
|
// asrModel
|
||||||
Meeting meeting = new Meeting();
|
// );
|
||||||
meeting.setId(1L);
|
//
|
||||||
meeting.setAudioUrl("https://cdn.example.com/audio/demo.m4a");
|
// assertEquals("http://localhost:8080/api/static/meetings/12/source%20audio.mp4", request.get("audio_address"));
|
||||||
|
// assertFalse(request.containsKey("file_url"));
|
||||||
AiTask task = new AiTask();
|
//
|
||||||
task.setId(11L);
|
// @SuppressWarnings("unchecked")
|
||||||
task.setMeetingId(1L);
|
// Map<String, Object> config = (Map<String, Object>) request.get("config");
|
||||||
task.setTaskType("ASR");
|
// assertEquals(Boolean.TRUE, config.get("enable_speaker"));
|
||||||
task.setTaskConfig(new HashMap<>(Map.of(
|
// assertEquals(Boolean.TRUE, config.get("match_speaker_registry"));
|
||||||
"asrModelId", 101L,
|
// assertEquals(Boolean.TRUE, config.get("enable_text_cleanup"));
|
||||||
"useSpkId", 1,
|
// assertFalse(config.containsKey("enable_text_refine"));
|
||||||
"enableTextRefine", true
|
// assertFalse(config.containsKey("enable_two_pass"));
|
||||||
)));
|
// assertFalse(config.containsKey("model"));
|
||||||
|
//
|
||||||
AiModelVO model = new AiModelVO();
|
// @SuppressWarnings("unchecked")
|
||||||
model.setId(101L);
|
// List<Map<String, Object>> hotwords = (List<Map<String, Object>>) config.get("hotwords");
|
||||||
model.setProvider("tencent");
|
// assertEquals(1, hotwords.size());
|
||||||
model.setModelCode("16k_zh");
|
// assertEquals("汇智", hotwords.get(0).get("hotword"));
|
||||||
model.setMediaConfig(Map.of(
|
// assertEquals(2.5, hotwords.get(0).get("weight"));
|
||||||
"tencentAppId", "123456",
|
// }
|
||||||
"tencentSecretId", "secret-id",
|
//
|
||||||
"tencentSecretKey", "secret-key"
|
// @Test
|
||||||
));
|
// void buildAsrRequestShouldDisableRegistryMatchWhenSpeakerSplitDisabled() {
|
||||||
when(aiModelService.getModelById(101L, "ASR")).thenReturn(model);
|
// AiTaskServiceImpl service = new AiTaskServiceImpl(
|
||||||
|
// mock(MeetingMapper.class),
|
||||||
doReturn(true).when(service).updateById(any(AiTask.class));
|
// mock(MeetingTranscriptMapper.class),
|
||||||
doReturn("腾讯离线转写结果").when(service).processTencentOfflineAsr(eq(meeting), eq(task), eq(model));
|
// mock(AiModelService.class),
|
||||||
|
// new ObjectMapper(),
|
||||||
String result = ReflectionTestUtils.invokeMethod(service, "processAsrTask", meeting, task);
|
// mock(SysUserMapper.class),
|
||||||
|
// mock(HotWordService.class),
|
||||||
assertEquals("腾讯离线转写结果", result);
|
// mock(RedisValueSupport.class),
|
||||||
verify(service).processTencentOfflineAsr(meeting, task, model);
|
// mock(MeetingProgressService.class),
|
||||||
verify(meetingPointsService).recordAsrSuccessCharge(meeting, task);
|
// mock(MeetingSummaryFileService.class),
|
||||||
}
|
// mock(MeetingTranscriptFileService.class),
|
||||||
|
// mock(MeetingTranscriptChapterService.class),
|
||||||
@Test
|
// mock(MeetingSummaryPromptAssembler.class),
|
||||||
void buildTencentOfflineCreateRequestShouldMapMeetingAndTaskConfig() {
|
// mock(TaskSecurityContextRunner.class),
|
||||||
AiTaskServiceImpl service = createService(mock(MeetingPointsService.class));
|
// mock(MeetingExternalSummaryWebhookTrigger.class),
|
||||||
ReflectionTestUtils.setField(service, "serverBaseUrl", "https://server.example.com");
|
// mock(SysParamService.class)
|
||||||
|
// );
|
||||||
Meeting meeting = new Meeting();
|
// ReflectionTestUtils.setField(service, "serverBaseUrl", "http://localhost:8080");
|
||||||
meeting.setId(2L);
|
//
|
||||||
meeting.setAudioUrl("/upload/audio/demo.m4a");
|
// Meeting meeting = new Meeting();
|
||||||
|
// meeting.setAudioUrl("/api/static/audio/demo.wav");
|
||||||
AiTask task = new AiTask();
|
//
|
||||||
task.setTaskConfig(new HashMap<>(Map.of(
|
// AiTask task = new AiTask();
|
||||||
"useSpkId", 1,
|
// Map<String, Object> taskConfig = new HashMap<>();
|
||||||
"enableTextRefine", true,
|
// taskConfig.put("useSpkId", 0);
|
||||||
"hotWords", java.util.List.of("腾讯会议", "离线转写")
|
// taskConfig.put("enableTextRefine", false);
|
||||||
)));
|
// task.setTaskConfig(taskConfig);
|
||||||
|
//
|
||||||
AiModelVO model = new AiModelVO();
|
// @SuppressWarnings("unchecked")
|
||||||
model.setModelCode("16k_zh");
|
// Map<String, Object> request = (Map<String, Object>) ReflectionTestUtils.invokeMethod(
|
||||||
model.setMediaConfig(Map.of(
|
// service,
|
||||||
"tencentAppId", "123456",
|
// "buildAsrRequest",
|
||||||
"tencentSecretId", "secret-id",
|
// meeting,
|
||||||
"tencentSecretKey", "secret-key"
|
// task,
|
||||||
));
|
// new AiModelVO()
|
||||||
|
// );
|
||||||
@SuppressWarnings("unchecked")
|
//
|
||||||
Map<String, Object> request = (Map<String, Object>) ReflectionTestUtils.invokeMethod(
|
// @SuppressWarnings("unchecked")
|
||||||
service,
|
// Map<String, Object> config = (Map<String, Object>) request.get("config");
|
||||||
"buildTencentOfflineCreateRequest",
|
// assertEquals(Boolean.FALSE, config.get("enable_speaker"));
|
||||||
meeting,
|
// assertEquals(Boolean.FALSE, config.get("match_speaker_registry"));
|
||||||
task,
|
// assertEquals(Boolean.FALSE, config.get("enable_text_cleanup"));
|
||||||
model
|
// assertTrue(((List<?>) config.get("hotwords")).isEmpty());
|
||||||
);
|
// assertNull(request.get("file_url"));
|
||||||
|
// }
|
||||||
assertEquals("16k_zh", request.get("engineModelType"));
|
//}
|
||||||
assertEquals(1L, request.get("channelNum"));
|
|
||||||
assertEquals(2L, request.get("resTextFormat"));
|
|
||||||
assertEquals(0L, request.get("sourceType"));
|
|
||||||
assertEquals("https://server.example.com/upload/audio/demo.m4a", request.get("url"));
|
|
||||||
assertEquals(1L, request.get("speakerDiarization"));
|
|
||||||
assertEquals(0L, request.get("speakerNumber"));
|
|
||||||
assertEquals("腾讯会议|5,离线转写|5", request.get("hotwordList"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void buildTencentOfflineCreateRequestShouldKeepAbsoluteAudioUrl() {
|
|
||||||
AiTaskServiceImpl service = createService(mock(MeetingPointsService.class));
|
|
||||||
ReflectionTestUtils.setField(service, "serverBaseUrl", "https://server.example.com");
|
|
||||||
|
|
||||||
Meeting meeting = new Meeting();
|
|
||||||
meeting.setId(22L);
|
|
||||||
meeting.setAudioUrl("https://cdn.example.com/audio/demo.m4a");
|
|
||||||
|
|
||||||
AiTask task = new AiTask();
|
|
||||||
task.setTaskConfig(new HashMap<>(Map.of("useSpkId", 0)));
|
|
||||||
|
|
||||||
AiModelVO model = new AiModelVO();
|
|
||||||
model.setModelCode("16k_zh");
|
|
||||||
model.setMediaConfig(Map.of(
|
|
||||||
"tencentAppId", "123456",
|
|
||||||
"tencentSecretId", "secret-id",
|
|
||||||
"tencentSecretKey", "secret-key"
|
|
||||||
));
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Map<String, Object> request = (Map<String, Object>) ReflectionTestUtils.invokeMethod(
|
|
||||||
service,
|
|
||||||
"buildTencentOfflineCreateRequest",
|
|
||||||
meeting,
|
|
||||||
task,
|
|
||||||
model
|
|
||||||
);
|
|
||||||
|
|
||||||
assertEquals("https://cdn.example.com/audio/demo.m4a", request.get("url"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void processTencentOfflineAsrShouldPollUntilSuccessAndReturnTranscriptText() throws Exception {
|
|
||||||
MeetingPointsService meetingPointsService = mock(MeetingPointsService.class);
|
|
||||||
AiTaskServiceImpl service = spy(createService(meetingPointsService));
|
|
||||||
|
|
||||||
Meeting meeting = new Meeting();
|
|
||||||
meeting.setId(3L);
|
|
||||||
meeting.setAudioUrl("https://cdn.example.com/audio/demo.m4a");
|
|
||||||
|
|
||||||
AiTask task = new AiTask();
|
|
||||||
task.setId(31L);
|
|
||||||
task.setMeetingId(3L);
|
|
||||||
task.setTaskType("ASR");
|
|
||||||
task.setTaskConfig(new HashMap<>(Map.of(
|
|
||||||
"asrModelId", 101L,
|
|
||||||
"useSpkId", 1
|
|
||||||
)));
|
|
||||||
|
|
||||||
AiModelVO model = new AiModelVO();
|
|
||||||
model.setProvider("tencent");
|
|
||||||
model.setModelCode("16k_zh");
|
|
||||||
model.setMediaConfig(Map.of(
|
|
||||||
"tencentAppId", "123456",
|
|
||||||
"tencentSecretId", "secret-id",
|
|
||||||
"tencentSecretKey", "secret-key"
|
|
||||||
));
|
|
||||||
|
|
||||||
TaskStatus doing = new TaskStatus();
|
|
||||||
doing.setStatusStr("doing");
|
|
||||||
|
|
||||||
SentenceDetail sentence = new SentenceDetail();
|
|
||||||
sentence.setSpeakerId(3L);
|
|
||||||
sentence.setFinalSentence("测试文本");
|
|
||||||
sentence.setStartMs(1000L);
|
|
||||||
sentence.setEndMs(2500L);
|
|
||||||
|
|
||||||
TaskStatus success = new TaskStatus();
|
|
||||||
success.setStatusStr("success");
|
|
||||||
success.setResultDetail(new SentenceDetail[]{sentence});
|
|
||||||
|
|
||||||
doReturn(90001L).when(service).submitTencentOfflineTask(meeting, task, model);
|
|
||||||
doReturn(doing).doReturn(success).when(service).queryTencentOfflineTask(model, 90001L);
|
|
||||||
doReturn("未知说话人3: 测试文本\n").when(service).saveTencentOfflineTranscripts(meeting, success.getResultDetail());
|
|
||||||
doReturn(true).when(service).updateById(any(AiTask.class));
|
|
||||||
|
|
||||||
String text = service.processTencentOfflineAsr(meeting, task, model);
|
|
||||||
|
|
||||||
assertEquals("未知说话人3: 测试文本\n", text);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void saveTencentOfflineTranscriptsShouldOverwriteExistingRowsAndUseUnknownSpeakerName() {
|
|
||||||
MeetingPointsService meetingPointsService = mock(MeetingPointsService.class);
|
|
||||||
MeetingTranscriptMapper transcriptMapper = mock(MeetingTranscriptMapper.class);
|
|
||||||
MeetingTranscriptFileService transcriptFileService = mock(MeetingTranscriptFileService.class);
|
|
||||||
AiTaskServiceImpl service = createService(meetingPointsService, transcriptMapper, transcriptFileService);
|
|
||||||
|
|
||||||
Meeting meeting = new Meeting();
|
|
||||||
meeting.setId(5L);
|
|
||||||
|
|
||||||
SentenceDetail first = new SentenceDetail();
|
|
||||||
first.setSpeakerId(7L);
|
|
||||||
first.setFinalSentence("第一句");
|
|
||||||
first.setStartMs(0L);
|
|
||||||
first.setEndMs(1000L);
|
|
||||||
|
|
||||||
SentenceDetail second = new SentenceDetail();
|
|
||||||
second.setSpeakerId(7L);
|
|
||||||
second.setFinalSentence("第二句");
|
|
||||||
second.setStartMs(1000L);
|
|
||||||
second.setEndMs(2000L);
|
|
||||||
|
|
||||||
String text = service.saveTencentOfflineTranscripts(meeting, new SentenceDetail[]{first, second});
|
|
||||||
|
|
||||||
assertEquals("未知说话人7: 第一句\n未知说话人7: 第二句\n", text);
|
|
||||||
verify(transcriptMapper).delete(any());
|
|
||||||
verify(transcriptMapper, org.mockito.Mockito.times(2)).insert(any());
|
|
||||||
verify(transcriptFileService).initializeTranscriptFileIfAbsent(5L);
|
|
||||||
}
|
|
||||||
|
|
||||||
private AiTaskServiceImpl createService(MeetingPointsService meetingPointsService) {
|
|
||||||
return createService(meetingPointsService, mock(MeetingTranscriptMapper.class), mock(MeetingTranscriptFileService.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private AiTaskServiceImpl createService(MeetingPointsService meetingPointsService,
|
|
||||||
MeetingTranscriptMapper transcriptMapper,
|
|
||||||
MeetingTranscriptFileService transcriptFileService) {
|
|
||||||
AiTaskServiceImpl service = new AiTaskServiceImpl(
|
|
||||||
mock(MeetingMapper.class),
|
|
||||||
transcriptMapper,
|
|
||||||
mock(AiModelService.class),
|
|
||||||
new ObjectMapper(),
|
|
||||||
mock(SysUserMapper.class),
|
|
||||||
mock(HotWordService.class),
|
|
||||||
mock(MeetingLockCache.class),
|
|
||||||
mock(MeetingAsrPermitCache.class),
|
|
||||||
mock(MeetingProgressService.class),
|
|
||||||
meetingPointsService,
|
|
||||||
mock(MeetingSummaryFileService.class),
|
|
||||||
transcriptFileService,
|
|
||||||
mock(MeetingTranscriptChapterService.class),
|
|
||||||
mock(MeetingSummaryPromptAssembler.class),
|
|
||||||
mock(TaskSecurityContextRunner.class),
|
|
||||||
mock(MeetingExternalSummaryWebhookTrigger.class),
|
|
||||||
mock(SysParamService.class)
|
|
||||||
);
|
|
||||||
ReflectionTestUtils.setField(service, "baseMapper", mock(AiTaskMapper.class));
|
|
||||||
ReflectionTestUtils.setField(service, "androidMeetingPushService", mock(AndroidMeetingPushService.class));
|
|
||||||
return service;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue