feat: 更新 Android 会议控制器,添加 ASR 重试和总结重试功能
- 新增 `retryTranscription` 和 `retrySummary` 方法,支持重试 ASR 识别和会议总结 - 更新 `create` 方法,返回 `AndroidMeetingCreateResponse` 对象 - 更新 `list` 方法,返回 `AndroidMeetingListItemVO` 列表 - 添加辅助方法 `buildAndroidMeetingCreateResponse` 和 `buildAndroidMeetingListPage` 用于构建响应对象 - 引入 `h5BaseUrl` 配置项,用于生成会议预览 URLdev_na
parent
11c3ee1b09
commit
1e7a8ad83c
|
|
@ -6,7 +6,9 @@ import com.imeeting.common.MeetingConstants;
|
||||||
import com.imeeting.common.SysParamKeys;
|
import com.imeeting.common.SysParamKeys;
|
||||||
import com.imeeting.common.exception.ExistingOfflineMeetingException;
|
import com.imeeting.common.exception.ExistingOfflineMeetingException;
|
||||||
import com.imeeting.dto.android.AndroidAuthContext;
|
import com.imeeting.dto.android.AndroidAuthContext;
|
||||||
|
import com.imeeting.dto.android.AndroidMeetingCreateResponse;
|
||||||
import com.imeeting.dto.android.AndroidMeetingConfigVo;
|
import com.imeeting.dto.android.AndroidMeetingConfigVo;
|
||||||
|
import com.imeeting.dto.android.AndroidMeetingListItemVO;
|
||||||
import com.imeeting.dto.android.AndroidOfflineMeetingConflictVO;
|
import com.imeeting.dto.android.AndroidOfflineMeetingConflictVO;
|
||||||
import com.imeeting.dto.android.AndroidOfflineMeetingFinishRequest;
|
import com.imeeting.dto.android.AndroidOfflineMeetingFinishRequest;
|
||||||
import com.imeeting.dto.android.AndroidUnifiedMeetingStatusRequest;
|
import com.imeeting.dto.android.AndroidUnifiedMeetingStatusRequest;
|
||||||
|
|
@ -51,6 +53,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
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.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
|
@ -65,7 +70,9 @@ import org.springframework.web.multipart.MultipartFile;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -84,6 +91,9 @@ public class AndroidMeetingController {
|
||||||
private static final String STAGE_SUMMARY_GENERATION = "summary_generation";
|
private static final String STAGE_SUMMARY_GENERATION = "summary_generation";
|
||||||
private static final String STAGE_COMPLETED = "completed";
|
private static final String STAGE_COMPLETED = "completed";
|
||||||
|
|
||||||
|
@Value("${imeeting.h5.base-url:}")
|
||||||
|
private String h5BaseUrl;
|
||||||
|
|
||||||
private final AndroidAuthService androidAuthService;
|
private final AndroidAuthService androidAuthService;
|
||||||
private final AndroidChunkUploadService androidChunkUploadService;
|
private final AndroidChunkUploadService androidChunkUploadService;
|
||||||
private final LegacyMeetingAdapterService legacyMeetingAdapterService;
|
private final LegacyMeetingAdapterService legacyMeetingAdapterService;
|
||||||
|
|
@ -138,7 +148,7 @@ public class AndroidMeetingController {
|
||||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||||
responseCode = "200",
|
responseCode = "200",
|
||||||
description = "返回新创建的会议详情",
|
description = "返回新创建的会议详情",
|
||||||
content = @Content(schema = @Schema(implementation = MeetingVO.class))
|
content = @Content(schema = @Schema(implementation = AndroidMeetingCreateResponse.class))
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@PostMapping("/create")
|
@PostMapping("/create")
|
||||||
|
|
@ -153,7 +163,8 @@ public class AndroidMeetingController {
|
||||||
// if (existingMeeting != null) {
|
// if (existingMeeting != null) {
|
||||||
// return new ApiResponse<>("409", "设备端已有会议", meetingQueryService.getDetailIgnoreTenant(existingMeeting.getId()));
|
// return new ApiResponse<>("409", "设备端已有会议", meetingQueryService.getDetailIgnoreTenant(existingMeeting.getId()));
|
||||||
// }
|
// }
|
||||||
return ApiResponse.ok(legacyMeetingAdapterService.createMeeting(command, authContext, loginUser));
|
MeetingVO meeting = legacyMeetingAdapterService.createMeeting(command, authContext, loginUser);
|
||||||
|
return ApiResponse.ok(buildAndroidMeetingCreateResponse(meeting));
|
||||||
} catch (ExistingOfflineMeetingException ex) {
|
} catch (ExistingOfflineMeetingException ex) {
|
||||||
return new ApiResponse<>("409", "有未结束会议", new AndroidOfflineMeetingConflictVO(ex.getMeetingId()));
|
return new ApiResponse<>("409", "有未结束会议", new AndroidOfflineMeetingConflictVO(ex.getMeetingId()));
|
||||||
}
|
}
|
||||||
|
|
@ -247,7 +258,7 @@ public class AndroidMeetingController {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public ApiResponse<PageResult<List<MeetingVO>>> list(HttpServletRequest request,
|
public ApiResponse<PageResult<List<AndroidMeetingListItemVO>>> list(HttpServletRequest request,
|
||||||
@RequestParam(value = "user_id", required = false) Long ignoredUserId,
|
@RequestParam(value = "user_id", required = false) Long ignoredUserId,
|
||||||
@RequestParam(defaultValue = "1") Integer page,
|
@RequestParam(defaultValue = "1") Integer page,
|
||||||
@RequestParam(value = "page_size", defaultValue = "10") Integer pageSize,
|
@RequestParam(value = "page_size", defaultValue = "10") Integer pageSize,
|
||||||
|
|
@ -259,7 +270,7 @@ public class AndroidMeetingController {
|
||||||
"title", title);
|
"title", title);
|
||||||
AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);
|
AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);
|
||||||
LoginUser loginUser = AndroidLoginUserSupport.requireLoginUser(authContext);
|
LoginUser loginUser = AndroidLoginUserSupport.requireLoginUser(authContext);
|
||||||
return ApiResponse.ok(meetingQueryService.pageMeetings(
|
PageResult<List<MeetingVO>> result = meetingQueryService.pageMeetings(
|
||||||
page,
|
page,
|
||||||
pageSize,
|
pageSize,
|
||||||
title,
|
title,
|
||||||
|
|
@ -269,7 +280,8 @@ public class AndroidMeetingController {
|
||||||
"all",
|
"all",
|
||||||
null,
|
null,
|
||||||
AndroidLoginUserSupport.isAdmin(authContext)
|
AndroidLoginUserSupport.isAdmin(authContext)
|
||||||
));
|
);
|
||||||
|
return ApiResponse.ok(buildAndroidMeetingListPage(result));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(summary = "查询Android会议预览数据")
|
@Operation(summary = "查询Android会议预览数据")
|
||||||
|
|
@ -323,6 +335,43 @@ public class AndroidMeetingController {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "重试 Android 会议 ASR 识别")
|
||||||
|
@ApiResponses({
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "重试成功返回 true",
|
||||||
|
content = @Content(schema = @Schema(implementation = Boolean.class))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@PostMapping("/{meetingId}/transcripts/retry")
|
||||||
|
@Anonymous
|
||||||
|
public ApiResponse<Boolean> retryTranscription(HttpServletRequest request, @PathVariable Long meetingId) {
|
||||||
|
AndroidRequestLogHelper.logRequest(log, "Android会议", "重试会议 ASR 识别接口", "meetingId", meetingId);
|
||||||
|
AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);
|
||||||
|
LoginUser loginUser = authContext.isAnonymous() ? null : AndroidLoginUserSupport.requireLoginUser(authContext);
|
||||||
|
requireOperableOfflineMeeting(meetingId, authContext, loginUser);
|
||||||
|
meetingCommandService.retryTranscription(meetingId);
|
||||||
|
return ApiResponse.ok(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "重试 Android 会议总结")
|
||||||
|
@ApiResponses({
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||||
|
responseCode = "200",
|
||||||
|
description = "重试成功返回 true",
|
||||||
|
content = @Content(schema = @Schema(implementation = Boolean.class))
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@PostMapping("/{meetingId}/summary/retry")
|
||||||
|
@Anonymous
|
||||||
|
public ApiResponse<Boolean> retrySummary(HttpServletRequest request, @PathVariable Long meetingId) {
|
||||||
|
AndroidRequestLogHelper.logRequest(log, "Android会议", "重试会议总结接口", "meetingId", meetingId);
|
||||||
|
AndroidAuthContext authContext = androidAuthService.authenticateHttp(request);
|
||||||
|
LoginUser loginUser = authContext.isAnonymous() ? null : AndroidLoginUserSupport.requireLoginUser(authContext);
|
||||||
|
requireOperableOfflineMeeting(meetingId, authContext, loginUser);
|
||||||
|
meetingCommandService.retrySummary(meetingId);
|
||||||
|
return ApiResponse.ok(true);
|
||||||
|
}
|
||||||
@Operation(summary = "更新Android会议访问密码")
|
@Operation(summary = "更新Android会议访问密码")
|
||||||
@ApiResponses({
|
@ApiResponses({
|
||||||
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(
|
||||||
|
|
@ -419,6 +468,67 @@ public class AndroidMeetingController {
|
||||||
return ApiResponse.ok(resultVo);
|
return ApiResponse.ok(resultVo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private AndroidMeetingCreateResponse buildAndroidMeetingCreateResponse(MeetingVO meeting) {
|
||||||
|
AndroidMeetingCreateResponse response = new AndroidMeetingCreateResponse();
|
||||||
|
if (meeting == null) {
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
BeanUtils.copyProperties(meeting, response);
|
||||||
|
response.setPreviewUrl(buildPreviewUrl(meeting.getId()));
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PageResult<List<AndroidMeetingListItemVO>> buildAndroidMeetingListPage(PageResult<List<MeetingVO>> source) {
|
||||||
|
PageResult<List<AndroidMeetingListItemVO>> result = new PageResult<>();
|
||||||
|
if (source == null) {
|
||||||
|
result.setTotal(0L);
|
||||||
|
result.setRecords(List.of());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.setTotal(source.getTotal());
|
||||||
|
result.setRecords(source.getRecords() == null
|
||||||
|
? List.of()
|
||||||
|
: source.getRecords().stream().map(this::buildAndroidMeetingListItem).toList());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AndroidMeetingListItemVO buildAndroidMeetingListItem(MeetingVO meeting) {
|
||||||
|
AndroidMeetingListItemVO item = new AndroidMeetingListItemVO();
|
||||||
|
if (meeting == null) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
BeanUtils.copyProperties(meeting, item);
|
||||||
|
item.setPreviewUrl(buildPreviewUrl(meeting.getId()));
|
||||||
|
item.setDayOffset(resolveDayOffset(meeting.getMeetingTime()));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildPreviewUrl(Long meetingId) {
|
||||||
|
if (meetingId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String baseUrl = normalizeH5BaseUrl();
|
||||||
|
if (!StringUtils.hasText(baseUrl)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return baseUrl + "/meetings/" + meetingId + "/preview";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String normalizeH5BaseUrl() {
|
||||||
|
String baseUrl = StringUtils.hasText(h5BaseUrl) ? h5BaseUrl.trim() : "";
|
||||||
|
if (!StringUtils.hasText(baseUrl)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long resolveDayOffset(LocalDateTime meetingTime) {
|
||||||
|
if (meetingTime == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ChronoUnit.DAYS.between(LocalDate.now(), meetingTime.toLocalDate());
|
||||||
|
}
|
||||||
|
|
||||||
private MeetingVO requireOperableOfflineMeeting(Long meetingId, AndroidAuthContext authContext, LoginUser loginUser) {
|
private MeetingVO requireOperableOfflineMeeting(Long meetingId, AndroidAuthContext authContext, LoginUser loginUser) {
|
||||||
MeetingVO meeting = meetingQueryService.getDetailIgnoreTenant(meetingId);
|
MeetingVO meeting = meetingQueryService.getDetailIgnoreTenant(meetingId);
|
||||||
if (meeting == null) {
|
if (meeting == null) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue