refactor(meeting): 优化会议模块代码结构与导入语句

- 整理并优化后端会议相关服务、控制器及DTO的import语句
- 补充AndroidMeetingConfigVo等类的Swagger注解及必要字段
- 调整前端API与类型定义文件的代码格式
- 提升代码可读性与规范性,不涉及业务逻辑变更
dev_na
chenhao 2026-07-02 17:43:14 +08:00
parent 17dbeefdf8
commit 91ab0eb7f0
15 changed files with 144 additions and 58 deletions

View File

@ -111,6 +111,7 @@ public class AndroidMeetingController {
private final SysTenantMapper sysTenantMapper; private final SysTenantMapper sysTenantMapper;
private final SysUserMapper sysUserMapper; private final SysUserMapper sysUserMapper;
private final AiModelService aiModelService; private final AiModelService aiModelService;
private final HotWordGroupService hotWordGroupService;
private final SysDictItemService dictItemService; private final SysDictItemService dictItemService;
private final SysParamService paramService; private final SysParamService paramService;
private final MeetingProgressService meetingProgressService; private final MeetingProgressService meetingProgressService;
@ -129,6 +130,7 @@ public class AndroidMeetingController {
SysTenantMapper sysTenantMapper, SysTenantMapper sysTenantMapper,
SysUserMapper sysUserMapper, SysUserMapper sysUserMapper,
AiModelService aiModelService, AiModelService aiModelService,
HotWordGroupService hotWordGroupService,
SysDictItemService dictItemService, SysDictItemService dictItemService,
SysParamService paramService, SysParamService paramService,
MeetingProgressService meetingProgressService, MeetingProgressService meetingProgressService,
@ -147,6 +149,7 @@ public class AndroidMeetingController {
this.sysUserMapper = sysUserMapper; this.sysUserMapper = sysUserMapper;
this.meetingProgressService = meetingProgressService; this.meetingProgressService = meetingProgressService;
this.aiModelService = aiModelService; this.aiModelService = aiModelService;
this.hotWordGroupService = hotWordGroupService;
this.paramService = paramService; this.paramService = paramService;
this.dictItemService = dictItemService; this.dictItemService = dictItemService;
this.meetingUnifiedStatusService = meetingUnifiedStatusService; this.meetingUnifiedStatusService = meetingUnifiedStatusService;
@ -460,6 +463,7 @@ public class AndroidMeetingController {
.filter(item -> Integer.valueOf(1).equals(item.getStatus())) .filter(item -> Integer.valueOf(1).equals(item.getStatus()))
.toList(); .toList();
resultVo.setModelsList(enabledModels); resultVo.setModelsList(enabledModels);
resultVo.setHotWordGroupList(hotWordGroupService.listVisibleOptions(tenantId));
resultVo.setSummaryDegreeOfDetail(dictItemService.getItemsByTypeCode("summary_degree_detail")); resultVo.setSummaryDegreeOfDetail(dictItemService.getItemsByTypeCode("summary_degree_detail"));
resultVo.setMaxMeetingDuration(Integer.valueOf(paramService.getParamValue(SysParamKeys.MEETING_MAX_MEETING_DURATION, "30"))); resultVo.setMaxMeetingDuration(Integer.valueOf(paramService.getParamValue(SysParamKeys.MEETING_MAX_MEETING_DURATION, "30")));
resultVo.setMinMeetingDuration(Integer.valueOf(paramService.getParamValue(SysParamKeys.MEETING_MIN_MEETING_DURATION, "10"))); resultVo.setMinMeetingDuration(Integer.valueOf(paramService.getParamValue(SysParamKeys.MEETING_MIN_MEETING_DURATION, "10")));

View File

@ -87,7 +87,7 @@ public class AndroidMeetingRealtimeController {
command == null ? null : command.getEnableItn(), command == null ? null : command.getEnableItn(),
command == null ? null : command.getEnableTextRefine(), command == null ? null : command.getEnableTextRefine(),
command == null ? null : command.getSaveAudio(), command == null ? null : command.getSaveAudio(),
null, command == null ? null : command.getHotWordGroupId(),
command == null ? null : command.getHotWords() command == null ? null : command.getHotWords()
); );
CreateRealtimeMeetingCommand createCommand = buildCreateCommand(command, authContext, runtimeProfile); CreateRealtimeMeetingCommand createCommand = buildCreateCommand(command, authContext, runtimeProfile);
@ -208,6 +208,7 @@ public class AndroidMeetingRealtimeController {
createCommand.setAsrModelId(runtimeProfile.getResolvedAsrModelId()); createCommand.setAsrModelId(runtimeProfile.getResolvedAsrModelId());
createCommand.setSummaryModelId(runtimeProfile.getResolvedSummaryModelId()); createCommand.setSummaryModelId(runtimeProfile.getResolvedSummaryModelId());
createCommand.setPromptId(runtimeProfile.getResolvedPromptId()); createCommand.setPromptId(runtimeProfile.getResolvedPromptId());
createCommand.setHotWordGroupId(runtimeProfile.getResolvedHotWordGroupId());
createCommand.setSummaryDetailLevel(command == null ? null : command.getSummaryDetailLevel()); createCommand.setSummaryDetailLevel(command == null ? null : command.getSummaryDetailLevel());
createCommand.setMode(runtimeProfile.getResolvedMode()); createCommand.setMode(runtimeProfile.getResolvedMode());
createCommand.setLanguage(runtimeProfile.getResolvedLanguage()); createCommand.setLanguage(runtimeProfile.getResolvedLanguage());

View File

@ -21,6 +21,7 @@ public class AndroidCreateRealtimeMeetingCommand {
private Long asrModelId; private Long asrModelId;
private Long summaryModelId; private Long summaryModelId;
private Long promptId; private Long promptId;
private Long hotWordGroupId;
@Schema(description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁") @Schema(description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁")
private String summaryDetailLevel; private String summaryDetailLevel;
private String mode; private String mode;

View File

@ -2,9 +2,12 @@ package com.imeeting.dto.android;
import com.imeeting.dto.biz.AiModelVO; import com.imeeting.dto.biz.AiModelVO;
import com.imeeting.dto.biz.HotWordGroupDTO;
import com.imeeting.dto.biz.HotWordGroupVO;
import com.imeeting.dto.biz.PromptTemplateVO; import com.imeeting.dto.biz.PromptTemplateVO;
import com.unisbase.dto.SysDictItemDTO; import com.unisbase.dto.SysDictItemDTO;
import com.unisbase.entity.SysDictItem; import com.unisbase.entity.SysDictItem;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -25,22 +28,24 @@ import java.util.List;
*/ */
@Data @Data
public class AndroidMeetingConfigVo { public class AndroidMeetingConfigVo {
@io.swagger.v3.oas.annotations.media.Schema(description = "可用模型列表") @Schema(description = "可用模型列表")
private List<AiModelVO> modelsList; private List<AiModelVO> modelsList;
@io.swagger.v3.oas.annotations.media.Schema(description = "可用模板列表") @Schema(description = "可用模板列表")
private List<PromptTemplateVO> templateList; private List<PromptTemplateVO> templateList;
@io.swagger.v3.oas.annotations.media.Schema(description = "总结详细程度字典项") @Schema(description = "总结详细程度字典项")
private List<SysDictItemDTO> summaryDegreeOfDetail; private List<SysDictItemDTO> summaryDegreeOfDetail;
@io.swagger.v3.oas.annotations.media.Schema(description = "允许暂停最大时长,单位秒") @Schema(description = "允许暂停最大时长,单位秒")
private Integer maxPauseDuration; private Integer maxPauseDuration;
@io.swagger.v3.oas.annotations.media.Schema(description = "最大会议时长,单位分钟") @Schema(description = "最大会议时长,单位分钟")
private Integer maxMeetingDuration; private Integer maxMeetingDuration;
@io.swagger.v3.oas.annotations.media.Schema(description = "最小会议时长,单位秒") @Schema(description = "最小会议时长,单位秒")
private Integer minMeetingDuration; private Integer minMeetingDuration;
@io.swagger.v3.oas.annotations.media.Schema(description = "允许的最大丢包率") @Schema(description = "允许的最大丢包率")
private BigDecimal packetLossRate; private BigDecimal packetLossRate;
@io.swagger.v3.oas.annotations.media.Schema(description = "是否启用音频分片上传") @Schema(description = "是否启用音频分片上传")
private Boolean chunkUploadEnabled; private Boolean chunkUploadEnabled;
@io.swagger.v3.oas.annotations.media.Schema(description = "分片时长,单位秒") @Schema(description = "分片时长,单位秒")
private Integer chunkDurationSeconds; private Integer chunkDurationSeconds;
@Schema(description = "热词组")
private List<HotWordGroupVO> hotWordGroupList;
} }

View File

@ -17,6 +17,9 @@ public class AndroidOfflineMeetingCreateCommand extends LegacyMeetingCreateReque
@Schema(description = "总结模板ID") @Schema(description = "总结模板ID")
private Long promptId; private Long promptId;
@Schema(description = "热词组ID")
private Long hotWordGroupId;
@Schema( @Schema(
description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁", description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁",
allowableValues = { allowableValues = {

View File

@ -6,6 +6,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List;
@Data @Data
public class UpdateMeetingBasicCommand { public class UpdateMeetingBasicCommand {
@ -19,6 +20,7 @@ public class UpdateMeetingBasicCommand {
private String accessPassword; private String accessPassword;
private Long promptId; private Long promptId;
private Long summaryModelId; private Long summaryModelId;
private List<Long> participantIds;
@Schema( @Schema(
description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁", description = "总结详细程度DETAILED=详细STANDARD=标准BRIEF=简洁",

View File

@ -59,6 +59,9 @@ public class Meeting extends BaseEntity {
@Schema(description = "总结模板ID") @Schema(description = "总结模板ID")
private Long promptId; private Long promptId;
@Schema(description = "会议创建时最终生效的热词组ID")
private Long hotWordGroupId;
@Schema(description = "会议最终有效录音时长(秒)") @Schema(description = "会议最终有效录音时长(秒)")
private Integer effectiveAudioDurationSeconds; private Integer effectiveAudioDurationSeconds;

View File

@ -108,6 +108,9 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
Long requestedPromptId = request instanceof AndroidOfflineMeetingCreateCommand androidCommand Long requestedPromptId = request instanceof AndroidOfflineMeetingCreateCommand androidCommand
? androidCommand.getPromptId() ? androidCommand.getPromptId()
: null; : null;
Long requestedHotWordGroupId = request instanceof AndroidOfflineMeetingCreateCommand androidCommand
? androidCommand.getHotWordGroupId()
: null;
String requestedSummaryDetailLevel = request instanceof AndroidOfflineMeetingCreateCommand androidCommand String requestedSummaryDetailLevel = request instanceof AndroidOfflineMeetingCreateCommand androidCommand
? androidCommand.getSummaryDetailLevel() ? androidCommand.getSummaryDetailLevel()
: MeetingConstants.SUMMARY_DETAIL_STANDARD; : MeetingConstants.SUMMARY_DETAIL_STANDARD;
@ -123,7 +126,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
null, null,
null, null,
null, null,
null, requestedHotWordGroupId,
List.of() List.of()
); );
String resolvedCreatorName = meetingDomainSupport.resolveUserDisplayName(creatorUserId, creatorName); String resolvedCreatorName = meetingDomainSupport.resolveUserDisplayName(creatorUserId, creatorName);
@ -142,6 +145,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
resolvedCreatorName, resolvedCreatorName,
runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedSummaryModelId(),
runtimeProfile.getResolvedPromptId(), runtimeProfile.getResolvedPromptId(),
runtimeProfile.getResolvedHotWordGroupId(),
requestedSummaryDetailLevel, requestedSummaryDetailLevel,
0, 0,
authContext == null ? null : authContext.getDeviceId(), authContext == null ? null : authContext.getDeviceId(),
@ -207,7 +211,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
null, null,
true, true,
null, null,
null, meeting.getHotWordGroupId(),
List.of() List.of()
); );
@ -217,6 +221,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
meetingDomainSupport.applyMeetingAudioMetadata(meeting, relocatedUrl); meetingDomainSupport.applyMeetingAudioMetadata(meeting, relocatedUrl);
meeting.setSummaryModelId(profile.getResolvedSummaryModelId()); meeting.setSummaryModelId(profile.getResolvedSummaryModelId());
meeting.setPromptId(profile.getResolvedPromptId()); meeting.setPromptId(profile.getResolvedPromptId());
meeting.setHotWordGroupId(profile.getResolvedHotWordGroupId());
meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_SUCCESS); meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_SUCCESS);
meeting.setAudioSaveMessage(null); meeting.setAudioSaveMessage(null);
meeting.setOfflineRecordingStatus(MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED); meeting.setOfflineRecordingStatus(MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED);
@ -283,7 +288,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
null, null,
true, true,
null, null,
null, meeting.getHotWordGroupId(),
List.of() List.of()
); );
String stagingUrl = storeStagingAudio(audioFile); String stagingUrl = storeStagingAudio(audioFile);
@ -293,6 +298,7 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
meetingDomainSupport.prewarmPlaybackAudioAfterCommit(relocatedUrl); meetingDomainSupport.prewarmPlaybackAudioAfterCommit(relocatedUrl);
meeting.setSummaryModelId(profile.getResolvedSummaryModelId()); meeting.setSummaryModelId(profile.getResolvedSummaryModelId());
meeting.setPromptId(profile.getResolvedPromptId()); meeting.setPromptId(profile.getResolvedPromptId());
meeting.setHotWordGroupId(profile.getResolvedHotWordGroupId());
meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_SUCCESS); meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_SUCCESS);
meeting.setAudioSaveMessage(null); meeting.setAudioSaveMessage(null);
meeting.setOfflineRecordingStatus(MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED); meeting.setOfflineRecordingStatus(MeetingConstants.OFFLINE_RECORDING_UPLOAD_FINISHED);
@ -394,6 +400,9 @@ public class LegacyMeetingAdapterServiceImpl implements LegacyMeetingAdapterServ
taskConfig.put("useSpkId", profile.getResolvedUseSpkId()); taskConfig.put("useSpkId", profile.getResolvedUseSpkId());
taskConfig.put("enableTextRefine", profile.getResolvedEnableTextRefine()); taskConfig.put("enableTextRefine", profile.getResolvedEnableTextRefine());
taskConfig.put("hotWords", profile.getResolvedHotWords()); taskConfig.put("hotWords", profile.getResolvedHotWords());
if (profile.getResolvedHotWordGroupId() != null) {
taskConfig.put("hotWordGroupId", profile.getResolvedHotWordGroupId());
}
resetOrCreateTask(task, meetingId, "ASR", taskConfig); resetOrCreateTask(task, meetingId, "ASR", taskConfig);
} }

View File

@ -1209,12 +1209,6 @@ public class AiModelServiceImpl implements AiModelService {
vo.setSortOrder(entity.getSortOrder()); vo.setSortOrder(entity.getSortOrder());
vo.setRemark(entity.getRemark()); vo.setRemark(entity.getRemark());
vo.setCreatedAt(entity.getCreatedAt()); vo.setCreatedAt(entity.getCreatedAt());
if (!platformAdmin && Long.valueOf(0L).equals(entity.getTenantId())) {
vo.setBaseUrl(null);
vo.setApiKey(null);
vo.setWsUrl(null);
vo.setMediaConfig(null);
}
return vo; return vo;
} }

View File

@ -1,5 +1,7 @@
package com.imeeting.service.biz.impl; package com.imeeting.service.biz.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
@ -157,7 +159,8 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
String summaryDetailLevel = resolveSummaryDetailLevel(command.getSummaryDetailLevel()); String summaryDetailLevel = resolveSummaryDetailLevel(command.getSummaryDetailLevel());
Meeting meeting = meetingDomainSupport.initMeeting(command.getTitle(), command.getMeetingTime(), command.getParticipants(), command.getTags(), Meeting meeting = meetingDomainSupport.initMeeting(command.getTitle(), command.getMeetingTime(), command.getParticipants(), command.getTags(),
command.getAudioUrl(), MeetingConstants.TYPE_OFFLINE, meetingSource, tenantId, creatorId, resolvedCreatorName, command.getAudioUrl(), MeetingConstants.TYPE_OFFLINE, meetingSource, tenantId, creatorId, resolvedCreatorName,
hostUserId, hostName, runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedPromptId(), summaryDetailLevel, 0); hostUserId, hostName, runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedPromptId(),
runtimeProfile.getResolvedHotWordGroupId(), summaryDetailLevel, 0);
meetingService.save(meeting); meetingService.save(meeting);
AiTask asrTask = new AiTask(); AiTask asrTask = new AiTask();
@ -236,7 +239,8 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
String summaryDetailLevel = resolveSummaryDetailLevel(command.getSummaryDetailLevel()); String summaryDetailLevel = resolveSummaryDetailLevel(command.getSummaryDetailLevel());
Meeting meeting = meetingDomainSupport.initMeeting(command.getTitle(), command.getMeetingTime(), command.getParticipants(), command.getTags(), Meeting meeting = meetingDomainSupport.initMeeting(command.getTitle(), command.getMeetingTime(), command.getParticipants(), command.getTags(),
null, MeetingConstants.TYPE_REALTIME, meetingSource, tenantId, creatorId, resolvedCreatorName, null, MeetingConstants.TYPE_REALTIME, meetingSource, tenantId, creatorId, resolvedCreatorName,
hostUserId, hostName, runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedPromptId(), summaryDetailLevel, 0); hostUserId, hostName, runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedPromptId(),
runtimeProfile.getResolvedHotWordGroupId(), summaryDetailLevel, 0);
meetingService.save(meeting); meetingService.save(meeting);
Long chapterModelId = command.getChapterModelId() != null ? command.getChapterModelId() : runtimeProfile.getResolvedSummaryModelId(); Long chapterModelId = command.getChapterModelId() != null ? command.getChapterModelId() : runtimeProfile.getResolvedSummaryModelId();
createChapterTaskIfEnabled( createChapterTaskIfEnabled(
@ -314,6 +318,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
hostName, hostName,
runtimeProfile.getResolvedSummaryModelId(), runtimeProfile.getResolvedSummaryModelId(),
runtimeProfile.getResolvedPromptId(), runtimeProfile.getResolvedPromptId(),
runtimeProfile.getResolvedHotWordGroupId(),
summaryDetailLevel, summaryDetailLevel,
0, 0,
deviceCode, deviceCode,
@ -559,6 +564,9 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
asrConfig.put("useSpkId", resumeConfig.getUseSpkId() != null ? resumeConfig.getUseSpkId() : 1); asrConfig.put("useSpkId", resumeConfig.getUseSpkId() != null ? resumeConfig.getUseSpkId() : 1);
asrConfig.put("enableTextRefine", Boolean.TRUE.equals(resumeConfig.getEnableTextRefine())); asrConfig.put("enableTextRefine", Boolean.TRUE.equals(resumeConfig.getEnableTextRefine()));
asrConfig.put("hotWords", extractOfflineHotwords(resumeConfig.getHotwords())); asrConfig.put("hotWords", extractOfflineHotwords(resumeConfig.getHotwords()));
if (resumeConfig.getHotWordGroupId() != null) {
asrConfig.put("hotWordGroupId", resumeConfig.getHotWordGroupId());
}
if (asrTask == null) { if (asrTask == null) {
asrTask = new AiTask(); asrTask = new AiTask();
@ -687,21 +695,21 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
if (meeting == null) { if (meeting == null) {
throw new RuntimeException("会议不存在"); throw new RuntimeException("会议不存在");
} }
meetingRuntimeProfileResolver.resolve( // meetingRuntimeProfileResolver.resolve(
meeting.getTenantId(), // meeting.getTenantId(),
null, // null,
command.getSummaryModelId() != null ? command.getSummaryModelId() : meeting.getSummaryModelId(), // command.getSummaryModelId() != null ? command.getSummaryModelId() : meeting.getSummaryModelId(),
command.getPromptId() != null ? command.getPromptId() : meeting.getPromptId(), // command.getPromptId() != null ? command.getPromptId() : meeting.getPromptId(),
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
null, // null,
List.of() // List.of()
); // );
} }
meetingService.update(new LambdaUpdateWrapper<Meeting>() meetingService.update(new LambdaUpdateWrapper<Meeting>()
.eq(Meeting::getId, command.getMeetingId()) .eq(Meeting::getId, command.getMeetingId())
@ -711,6 +719,7 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
.set(command.getAccessPassword() != null, Meeting::getAccessPassword, normalizeAccessPassword(command.getAccessPassword())) .set(command.getAccessPassword() != null, Meeting::getAccessPassword, normalizeAccessPassword(command.getAccessPassword()))
.set(command.getSummaryModelId() != null, Meeting::getSummaryModelId, command.getSummaryModelId()) .set(command.getSummaryModelId() != null, Meeting::getSummaryModelId, command.getSummaryModelId())
.set(command.getPromptId() != null, Meeting::getPromptId, command.getPromptId()) .set(command.getPromptId() != null, Meeting::getPromptId, command.getPromptId())
.set(CollUtil.isNotEmpty(command.getParticipantIds()), Meeting::getParticipants, StrUtil.join(",", command.getParticipantIds()))
.set(command.getSummaryDetailLevel() != null, Meeting::getSummaryDetailLevel, resolveSummaryDetailLevel(command.getSummaryDetailLevel()))); .set(command.getSummaryDetailLevel() != null, Meeting::getSummaryDetailLevel, resolveSummaryDetailLevel(command.getSummaryDetailLevel())));
} }
@ -1091,7 +1100,11 @@ public class MeetingCommandServiceImpl implements MeetingCommandService {
throw new RuntimeException("未找到可用的总结任务配置"); throw new RuntimeException("未找到可用的总结任务配置");
} }
resetAiTask(asrTask, new HashMap<>(asrTask.getTaskConfig())); Map<String, Object> asrTaskConfig = new HashMap<>(asrTask.getTaskConfig());
if (meeting.getHotWordGroupId() != null) {
asrTaskConfig.put("hotWordGroupId", meeting.getHotWordGroupId());
}
resetAiTask(asrTask, asrTaskConfig);
aiTaskService.updateById(asrTask); aiTaskService.updateById(asrTask);
Long effectiveSummaryModelId = resolveMeetingSummaryModelId(meeting, summaryTask); Long effectiveSummaryModelId = resolveMeetingSummaryModelId(meeting, summaryTask);
Long effectivePromptId = resolveMeetingPromptId(meeting, summaryTask); Long effectivePromptId = resolveMeetingPromptId(meeting, summaryTask);

View File

@ -70,6 +70,18 @@ public class MeetingDomainSupport {
@Value("${imeeting.audio.ffmpeg-path:ffmpeg}") @Value("${imeeting.audio.ffmpeg-path:ffmpeg}")
private String ffmpegPath; private String ffmpegPath;
public Meeting initMeeting(String title, LocalDateTime meetingTime, String participants, String tags,
String audioUrl, String meetingType, String meetingSource,
Long tenantId, Long creatorId, String creatorName,
Long hostUserId, String hostName,
Long summaryModelId, Long promptId,
Long hotWordGroupId,
String summaryDetailLevel, int status) {
return initMeeting(title, meetingTime, participants, tags, audioUrl, meetingType, meetingSource,
tenantId, creatorId, creatorName, hostUserId, hostName, summaryModelId, promptId, hotWordGroupId, summaryDetailLevel, status,
null, null);
}
public Meeting initMeeting(String title, LocalDateTime meetingTime, String participants, String tags, public Meeting initMeeting(String title, LocalDateTime meetingTime, String participants, String tags,
String audioUrl, String meetingType, String meetingSource, String audioUrl, String meetingType, String meetingSource,
Long tenantId, Long creatorId, String creatorName, Long tenantId, Long creatorId, String creatorName,
@ -85,7 +97,7 @@ public class MeetingDomainSupport {
String audioUrl, String meetingType, String meetingSource, String audioUrl, String meetingType, String meetingSource,
Long tenantId, Long creatorId, String creatorName, Long tenantId, Long creatorId, String creatorName,
Long hostUserId, String hostName, Long hostUserId, String hostName,
Long summaryModelId, Long promptId, Long summaryModelId, Long promptId, Long hotWordGroupId,
String summaryDetailLevel, int status, String summaryDetailLevel, int status,
String sourceDeviceCode, String sourceDeviceMode) { String sourceDeviceCode, String sourceDeviceMode) {
Meeting meeting = new Meeting(); Meeting meeting = new Meeting();
@ -105,6 +117,7 @@ public class MeetingDomainSupport {
meeting.setSourceDeviceMode(sourceDeviceMode); meeting.setSourceDeviceMode(sourceDeviceMode);
meeting.setSummaryModelId(summaryModelId); meeting.setSummaryModelId(summaryModelId);
meeting.setPromptId(promptId); meeting.setPromptId(promptId);
meeting.setHotWordGroupId(hotWordGroupId);
meeting.setSummaryDetailLevel(normalizeSummaryDetailLevel(summaryDetailLevel)); meeting.setSummaryDetailLevel(normalizeSummaryDetailLevel(summaryDetailLevel));
meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_NONE); meeting.setAudioSaveStatus(RealtimeMeetingAudioStorageService.STATUS_NONE);
if (MeetingConstants.TYPE_OFFLINE.equalsIgnoreCase(meetingType)) { if (MeetingConstants.TYPE_OFFLINE.equalsIgnoreCase(meetingType)) {
@ -114,6 +127,18 @@ public class MeetingDomainSupport {
return meeting; return meeting;
} }
public Meeting initMeeting(String title, LocalDateTime meetingTime, String participants, String tags,
String audioUrl, String meetingType, String meetingSource,
Long tenantId, Long creatorId, String creatorName,
Long hostUserId, String hostName,
Long summaryModelId, Long promptId,
String summaryDetailLevel, int status,
String sourceDeviceCode, String sourceDeviceMode) {
return initMeeting(title, meetingTime, participants, tags, audioUrl, meetingType, meetingSource,
tenantId, creatorId, creatorName, hostUserId, hostName, summaryModelId, promptId, null, summaryDetailLevel, status,
sourceDeviceCode, sourceDeviceMode);
}
public String resolveUserDisplayName(Long userId, String fallbackName) { public String resolveUserDisplayName(Long userId, String fallbackName) {
if (userId == null) { if (userId == null) {
return fallbackName; return fallbackName;
@ -408,6 +433,7 @@ public class MeetingDomainSupport {
vo.setOfflineRecordingStatus(meeting.getOfflineRecordingStatus()); vo.setOfflineRecordingStatus(meeting.getOfflineRecordingStatus());
vo.setSummaryModelId(meeting.getSummaryModelId()); vo.setSummaryModelId(meeting.getSummaryModelId());
vo.setPromptId(meeting.getPromptId()); vo.setPromptId(meeting.getPromptId());
fillEffectiveHotWordGroup(meeting, vo);
vo.setAiCatalogEnabled(resolveAiCatalogEnabled()); vo.setAiCatalogEnabled(resolveAiCatalogEnabled());
vo.setSummaryDetailLevel(normalizeSummaryDetailLevel(meeting.getSummaryDetailLevel())); vo.setSummaryDetailLevel(normalizeSummaryDetailLevel(meeting.getSummaryDetailLevel()));
vo.setAudioSaveStatus(meeting.getAudioSaveStatus()); vo.setAudioSaveStatus(meeting.getAudioSaveStatus());
@ -446,7 +472,6 @@ public class MeetingDomainSupport {
vo.setSummaryContent(meetingSummaryFileService.loadSummaryContent(meeting)); vo.setSummaryContent(meetingSummaryFileService.loadSummaryContent(meeting));
vo.setAnalysis(meetingSummaryFileService.loadSummaryAnalysis(meeting)); vo.setAnalysis(meetingSummaryFileService.loadSummaryAnalysis(meeting));
vo.setLastUserPrompt(resolveLastSummaryUserPrompt(meeting)); vo.setLastUserPrompt(resolveLastSummaryUserPrompt(meeting));
fillEffectiveHotWordGroup(meeting, vo);
} }
} }
@ -463,6 +488,9 @@ public class MeetingDomainSupport {
} }
private Long resolveEffectiveHotWordGroupId(Meeting meeting) { private Long resolveEffectiveHotWordGroupId(Meeting meeting) {
if (meeting != null && meeting.getHotWordGroupId() != null) {
return meeting.getHotWordGroupId();
}
AiTask latestAsrTask = resolveLatestAsrTask(meeting); AiTask latestAsrTask = resolveLatestAsrTask(meeting);
Long asrHotWordGroupId = longValue( Long asrHotWordGroupId = longValue(
latestAsrTask == null || latestAsrTask.getTaskConfig() == null latestAsrTask == null || latestAsrTask.getTaskConfig() == null

View File

@ -29,9 +29,20 @@ export async function deleteParam(id: number) {
return resp.data.data as boolean; return resp.data.data as boolean;
} }
export async function listUsers(params?: { tenantId?: number; orgId?: number }) { export async function listUsers(params?: { tenantId?: number; orgId?: number; keyword?: string }) {
const resp = await http.get("/sys/api/users", {params: {current: 1, size: 1000, ...params}});
return (resp.data.data as PageResult<SysUser[]>).records || [];
}
export async function pageUsers(params?: {
current?: number;
size?: number;
tenantId?: number;
orgId?: number;
keyword?: string
}) {
const resp = await http.get("/sys/api/users", { params }); const resp = await http.get("/sys/api/users", { params });
return resp.data.data as SysUser[]; return resp.data.data as PageResult<SysUser[]>;
} }
export async function createUser(payload: Partial<SysUser>) { export async function createUser(payload: Partial<SysUser>) {

View File

@ -43,7 +43,7 @@ import {
listRoles, listRoles,
listTenants, listTenants,
listUserRoles, listUserRoles,
listUsers, pageUsers,
resetUserPassword, resetUserPassword,
updateUser, updateUser,
uploadPlatformAsset uploadPlatformAsset
@ -130,13 +130,17 @@ export default function Users() {
const [current, setCurrent] = useState(1); const [current, setCurrent] = useState(1);
const [pageSize, setPageSize] = useState(10); const [pageSize, setPageSize] = useState(10);
const [searchText, setSearchText] = useState(""); const [searchText, setSearchText] = useState("");
const [total, setTotal] = useState(0);
const [queryKeyword, setQueryKeyword] = useState("");
const handleSearch = () => { const handleSearch = () => {
setQueryKeyword(searchText.trim());
setCurrent(1); setCurrent(1);
}; };
const handleResetSearch = () => { const handleResetSearch = () => {
setSearchText(""); setSearchText("");
setQueryKeyword("");
setFilterTenantId(undefined); setFilterTenantId(undefined);
setCurrent(1); setCurrent(1);
}; };
@ -213,8 +217,14 @@ export default function Users() {
const loadUsersData = async () => { const loadUsersData = async () => {
setLoading(true); setLoading(true);
try { try {
const usersList = await listUsers({ tenantId: filterTenantId }); const response = await pageUsers({
setData(usersList || []); current,
size: pageSize,
tenantId: filterTenantId,
keyword: queryKeyword || undefined
});
setData(response?.records || []);
setTotal(response?.total || 0);
} finally { } finally {
setLoading(false); setLoading(false);
} }
@ -238,7 +248,7 @@ export default function Users() {
useEffect(() => { useEffect(() => {
loadUsersData(); loadUsersData();
}, [filterTenantId]); }, [filterTenantId, current, pageSize, queryKeyword]);
useEffect(() => { useEffect(() => {
const fetchOrgs = async () => { const fetchOrgs = async () => {
@ -255,16 +265,7 @@ export default function Users() {
const orgTreeData = useMemo(() => buildOrgTree(orgs), [orgs]); const orgTreeData = useMemo(() => buildOrgTree(orgs), [orgs]);
const filteredData = useMemo(() => {
if (!searchText) return data;
const lower = searchText.toLowerCase();
return data.filter(
(user) =>
user.username.toLowerCase().includes(lower) ||
user.displayName.toLowerCase().includes(lower) ||
(user.email && user.email.toLowerCase().includes(lower))
);
}, [data, searchText]);
const openCreate = () => { const openCreate = () => {
setEditing(null); setEditing(null);
@ -511,7 +512,10 @@ export default function Users() {
<Space size={8} wrap> <Space size={8} wrap>
{isPlatformMode && {isPlatformMode &&
<Select placeholder={t("users.tenantFilter")} style={{width: 200}} allowClear value={filterTenantId} <Select placeholder={t("users.tenantFilter")} style={{width: 200}} allowClear value={filterTenantId}
onChange={setFilterTenantId} onChange={(value) => {
setFilterTenantId(value);
setCurrent(1);
}}
options={tenants.map((tenant) => ({label: tenant.tenantName, value: tenant.id}))} options={tenants.map((tenant) => ({label: tenant.tenantName, value: tenant.id}))}
suffixIcon={<ShopOutlined aria-hidden="true"/>}/>} suffixIcon={<ShopOutlined aria-hidden="true"/>}/>}
<Input placeholder={t("users.searchPlaceholder")} prefix={<SearchOutlined aria-hidden="true"/>} <Input placeholder={t("users.searchPlaceholder")} prefix={<SearchOutlined aria-hidden="true"/>}
@ -528,7 +532,7 @@ export default function Users() {
<AppPagination <AppPagination
current={current} current={current}
pageSize={pageSize} pageSize={pageSize}
total={filteredData.length} total={total}
onChange={(page, size) => { onChange={(page, size) => {
setCurrent(page); setCurrent(page);
setPageSize(size); setPageSize(size);
@ -539,7 +543,7 @@ export default function Users() {
<ListTable <ListTable
rowKey="userId" rowKey="userId"
columns={columns} columns={columns}
dataSource={filteredData.slice((current - 1) * pageSize, current * pageSize)} dataSource={data}
loading={loading} loading={loading}
scroll={{ y: "100%" }} scroll={{ y: "100%" }}
pagination={false} pagination={false}

View File

@ -231,6 +231,11 @@ export default function Profile() {
label: t("users.phone"), label: t("users.phone"),
children: <span className="tabular-nums">{renderValue(user?.phone)}</span> children: <span className="tabular-nums">{renderValue(user?.phone)}</span>
}, },
{
key: "organization",
label: t("users.org"),
children: renderValue(user?.orgPath || user?.orgName)
},
{ {
key: "status", key: "status",
label: t("common.status"), label: t("common.status"),

View File

@ -31,6 +31,9 @@ export interface UserProfile {
email?: string; email?: string;
phone?: string; phone?: string;
avatarUrl?: string; avatarUrl?: string;
orgId?: number;
orgName?: string;
orgPath?: string;
status?: number; status?: number;
isAdmin: boolean; isAdmin: boolean;
isPlatformAdmin?: boolean; isPlatformAdmin?: boolean;