Files
label_backend/src/main/java/com/label/controller/VideoController.java
2026-04-14 16:33:34 +08:00

111 lines
4.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.label.controller;
import com.label.annotation.RequireRole;
import com.label.common.auth.TokenPrincipal;
import com.label.common.result.Result;
import com.label.entity.VideoProcessJob;
import com.label.service.VideoProcessService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 视频处理接口4 个端点)。
*
* POST /api/video/process — 触发视频处理ADMIN
* GET /api/video/jobs/{jobId} — 查询任务状态ADMIN
* POST /api/video/jobs/{jobId}/reset — 重置失败任务ADMIN
* POST /api/video/callback — AI 回调接口(无需认证,已在 AuthInterceptor 中排除)
*/
@Tag(name = "视频处理", description = "视频处理任务创建、查询、重置和回调")
@Slf4j
@RestController
@RequiredArgsConstructor
public class VideoController {
private final VideoProcessService videoProcessService;
@Value("${video.callback-secret:}")
private String callbackSecret;
/** POST /api/video/process — 触发视频处理任务 */
@Operation(summary = "触发视频处理任务")
@PostMapping("/api/video/process")
@RequireRole("ADMIN")
public Result<VideoProcessJob> createJob(@RequestBody Map<String, Object> body,
HttpServletRequest request) {
Object sourceIdVal = body.get("sourceId");
Object jobTypeVal = body.get("jobType");
if (sourceIdVal == null || jobTypeVal == null) {
return Result.failure("INVALID_PARAMS", "sourceId 和 jobType 不能为空");
}
Long sourceId = Long.parseLong(sourceIdVal.toString());
String jobType = jobTypeVal.toString();
String params = body.containsKey("params") ? body.get("params").toString() : null;
TokenPrincipal principal = principal(request);
return Result.success(
videoProcessService.createJob(sourceId, jobType, params, principal.getCompanyId()));
}
/** GET /api/video/jobs/{jobId} — 查询视频处理任务 */
@Operation(summary = "查询视频处理任务状态")
@GetMapping("/api/video/jobs/{jobId}")
@RequireRole("ADMIN")
public Result<VideoProcessJob> getJob(@PathVariable Long jobId,
HttpServletRequest request) {
return Result.success(videoProcessService.getJob(jobId, principal(request).getCompanyId()));
}
/** POST /api/video/jobs/{jobId}/reset — 管理员重置失败任务 */
@Operation(summary = "重置失败的视频处理任务")
@PostMapping("/api/video/jobs/{jobId}/reset")
@RequireRole("ADMIN")
public Result<VideoProcessJob> resetJob(@PathVariable Long jobId,
HttpServletRequest request) {
return Result.success(videoProcessService.reset(jobId, principal(request).getCompanyId()));
}
/**
* POST /api/video/callback — AI 服务回调(无需 Bearer Token
*
* 此端点已在 AuthInterceptor 中排除认证,
* 由 AI 服务直接调用,携带 jobId、status、outputPath 等参数。
*
* Body 示例:
* { "jobId": 123, "status": "SUCCESS", "outputPath": "processed/123/frames.zip" }
* { "jobId": 123, "status": "FAILED", "errorMessage": "ffmpeg error: ..." }
*/
@Operation(summary = "接收 AI 服务视频处理回调")
@PostMapping("/api/video/callback")
public Result<Void> handleCallback(@RequestBody Map<String, Object> body,
HttpServletRequest request) {
// 共享密钥校验(配置了 VIDEO_CALLBACK_SECRET 时强制校验)
if (callbackSecret != null && !callbackSecret.isBlank()) {
String provided = request.getHeader("X-Callback-Secret");
if (!callbackSecret.equals(provided)) {
return Result.failure("UNAUTHORIZED", "回调密钥无效");
}
}
Long jobId = Long.parseLong(body.get("jobId").toString());
String status = (String) body.get("status");
String outputPath = body.containsKey("outputPath") ? (String) body.get("outputPath") : null;
String errorMessage = body.containsKey("errorMessage") ? (String) body.get("errorMessage") : null;
log.info("视频处理回调jobId={}, status={}", jobId, status);
videoProcessService.handleCallback(jobId, status, outputPath, errorMessage);
return Result.success(null);
}
private TokenPrincipal principal(HttpServletRequest request) {
return (TokenPrincipal) request.getAttribute("__token_principal__");
}
}