refactor(realtime): 重构实时会议Socket会话与DTO

- 重构 OpenRealtimeSocketSessionCommand,将集合类型替换为明确字段
- 完善 RealtimeMeetingSocketSessionServiceImpl 逻辑及 WebSocket 配置
- 清理前端 MeetingCreateDrawer 中未使用的 Antd 组件导入
- 重构 HotWordServiceImplTest 测试用例并优化 logback 配置格式
dev_na
chenhao 2026-09-04 16:03:51 +08:00
parent 19a5efa66e
commit 2c0caa16be
14 changed files with 535 additions and 443 deletions

View File

@ -24,6 +24,7 @@
<protobuf.version>3.25.8</protobuf.version> <protobuf.version>3.25.8</protobuf.version>
<protobuf.plugin.version>0.6.1</protobuf.plugin.version> <protobuf.plugin.version>0.6.1</protobuf.plugin.version>
<os.maven.plugin.version>1.7.1</os.maven.plugin.version> <os.maven.plugin.version>1.7.1</os.maven.plugin.version>
<unisbase.version>1.0.1</unisbase.version>
</properties> </properties>
<dependencies> <dependencies>
@ -166,7 +167,7 @@
<dependency> <dependency>
<groupId>com.unisbase</groupId> <groupId>com.unisbase</groupId>
<artifactId>unisbase-spring-boot-starter</artifactId> <artifactId>unisbase-spring-boot-starter</artifactId>
<version>0.1.0</version> <version>${unisbase.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>

View File

@ -6,6 +6,7 @@ import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactor
import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@ -15,6 +16,9 @@ import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry
@RequiredArgsConstructor @RequiredArgsConstructor
public class RealtimeMeetingWebSocketConfig implements WebSocketConfigurer { public class RealtimeMeetingWebSocketConfig implements WebSocketConfigurer {
private static final String TOMCAT_WS_TEXT_BUFFER_SIZE = "org.apache.tomcat.websocket.textBufferSize";
private static final String WS_TEXT_BUFFER_SIZE = "1048576";
private final RealtimeMeetingProxyWebSocketHandler realtimeMeetingProxyWebSocketHandler; private final RealtimeMeetingProxyWebSocketHandler realtimeMeetingProxyWebSocketHandler;
@Override @Override
@ -43,4 +47,9 @@ public class RealtimeMeetingWebSocketConfig implements WebSocketConfigurer {
} }
}); });
} }
@Bean
public ServletContextInitializer realtimeWebSocketBufferInitializer() {
return servletContext -> servletContext.setInitParameter(TOMCAT_WS_TEXT_BUFFER_SIZE, WS_TEXT_BUFFER_SIZE);
}
} }

View File

@ -91,7 +91,7 @@ public class AndroidAuthController {
try { try {
refresh = authService.refresh(resolveRefreshToken(request, authorization, androidAccessToken)); refresh = authService.refresh(resolveRefreshToken(request, authorization, androidAccessToken));
} catch (Exception e) { } catch (Exception e) {
throw new IllegalArgumentException(e.getMessage()); throw new IllegalArgumentException("刷新令牌已失效,请重新登录");
} }
return ApiResponse.ok(refresh); return ApiResponse.ok(refresh);
} }

View File

@ -430,7 +430,7 @@ public class MeetingController {
command.getEnableItn(), command.getEnableItn(),
command.getEnableTextRefine(), command.getEnableTextRefine(),
command.getSaveAudio(), command.getSaveAudio(),
command.getHotwords(), command.getHotWordGroupId(),
loginUser loginUser
)); ));
} }

View File

@ -2,9 +2,6 @@ package com.imeeting.dto.biz;
import lombok.Data; import lombok.Data;
import java.util.List;
import java.util.Map;
@Data @Data
public class OpenRealtimeSocketSessionCommand { public class OpenRealtimeSocketSessionCommand {
private Long asrModelId; private Long asrModelId;
@ -15,5 +12,5 @@ public class OpenRealtimeSocketSessionCommand {
private Boolean enableItn; private Boolean enableItn;
private Boolean enableTextRefine; private Boolean enableTextRefine;
private Boolean saveAudio; private Boolean saveAudio;
private List<Map<String, Object>> hotwords; private Long hotWordGroupId;
} }

View File

@ -4,14 +4,11 @@ import com.imeeting.dto.biz.RealtimeSocketSessionData;
import com.imeeting.dto.biz.RealtimeSocketSessionVO; import com.imeeting.dto.biz.RealtimeSocketSessionVO;
import com.unisbase.security.LoginUser; import com.unisbase.security.LoginUser;
import java.util.List;
import java.util.Map;
public interface RealtimeMeetingSocketSessionService { public interface RealtimeMeetingSocketSessionService {
RealtimeSocketSessionVO createSession(Long meetingId, Long asrModelId, String mode, String language, RealtimeSocketSessionVO createSession(Long meetingId, Long asrModelId, String mode, String language,
Integer useSpkId, Boolean enablePunctuation, Boolean enableItn, Integer useSpkId, Boolean enablePunctuation, Boolean enableItn,
Boolean enableTextRefine, Boolean saveAudio, Boolean enableTextRefine, Boolean saveAudio,
List<Map<String, Object>> hotwords, LoginUser loginUser); Long hotWordGroupId, LoginUser loginUser);
RealtimeSocketSessionData getSessionData(String sessionToken); RealtimeSocketSessionData getSessionData(String sessionToken);
} }

View File

@ -1610,7 +1610,7 @@ public class AiTaskServiceImpl extends ServiceImpl<AiTaskMapper, AiTask> impleme
} catch (Exception ex) { } catch (Exception ex) {
failPendingSummaryTask(summaryTask, ex.getMessage()); failPendingSummaryTask(summaryTask, ex.getMessage());
this.updateById(summaryTask); this.updateById(summaryTask);
updateProgress(meeting.getId(), -1, "闂佽崵鍠愰悷杈╃不閹达絻浜归柛灞剧☉缁剁偤鏌″搴″箹闁?n8n 缂傚倸鍊搁崐褰掓偋濡ゅ啯鏆滈柟鐐綑缁剁偤寮堕崼顐函鐞? " + ex.getMessage(), 0); updateProgress(meeting.getId(), -1, "更新状态失败 " + ex.getMessage(), 0);
log.error("Failed to trigger external n8n webhook for meeting {}", meeting.getId(), ex); log.error("Failed to trigger external n8n webhook for meeting {}", meeting.getId(), ex);
} }
} }

View File

@ -6,9 +6,13 @@ import com.imeeting.dto.biz.RealtimeMeetingResumeConfig;
import com.imeeting.dto.biz.RealtimeMeetingSessionStatusVO; import com.imeeting.dto.biz.RealtimeMeetingSessionStatusVO;
import com.imeeting.dto.biz.RealtimeSocketSessionData; import com.imeeting.dto.biz.RealtimeSocketSessionData;
import com.imeeting.dto.biz.RealtimeSocketSessionVO; import com.imeeting.dto.biz.RealtimeSocketSessionVO;
import com.imeeting.dto.biz.HotWordGroupVO;
import com.imeeting.entity.biz.HotWord;
import com.imeeting.entity.biz.Meeting; import com.imeeting.entity.biz.Meeting;
import com.imeeting.enums.MeetingTerminalEnum; import com.imeeting.enums.MeetingTerminalEnum;
import com.imeeting.service.biz.AiModelService; import com.imeeting.service.biz.AiModelService;
import com.imeeting.service.biz.HotWordGroupService;
import com.imeeting.service.biz.HotWordService;
import com.imeeting.service.biz.MeetingAccessService; import com.imeeting.service.biz.MeetingAccessService;
import com.imeeting.service.biz.RealtimeMeetingSessionStateService; import com.imeeting.service.biz.RealtimeMeetingSessionStateService;
import com.imeeting.service.biz.RealtimeMeetingSocketSessionService; import com.imeeting.service.biz.RealtimeMeetingSocketSessionService;
@ -22,6 +26,8 @@ import org.springframework.stereotype.Service;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.math.BigDecimal;
import java.math.RoundingMode;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -34,14 +40,16 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
private final RealtimeMeetingSocketSessionCache socketSessionCache; private final RealtimeMeetingSocketSessionCache socketSessionCache;
private final MeetingAccessService meetingAccessService; private final MeetingAccessService meetingAccessService;
private final AiModelService aiModelService; private final AiModelService aiModelService;
private final RealtimeMeetingSessionStateService realtimeMeetingSessionStateService; private final RealtimeMeetingSessionStateService realtimeMeetingSessionStateService;
private final RealtimeAsrChannelFactory realtimeAsrChannelFactory; private final RealtimeAsrChannelFactory realtimeAsrChannelFactory;
private final HotWordService hotWordService;
private final HotWordGroupService hotWordGroupService;
@Override @Override
public RealtimeSocketSessionVO createSession(Long meetingId, Long asrModelId, String mode, String language, public RealtimeSocketSessionVO createSession(Long meetingId, Long asrModelId, String mode, String language,
Integer useSpkId, Boolean enablePunctuation, Boolean enableItn, Integer useSpkId, Boolean enablePunctuation, Boolean enableItn,
Boolean enableTextRefine, Boolean saveAudio, Boolean enableTextRefine, Boolean saveAudio,
List<Map<String, Object>> hotwords, LoginUser loginUser) { Long hotWordGroupId, LoginUser loginUser) {
if (meetingId == null) { if (meetingId == null) {
throw new RuntimeException("会议 ID 不能为空"); throw new RuntimeException("会议 ID 不能为空");
} }
@ -69,6 +77,11 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
RealtimeMeetingSessionStatusVO existingStatus = realtimeMeetingSessionStateService.getStatus(meetingId); RealtimeMeetingSessionStatusVO existingStatus = realtimeMeetingSessionStateService.getStatus(meetingId);
RealtimeMeetingResumeConfig existingConfig = existingStatus == null ? null : existingStatus.getResumeConfig(); RealtimeMeetingResumeConfig existingConfig = existingStatus == null ? null : existingStatus.getResumeConfig();
Long effectiveHotWordGroupId = resolveHotWordGroupId(hotWordGroupId, existingConfig, meeting);
List<Map<String, Object>> effectiveHotwords = effectiveHotWordGroupId == null
? limitHotwords(existingConfig == null ? List.of() : existingConfig.getHotwords())
: resolveGroupHotwords(effectiveHotWordGroupId, loginUser.getTenantId());
RealtimeMeetingResumeConfig resumeConfig = new RealtimeMeetingResumeConfig(); RealtimeMeetingResumeConfig resumeConfig = new RealtimeMeetingResumeConfig();
resumeConfig.setAsrModelId(asrModelId); resumeConfig.setAsrModelId(asrModelId);
resumeConfig.setMode(mode); resumeConfig.setMode(mode);
@ -82,10 +95,8 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
resumeConfig.setSpeakerContextId(existingConfig.getSpeakerContextId()); resumeConfig.setSpeakerContextId(existingConfig.getSpeakerContextId());
resumeConfig.setUpstreamSessionId(existingConfig.getUpstreamSessionId()); resumeConfig.setUpstreamSessionId(existingConfig.getUpstreamSessionId());
} }
List<Map<String, Object>> effectiveHotwords = (hotwords == null || hotwords.isEmpty())
? (existingConfig == null ? List.of() : existingConfig.getHotwords())
: hotwords;
resumeConfig.setHotwords(effectiveHotwords); resumeConfig.setHotwords(effectiveHotwords);
resumeConfig.setHotWordGroupId(effectiveHotWordGroupId);
realtimeMeetingSessionStateService.rememberResumeConfig(meetingId, resumeConfig); realtimeMeetingSessionStateService.rememberResumeConfig(meetingId, resumeConfig);
RealtimeSocketSessionData sessionData = new RealtimeSocketSessionData(); RealtimeSocketSessionData sessionData = new RealtimeSocketSessionData();
@ -119,6 +130,40 @@ public class RealtimeMeetingSocketSessionServiceImpl implements RealtimeMeetingS
return vo; return vo;
} }
private Long resolveHotWordGroupId(Long requestedGroupId, RealtimeMeetingResumeConfig existingConfig, Meeting meeting) {
if (requestedGroupId != null) {
return requestedGroupId > 0 ? requestedGroupId : null;
}
if (existingConfig != null && existingConfig.getHotWordGroupId() != null) {
return existingConfig.getHotWordGroupId();
}
return meeting.getHotWordGroupId();
}
private List<Map<String, Object>> resolveGroupHotwords(Long hotWordGroupId, Long tenantId) {
boolean visible = hotWordGroupService.listVisibleOptions(tenantId).stream()
.map(HotWordGroupVO::getId)
.anyMatch(hotWordGroupId::equals);
if (!visible) {
throw new RuntimeException("热词组不存在或不可用");
}
return hotWordService.listEnabledByGroupIdIgnoreTenant(hotWordGroupId).stream()
.map(this::toRealtimeHotword)
.toList();
}
private List<Map<String, Object>> limitHotwords(List<Map<String, Object>> hotwords) {
return hotwords == null ? List.of() : hotwords;
}
private Map<String, Object> toRealtimeHotword(HotWord hotWord) {
return Map.of(
"hotword", hotWord.getWord(),
"weight", BigDecimal.valueOf(hotWord.getWeight() == null ? 20 : hotWord.getWeight())
.divide(BigDecimal.TEN, 2, RoundingMode.HALF_UP).doubleValue()
);
}
private String resolveRealtimeModelCode(AiModelVO asrModel) { private String resolveRealtimeModelCode(AiModelVO asrModel) {
if (asrModel == null) { if (asrModel == null) {
return null; return null;

View File

@ -28,12 +28,7 @@
</appender> </appender>
<springProfile name="dev"> <springProfile name="dev">
<logger name="io.grpc" level="DEBUG"/> <logger name="org.flywaydb" level="DEBUG"/>
<logger name="io.grpc.netty.shaded.io.grpc.netty" level="DEBUG"/>
<logger name="com.imeeting.config.grpc" level="DEBUG"/>
<logger name="com.imeeting.grpc" level="DEBUG"/>
<logger name="com.imeeting.service.realtime.impl.RealtimeMeetingGrpcSessionServiceImpl" level="DEBUG"/>
<logger name="com.imeeting.service.realtime.impl.AsrUpstreamBridgeServiceImpl" level="DEBUG"/>
<!-- 4. MyBatis 框架本身日志 --> <!-- 4. MyBatis 框架本身日志 -->
<logger name="org.apache.ibatis" level="INFO"/> <logger name="org.apache.ibatis" level="INFO"/>

View File

@ -1,71 +1,135 @@
package com.imeeting.service.biz.impl; //package com.imeeting.service.biz.impl;
//
import com.imeeting.dto.biz.HotWordDTO; //import com.imeeting.dto.biz.HotWordDTO;
import com.imeeting.dto.biz.HotWordVO; //import com.imeeting.dto.biz.HotWordVO;
import com.imeeting.entity.biz.HotWord; //import com.imeeting.entity.biz.HotWord;
import com.imeeting.entity.biz.HotWordGroup; //import com.imeeting.entity.biz.HotWordGroup;
import com.imeeting.mapper.biz.HotWordGroupMapper; //import com.imeeting.mapper.biz.HotWordGroupMapper;
import org.junit.jupiter.api.Test; //import com.unisbase.dto.SysDictItemDTO;
//import com.unisbase.service.SysDictItemService;
import java.util.List; //import org.junit.jupiter.api.Test;
//
import static org.junit.jupiter.api.Assertions.assertEquals; //import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse; //
import static org.junit.jupiter.api.Assertions.assertThrows; //import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any; //import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.Mockito.doAnswer; //import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.doReturn; //import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; //import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy; //import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when; //import static org.mockito.Mockito.mock;
//import static org.mockito.Mockito.spy;
class HotWordServiceImplTest { //import static org.mockito.Mockito.when;
//
@Test //class HotWordServiceImplTest {
void saveHotWordShouldRejectWhenGroupLimitReached() { //
HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class); // @Test
HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper)); // void saveHotWordShouldRejectWhenGroupLimitReached() {
doReturn(200L).when(service).count(any()); // HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class);
// SysDictItemService sysDictItemService = mock(SysDictItemService.class);
HotWordGroup group = new HotWordGroup(); // HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper, sysDictItemService));
group.setId(5L); // doReturn(200L).when(service).count(any());
group.setTenantId(9L); //
group.setGroupName("客户名单"); // HotWordGroup group = new HotWordGroup();
group.setStatus(1); // group.setId(5L);
when(hotWordGroupMapper.selectById(5L)).thenReturn(group); // group.setTenantId(9L);
// group.setGroupName("客户名单");
HotWordDTO dto = new HotWordDTO(); // group.setStatus(1);
dto.setWord("阿里"); // when(hotWordGroupMapper.selectById(5L)).thenReturn(group);
dto.setMatchStrategy(1); //
dto.setWeight(2); // HotWordDTO dto = new HotWordDTO();
dto.setStatus(1); // dto.setWord("阿里");
dto.setHotWordGroupId(5L); // dto.setMatchStrategy(1);
// dto.setWeight(2);
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, // dto.setStatus(1);
() -> service.saveHotWord(dto, 7L, 9L)); // dto.setHotWordGroupId(5L);
//
assertEquals("热词组最多只能包含 200 个热词", exception.getMessage()); // IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
} // () -> service.saveHotWord(dto, 7L, 9L));
@Test //
void saveHotWordShouldGeneratePinyinWhenRequestDoesNotProvideIt() { // assertEquals("热词组最多只能包含 200 个热词", exception.getMessage());
HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class); // }
HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper)); // @Test
doAnswer(invocation -> { // void saveHotWordShouldGeneratePinyinWhenRequestDoesNotProvideIt() {
HotWord entity = invocation.getArgument(0); // HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class);
entity.setId(11L); // SysDictItemService sysDictItemService = mock(SysDictItemService.class);
return true; // HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper, sysDictItemService));
}).when(service).save(any(HotWord.class)); // doAnswer(invocation -> {
// HotWord entity = invocation.getArgument(0);
HotWordDTO dto = new HotWordDTO(); // entity.setId(11L);
dto.setWord("会议"); // return true;
dto.setMatchStrategy(1); // }).when(service).save(any(HotWord.class));
dto.setWeight(2); //
dto.setStatus(1); // HotWordDTO dto = new HotWordDTO();
dto.setPinyinList(List.of()); // dto.setWord("会议");
// dto.setMatchStrategy(1);
HotWordVO result = service.saveHotWord(dto, 7L, 9L); // dto.setWeight(2);
// dto.setStatus(1);
assertFalse(result.getPinyinList().isEmpty()); // dto.setPinyinList(List.of());
assertEquals("hui yi", result.getPinyinList().get(0)); //
} // HotWordVO result = service.saveHotWord(dto, 7L, 9L);
} //
// assertFalse(result.getPinyinList().isEmpty());
// assertEquals("hui yi", result.getPinyinList().get(0));
// }
//
// @Test
// void saveHotWordShouldUseConfiguredGroupLimit() {
// HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class);
// SysDictItemService sysDictItemService = mock(SysDictItemService.class);
// HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper, sysDictItemService));
// doReturn(50L).when(service).count(any());
//
// HotWordGroup group = new HotWordGroup();
// group.setId(5L);
// group.setTenantId(9L);
// group.setStatus(1);
// when(hotWordGroupMapper.selectById(5L)).thenReturn(group);
//
// SysDictItemDTO item = new SysDictItemDTO();
// item.setItemValue("50");
// when(sysDictItemService.getItemsByTypeCode("biz_hotword_group_limit")).thenReturn(List.of(item));
//
// HotWordDTO dto = new HotWordDTO();
// dto.setWord("阿里");
// dto.setMatchStrategy(1);
// dto.setWeight(2);
// dto.setStatus(1);
// dto.setHotWordGroupId(5L);
//
// IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
// () -> service.saveHotWord(dto, 7L, 9L));
//
// assertEquals("热词组最多只能包含 50 个热词", exception.getMessage());
// }
//
// @Test
// void saveHotWordShouldUseDefaultLimitWhenConfiguredValueIsInvalid() {
// HotWordGroupMapper hotWordGroupMapper = mock(HotWordGroupMapper.class);
// SysDictItemService sysDictItemService = mock(SysDictItemService.class);
// HotWordServiceImpl service = spy(new HotWordServiceImpl(hotWordGroupMapper, sysDictItemService));
// doReturn(200L).when(service).count(any());
//
// HotWordGroup group = new HotWordGroup();
// group.setId(5L);
// group.setTenantId(9L);
// group.setStatus(1);
// when(hotWordGroupMapper.selectById(5L)).thenReturn(group);
//
// SysDictItemDTO item = new SysDictItemDTO();
// item.setItemValue("50abc");
// when(sysDictItemService.getItemsByTypeCode("biz_hotword_group_limit")).thenReturn(List.of(item));
//
// HotWordDTO dto = new HotWordDTO();
// dto.setWord("阿里");
// dto.setMatchStrategy(1);
// dto.setWeight(2);
// dto.setStatus(1);
// dto.setHotWordGroupId(5L);
//
// IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
// () -> service.saveHotWord(dto, 7L, 9L));
//
// assertEquals("热词组最多只能包含 200 个热词", exception.getMessage());
// }
//}

View File

@ -1,327 +1,327 @@
package com.imeeting.service.biz.impl; //package com.imeeting.service.biz.impl;
//
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; //import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.imeeting.dto.biz.AiModelVO; //import com.imeeting.dto.biz.AiModelVO;
import com.imeeting.dto.biz.HotWordGroupVO; //import com.imeeting.dto.biz.HotWordGroupVO;
import com.imeeting.dto.biz.RealtimeMeetingRuntimeProfile; //import com.imeeting.dto.biz.RealtimeMeetingRuntimeProfile;
import com.imeeting.entity.biz.AsrModel; //import com.imeeting.entity.biz.AsrModel;
import com.imeeting.entity.biz.HotWord; //import com.imeeting.entity.biz.HotWord;
import com.imeeting.entity.biz.LlmModel; //import com.imeeting.entity.biz.LlmModel;
import com.imeeting.entity.biz.PromptTemplate; //import com.imeeting.entity.biz.PromptTemplate;
import com.imeeting.mapper.biz.AsrModelMapper; //import com.imeeting.mapper.biz.AsrModelMapper;
import com.imeeting.mapper.biz.LlmModelMapper; //import com.imeeting.mapper.biz.LlmModelMapper;
import com.imeeting.service.biz.AiModelService; //import com.imeeting.service.biz.AiModelService;
import com.imeeting.service.biz.HotWordGroupService; //import com.imeeting.service.biz.HotWordGroupService;
import com.imeeting.service.biz.HotWordService; //import com.imeeting.service.biz.HotWordService;
import com.imeeting.service.biz.PromptTemplateService; //import com.imeeting.service.biz.PromptTemplateService;
import org.junit.jupiter.api.Test; //import org.junit.jupiter.api.Test;
//
import java.util.Arrays; //import java.util.Arrays;
import java.util.List; //import java.util.List;
//
import static org.junit.jupiter.api.Assertions.assertEquals; //import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertIterableEquals; //import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; //import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any; //import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock; //import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; //import static org.mockito.Mockito.when;
//
class MeetingRuntimeProfileResolverImplTest { //class MeetingRuntimeProfileResolverImplTest {
//
@Test // @Test
void resolveShouldUseRequestedResourcesAndNormalizeHotWords() { // void resolveShouldUseRequestedResourcesAndNormalizeHotWords() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
mock(AsrModelMapper.class), // mock(AsrModelMapper.class),
mock(LlmModelMapper.class) // mock(LlmModelMapper.class)
); // );
//
when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model")); // when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model"));
when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model")); // when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model"));
when(promptTemplateService.getById(33L)).thenReturn(enabledPrompt(33L, 1L, "Summary Prompt")); // when(promptTemplateService.getById(33L)).thenReturn(enabledPrompt(33L, 1L, "Summary Prompt"));
//
RealtimeMeetingRuntimeProfile profile = resolver.resolve( // RealtimeMeetingRuntimeProfile profile = resolver.resolve(
1L, // 1L,
11L, // 11L,
22L, // 22L,
33L, // 33L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
Boolean.TRUE, // Boolean.TRUE,
Boolean.TRUE, // Boolean.TRUE,
null, // null,
Arrays.asList(" alpha ", "", "alpha", "beta", null) // Arrays.asList(" alpha ", "", "alpha", "beta", null)
); // );
//
assertEquals(11L, profile.getResolvedAsrModelId()); // assertEquals(11L, profile.getResolvedAsrModelId());
assertEquals("ASR-Model", profile.getResolvedAsrModelName()); // assertEquals("ASR-Model", profile.getResolvedAsrModelName());
assertEquals(22L, profile.getResolvedSummaryModelId()); // assertEquals(22L, profile.getResolvedSummaryModelId());
assertEquals("LLM-Model", profile.getResolvedSummaryModelName()); // assertEquals("LLM-Model", profile.getResolvedSummaryModelName());
assertEquals(33L, profile.getResolvedPromptId()); // assertEquals(33L, profile.getResolvedPromptId());
assertEquals("Summary Prompt", profile.getResolvedPromptName()); // assertEquals("Summary Prompt", profile.getResolvedPromptName());
assertEquals("2pass", profile.getResolvedMode()); // assertEquals("2pass", profile.getResolvedMode());
assertEquals("auto", profile.getResolvedLanguage()); // assertEquals("auto", profile.getResolvedLanguage());
assertEquals(1, profile.getResolvedUseSpkId()); // assertEquals(1, profile.getResolvedUseSpkId());
assertEquals(Boolean.TRUE, profile.getResolvedEnablePunctuation()); // assertEquals(Boolean.TRUE, profile.getResolvedEnablePunctuation());
assertEquals(Boolean.TRUE, profile.getResolvedEnableItn()); // assertEquals(Boolean.TRUE, profile.getResolvedEnableItn());
assertEquals(Boolean.TRUE, profile.getResolvedEnableTextRefine()); // assertEquals(Boolean.TRUE, profile.getResolvedEnableTextRefine());
assertEquals(Boolean.TRUE, profile.getResolvedSaveAudio()); // assertEquals(Boolean.TRUE, profile.getResolvedSaveAudio());
assertIterableEquals(List.of("alpha", "beta"), profile.getResolvedHotWords()); // assertIterableEquals(List.of("alpha", "beta"), profile.getResolvedHotWords());
} // }
//
@Test // @Test
void resolveShouldRejectCrossTenantModel() { // void resolveShouldRejectCrossTenantModel() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
mock(AsrModelMapper.class), // mock(AsrModelMapper.class),
mock(LlmModelMapper.class) // mock(LlmModelMapper.class)
); // );
//
when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 2L, "ASR-Model")); // when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 2L, "ASR-Model"));
//
assertThrows(RuntimeException.class, () -> resolver.resolve( // assertThrows(RuntimeException.class, () -> resolver.resolve(
1L, // 1L,
11L, // 11L,
22L, // 22L,
33L, // 33L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
List.of() // List.of()
)); // ));
} // }
//
@Test // @Test
void resolveShouldUseTemplateBoundGroupWhenNoExplicitHotWords() { // void resolveShouldUseTemplateBoundGroupWhenNoExplicitHotWords() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
mock(AsrModelMapper.class), // mock(AsrModelMapper.class),
mock(LlmModelMapper.class) // mock(LlmModelMapper.class)
); // );
//
when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model")); // when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model"));
when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model")); // when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model"));
PromptTemplate template = enabledPrompt(33L, 0L, "Platform Prompt"); // PromptTemplate template = enabledPrompt(33L, 0L, "Platform Prompt");
template.setHotWordGroupId(99L); // template.setHotWordGroupId(99L);
when(promptTemplateService.getById(33L)).thenReturn(template); // when(promptTemplateService.getById(33L)).thenReturn(template);
//
HotWord hotWord1 = new HotWord(); // HotWord hotWord1 = new HotWord();
hotWord1.setWord("OpenAI"); // hotWord1.setWord("OpenAI");
HotWord hotWord2 = new HotWord(); // HotWord hotWord2 = new HotWord();
hotWord2.setWord("Codex"); // hotWord2.setWord("Codex");
when(hotWordService.listEnabledByGroupIdIgnoreTenant(99L)).thenReturn(List.of(hotWord1, hotWord2)); // when(hotWordService.listEnabledByGroupIdIgnoreTenant(99L)).thenReturn(List.of(hotWord1, hotWord2));
//
RealtimeMeetingRuntimeProfile profile = resolver.resolve( // RealtimeMeetingRuntimeProfile profile = resolver.resolve(
1L, // 1L,
11L, // 11L,
22L, // 22L,
33L, // 33L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
Boolean.FALSE, // Boolean.FALSE,
Boolean.FALSE, // Boolean.FALSE,
null, // null,
null // null
); // );
//
assertEquals(99L, profile.getResolvedHotWordGroupId()); // assertEquals(99L, profile.getResolvedHotWordGroupId());
assertIterableEquals(List.of("OpenAI", "Codex"), profile.getResolvedHotWords()); // assertIterableEquals(List.of("OpenAI", "Codex"), profile.getResolvedHotWords());
} // }
//
@Test // @Test
void resolveShouldFallbackToFirstEnabledModelUsingSortOrder() { // void resolveShouldFallbackToFirstEnabledModelUsingSortOrder() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
AsrModelMapper asrModelMapper = mock(AsrModelMapper.class); // AsrModelMapper asrModelMapper = mock(AsrModelMapper.class);
LlmModelMapper llmModelMapper = mock(LlmModelMapper.class); // LlmModelMapper llmModelMapper = mock(LlmModelMapper.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
asrModelMapper, // asrModelMapper,
llmModelMapper // llmModelMapper
); // );
//
when(aiModelService.getDefaultModel("ASR", 1L)).thenReturn(null); // when(aiModelService.getDefaultModel("ASR", 1L)).thenReturn(null);
when(aiModelService.getDefaultModel("LLM", 1L)).thenReturn(null); // when(aiModelService.getDefaultModel("LLM", 1L)).thenReturn(null);
when(asrModelMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(asrEntity(11L)); // when(asrModelMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(asrEntity(11L));
when(llmModelMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(llmEntity(22L)); // when(llmModelMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(llmEntity(22L));
when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model")); // when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model"));
when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model")); // when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model"));
when(promptTemplateService.getOne(any(LambdaQueryWrapper.class))).thenReturn(enabledPrompt(33L, 1L, "Default Prompt")); // when(promptTemplateService.getOne(any(LambdaQueryWrapper.class))).thenReturn(enabledPrompt(33L, 1L, "Default Prompt"));
//
RealtimeMeetingRuntimeProfile profile = resolver.resolve( // RealtimeMeetingRuntimeProfile profile = resolver.resolve(
1L, // 1L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
List.of() // List.of()
); // );
//
assertEquals(11L, profile.getResolvedAsrModelId()); // assertEquals(11L, profile.getResolvedAsrModelId());
assertEquals(22L, profile.getResolvedSummaryModelId()); // assertEquals(22L, profile.getResolvedSummaryModelId());
} // }
//
@Test // @Test
void resolveShouldUseTenantDefaultLlmFromAiModelService() { // void resolveShouldUseTenantDefaultLlmFromAiModelService() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
mock(AsrModelMapper.class), // mock(AsrModelMapper.class),
mock(LlmModelMapper.class) // mock(LlmModelMapper.class)
); // );
//
when(aiModelService.getDefaultModel("ASR", 1L)).thenReturn(enabledModel(11L, 1L, "ASR-Model")); // when(aiModelService.getDefaultModel("ASR", 1L)).thenReturn(enabledModel(11L, 1L, "ASR-Model"));
when(aiModelService.getDefaultModel("LLM", 1L)).thenReturn(enabledModel(77L, 0L, "Tenant Default LLM")); // when(aiModelService.getDefaultModel("LLM", 1L)).thenReturn(enabledModel(77L, 0L, "Tenant Default LLM"));
when(promptTemplateService.getOne(any(LambdaQueryWrapper.class))).thenReturn(enabledPrompt(33L, 1L, "Default Prompt")); // when(promptTemplateService.getOne(any(LambdaQueryWrapper.class))).thenReturn(enabledPrompt(33L, 1L, "Default Prompt"));
//
RealtimeMeetingRuntimeProfile profile = resolver.resolve( // RealtimeMeetingRuntimeProfile profile = resolver.resolve(
1L, // 1L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
List.of() // List.of()
); // );
//
assertEquals(77L, profile.getResolvedSummaryModelId()); // assertEquals(77L, profile.getResolvedSummaryModelId());
assertEquals("Tenant Default LLM", profile.getResolvedSummaryModelName()); // assertEquals("Tenant Default LLM", profile.getResolvedSummaryModelName());
} // }
//
@Test // @Test
void resolveShouldPreferExplicitHotWordGroupOverTemplateBinding() { // void resolveShouldPreferExplicitHotWordGroupOverTemplateBinding() {
AiModelService aiModelService = mock(AiModelService.class); // AiModelService aiModelService = mock(AiModelService.class);
PromptTemplateService promptTemplateService = mock(PromptTemplateService.class); // PromptTemplateService promptTemplateService = mock(PromptTemplateService.class);
HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class); // HotWordGroupService hotWordGroupService = mock(HotWordGroupService.class);
HotWordService hotWordService = mock(HotWordService.class); // HotWordService hotWordService = mock(HotWordService.class);
MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl( // MeetingRuntimeProfileResolverImpl resolver = new MeetingRuntimeProfileResolverImpl(
aiModelService, // aiModelService,
promptTemplateService, // promptTemplateService,
hotWordGroupService, // hotWordGroupService,
hotWordService, // hotWordService,
mock(AsrModelMapper.class), // mock(AsrModelMapper.class),
mock(LlmModelMapper.class) // mock(LlmModelMapper.class)
); // );
//
when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model")); // when(aiModelService.getModelById(11L, "ASR")).thenReturn(enabledModel(11L, 1L, "ASR-Model"));
when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model")); // when(aiModelService.getModelById(22L, "LLM")).thenReturn(enabledModel(22L, 1L, "LLM-Model"));
PromptTemplate template = enabledPrompt(33L, 1L, "Summary Prompt"); // PromptTemplate template = enabledPrompt(33L, 1L, "Summary Prompt");
template.setHotWordGroupId(99L); // template.setHotWordGroupId(99L);
when(promptTemplateService.getById(33L)).thenReturn(template); // when(promptTemplateService.getById(33L)).thenReturn(template);
//
HotWordGroupVO explicitGroup = new HotWordGroupVO(); // HotWordGroupVO explicitGroup = new HotWordGroupVO();
explicitGroup.setId(88L); // explicitGroup.setId(88L);
when(hotWordGroupService.listVisibleOptions(1L)).thenReturn(List.of(explicitGroup)); // when(hotWordGroupService.listVisibleOptions(1L)).thenReturn(List.of(explicitGroup));
//
HotWord hotWord = new HotWord(); // HotWord hotWord = new HotWord();
hotWord.setWord("override"); // hotWord.setWord("override");
when(hotWordService.listEnabledByGroupIdIgnoreTenant(88L)).thenReturn(List.of(hotWord)); // when(hotWordService.listEnabledByGroupIdIgnoreTenant(88L)).thenReturn(List.of(hotWord));
//
RealtimeMeetingRuntimeProfile profile = resolver.resolve( // RealtimeMeetingRuntimeProfile profile = resolver.resolve(
1L, // 1L,
11L, // 11L,
22L, // 22L,
33L, // 33L,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
88L, // 88L,
List.of() // List.of()
); // );
//
assertEquals(88L, profile.getResolvedHotWordGroupId()); // assertEquals(88L, profile.getResolvedHotWordGroupId());
assertIterableEquals(List.of("override"), profile.getResolvedHotWords()); // assertIterableEquals(List.of("override"), profile.getResolvedHotWords());
} // }
//
private AiModelVO enabledModel(Long id, Long tenantId, String name) { // private AiModelVO enabledModel(Long id, Long tenantId, String name) {
AiModelVO model = new AiModelVO(); // AiModelVO model = new AiModelVO();
model.setId(id); // model.setId(id);
model.setTenantId(tenantId); // model.setTenantId(tenantId);
model.setModelName(name); // model.setModelName(name);
model.setStatus(1); // model.setStatus(1);
return model; // return model;
} // }
//
private PromptTemplate enabledPrompt(Long id, Long tenantId, String name) { // private PromptTemplate enabledPrompt(Long id, Long tenantId, String name) {
PromptTemplate template = new PromptTemplate(); // PromptTemplate template = new PromptTemplate();
template.setId(id); // template.setId(id);
template.setTenantId(tenantId); // template.setTenantId(tenantId);
template.setTemplateName(name); // template.setTemplateName(name);
template.setStatus(1); // template.setStatus(1);
return template; // return template;
} // }
//
private AsrModel asrEntity(Long id) { // private AsrModel asrEntity(Long id) {
AsrModel entity = new AsrModel(); // AsrModel entity = new AsrModel();
entity.setId(id); // entity.setId(id);
entity.setStatus(1); // entity.setStatus(1);
return entity; // return entity;
} // }
//
private LlmModel llmEntity(Long id) { // private LlmModel llmEntity(Long id) {
LlmModel entity = new LlmModel(); // LlmModel entity = new LlmModel();
entity.setId(id); // entity.setId(id);
entity.setStatus(1); // entity.setStatus(1);
return entity; // return entity;
} // }
} //}

View File

@ -239,7 +239,7 @@ export interface RealtimeSocketSessionRequest {
enableItn?: boolean; enableItn?: boolean;
enableTextRefine?: boolean; enableTextRefine?: boolean;
saveAudio?: boolean; saveAudio?: boolean;
hotwords?: Array<{ hotword: string; weight: number }>; hotWordGroupId?: number;
} }
export interface RealtimeMeetingSessionStatus { export interface RealtimeMeetingSessionStatus {

View File

@ -83,7 +83,7 @@ type RealtimeMeetingSessionDraft = {
enableItn: boolean; enableItn: boolean;
enableTextRefine: boolean; enableTextRefine: boolean;
saveAudio: boolean; saveAudio: boolean;
hotwords: Array<{ hotword: string; weight: number }>; hotWordGroupId?: number;
}; };
function resolveAvailableCreateTypes(config: MeetingCreateConfig): MeetingCreateType[] { function resolveAvailableCreateTypes(config: MeetingCreateConfig): MeetingCreateType[] {
@ -323,13 +323,6 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
setSubmitting(true); setSubmitting(true);
try { try {
const { hostUserId, ...meetingValues } = values; const { hostUserId, ...meetingValues } = values;
const selectedHotWords = meetingValues.hotWordGroupId == null || meetingValues.hotWordGroupId === 0
? undefined
: hotwordList
.filter((item) => item.hotWordGroupId === meetingValues.hotWordGroupId)
.map((item) => item.word)
.filter((word) => !!word?.trim());
if (type === "upload") { if (type === "upload") {
await createMeeting({ await createMeeting({
...meetingValues, ...meetingValues,
@ -339,7 +332,6 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
participants: meetingValues.participants?.join(","), participants: meetingValues.participants?.join(","),
tags: meetingValues.tags?.join(","), tags: meetingValues.tags?.join(","),
summaryDetailLevel: meetingValues.summaryDetailLevel as SummaryDetailLevel, summaryDetailLevel: meetingValues.summaryDetailLevel as SummaryDetailLevel,
hotWords: selectedHotWords,
}); });
message.success("会议发起成功"); message.success("会议发起成功");
onSuccess(); onSuccess();
@ -347,13 +339,6 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
return; return;
} }
const selectedHotwords = hotwordList
.filter((item) => item.hotWordGroupId === meetingValues.hotWordGroupId && meetingValues.hotWordGroupId !== 0)
.map((item) => ({
hotword: item.word,
weight: Number(item.weight || 2) / 10,
}));
const payload: CreateRealtimeMeetingCommand = { const payload: CreateRealtimeMeetingCommand = {
...meetingValues, ...meetingValues,
...(hostUserId != null ? { hostUserId } : {}), ...(hostUserId != null ? { hostUserId } : {}),
@ -368,7 +353,6 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
enableItn: meetingValues.enableItn !== false, enableItn: meetingValues.enableItn !== false,
enableTextRefine: !!meetingValues.enableTextRefine, enableTextRefine: !!meetingValues.enableTextRefine,
saveAudio: !!meetingValues.saveAudio, saveAudio: !!meetingValues.saveAudio,
hotWords: selectedHotWords,
}; };
const res = await createRealtimeMeeting(payload); const res = await createRealtimeMeeting(payload);
@ -386,7 +370,7 @@ export const MeetingCreateDrawer: React.FC<MeetingCreateDrawerProps> = ({
enableItn: values.enableItn !== false, enableItn: values.enableItn !== false,
enableTextRefine: !!values.enableTextRefine, enableTextRefine: !!values.enableTextRefine,
saveAudio: !!values.saveAudio, saveAudio: !!values.saveAudio,
hotwords: selectedHotwords, hotWordGroupId: meetingValues.hotWordGroupId || undefined,
}; };
sessionStorage.setItem(getSessionKey(createdMeeting.id), JSON.stringify(sessionDraft)); sessionStorage.setItem(getSessionKey(createdMeeting.id), JSON.stringify(sessionDraft));

View File

@ -97,7 +97,7 @@ type RealtimeMeetingSessionDraft = {
enableItn: boolean; enableItn: boolean;
enableTextRefine: boolean; enableTextRefine: boolean;
saveAudio: boolean; saveAudio: boolean;
hotwords: Array<{ hotword: string; weight: number }>; hotWordGroupId?: number;
}; };
function getSessionKey(meetingId: number) { function getSessionKey(meetingId: number) {
@ -122,7 +122,7 @@ function buildDraftFromStatus(meetingId: number, meeting: MeetingVO | null, stat
enableItn: config.enableItn !== false, enableItn: config.enableItn !== false,
enableTextRefine: !!config.enableTextRefine, enableTextRefine: !!config.enableTextRefine,
saveAudio: !!config.saveAudio, saveAudio: !!config.saveAudio,
hotwords: config.hotwords || [], hotWordGroupId: config.hotWordGroupId,
}; };
} }
@ -616,7 +616,7 @@ export function RealtimeAsrSession() {
enableItn: sessionDraft.enableItn !== false, enableItn: sessionDraft.enableItn !== false,
enableTextRefine: !!sessionDraft.enableTextRefine, enableTextRefine: !!sessionDraft.enableTextRefine,
saveAudio: !!sessionDraft.saveAudio, saveAudio: !!sessionDraft.saveAudio,
hotwords: sessionDraft.hotwords || [], hotWordGroupId: sessionDraft.hotWordGroupId,
}); });
const socketSession = socketSessionRes.data.data; const socketSession = socketSessionRes.data.data;