deploy 04191544

This commit is contained in:
MaDaLei 2026-04-19 15:44:49 +08:00
parent 01e04064cb
commit 6b2a60de32
3 changed files with 49 additions and 3 deletions

View File

@ -62,8 +62,19 @@ public class ReportController {
return "photo"; return "photo";
} }
/**
* 报告 JSON 中与服务回顾短片相关的字段与小程序 / H5 共用契约
* <ul>
* <li>{@code highlightVideoUrl} 成片 MP4库内为相对路径 {@code /api/upload/image/...} 或历史绝对 URL接口统一为可访问绝对 URL</li>
* <li>{@code highlightShareCoverUrl} 成片首帧 JPEG用于分享卡片封面截帧失败时为 {@code null}前端按 utils 中分享封面降级链处理</li>
* <li>{@code highlightPosterUrl} 预留画布海报等当前恒为 {@code null}前端可用 {@code highlightShareCoverUrl}</li>
* <li>{@code highlightVideoStatus} {@code processing} | {@code done} | {@code failed} </li>
* </ul>
*/
private void putHighlightFields(Map<String, Object> target, Report r) { private void putHighlightFields(Map<String, Object> target, Report r) {
target.put("highlightVideoUrl", r.getHighlightVideoUrl() != null ? fullUrl(r.getHighlightVideoUrl()) : null); target.put("highlightVideoUrl", r.getHighlightVideoUrl() != null ? fullUrl(r.getHighlightVideoUrl()) : null);
target.put("highlightShareCoverUrl", r.getHighlightShareCoverUrl() != null ? fullUrl(r.getHighlightShareCoverUrl()) : null);
target.put("highlightPosterUrl", null);
target.put("highlightVideoStatus", r.getHighlightVideoStatus()); target.put("highlightVideoStatus", r.getHighlightVideoStatus());
target.put("highlightVideoError", r.getHighlightVideoError()); target.put("highlightVideoError", r.getHighlightVideoError());
target.put("highlightDurationSec", r.getHighlightDurationSec()); target.put("highlightDurationSec", r.getHighlightDurationSec());

View File

@ -75,4 +75,11 @@ public class Report {
/** 失败原因归类material | service | network | unknown对宠主展示无技术细节 */ /** 失败原因归类material | service | network | unknown对宠主展示无技术细节 */
@Column(name = "highlight_fail_reason", length = 16) @Column(name = "highlight_fail_reason", length = 16)
private String highlightFailReason; private String highlightFailReason;
/**
* 成片首帧截图竖屏内一帧用于分享卡片封面等相对路径如 {@code /api/upload/image////xxx_cover.jpg}
* 与成片 MP4 同目录仅当 highlight 状态为 done 且截帧成功时有值
*/
@Column(name = "highlight_share_cover_url", length = 500)
private String highlightShareCoverUrl;
} }

View File

@ -143,6 +143,7 @@ public class ReportHighlightVideoService {
report.setHighlightVideoStatus("processing"); report.setHighlightVideoStatus("processing");
report.setHighlightVideoError(null); report.setHighlightVideoError(null);
report.setHighlightFailReason(null); report.setHighlightFailReason(null);
report.setHighlightShareCoverUrl(null);
report.setHighlightComposeMode(mode); report.setHighlightComposeMode(mode);
report.setUpdateTime(LocalDateTime.now()); report.setUpdateTime(LocalDateTime.now());
reportMapper.save(report); reportMapper.save(report);
@ -356,13 +357,20 @@ public class ReportHighlightVideoService {
Path finalPath = outDir.resolve(filename); Path finalPath = outDir.resolve(filename);
Files.copy(afterBgm, finalPath, StandardCopyOption.REPLACE_EXISTING); Files.copy(afterBgm, finalPath, StandardCopyOption.REPLACE_EXISTING);
String publicBase = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl; String videoRel = "/api/upload/image/" + datePath + "/" + filename;
String url = publicBase + "/api/upload/image/" + datePath + "/" + filename; String coverFilename = filename.endsWith(".mp4")
? filename.substring(0, filename.length() - 4) + "_cover.jpg"
: filename + "_cover.jpg";
Path coverPath = outDir.resolve(coverFilename);
String coverRel = extractVideoCoverFrame(finalPath, coverPath)
? "/api/upload/image/" + datePath + "/" + coverFilename
: null;
int durationRoundedSec = (int) Math.ceil(totalDurationSec); int durationRoundedSec = (int) Math.ceil(totalDurationSec);
synchronized (lockFor(reportId)) { synchronized (lockFor(reportId)) {
reportMapper.findByIdAndDeletedFalse(reportId).ifPresent(r -> { reportMapper.findByIdAndDeletedFalse(reportId).ifPresent(r -> {
r.setHighlightVideoUrl(url); r.setHighlightVideoUrl(videoRel);
r.setHighlightShareCoverUrl(coverRel);
r.setHighlightVideoStatus("done"); r.setHighlightVideoStatus("done");
r.setHighlightVideoError(null); r.setHighlightVideoError(null);
r.setHighlightFailReason(null); r.setHighlightFailReason(null);
@ -451,6 +459,26 @@ public class ReportHighlightVideoService {
/** /**
* 服务前第一张 | 服务后第一张 左右拼接为 1080×1920 竖屏对比图左前右后 * 服务前第一张 | 服务后第一张 左右拼接为 1080×1920 竖屏对比图左前右后
*/ */
/**
* 从成片 MP4 截取首帧为 JPEG供分享卡片等使用
*
* @return 是否写出有效文件
*/
private boolean extractVideoCoverFrame(Path mp4, Path outJpg) {
try {
int code = runFfmpeg(List.of(
ffmpegBinary, "-y",
"-i", mp4.toString(),
"-map", "0:v:0",
"-frames:v", "1",
"-q:v", "2",
outJpg.toString()));
return code == 0 && Files.isRegularFile(outJpg);
} catch (Exception e) {
return false;
}
}
private boolean buildComparisonCover(Path beforeFile, Path afterFile, Path outJpg) throws Exception { private boolean buildComparisonCover(Path beforeFile, Path afterFile, Path outJpg) throws Exception {
String fc = "[0:v]scale=540:1920:force_original_aspect_ratio=decrease,pad=540:1920:(ow-iw)/2:(oh-ih)/2,setsar=1[va];" String fc = "[0:v]scale=540:1920:force_original_aspect_ratio=decrease,pad=540:1920:(ow-iw)/2:(oh-ih)/2,setsar=1[va];"
+ "[1:v]scale=540:1920:force_original_aspect_ratio=decrease,pad=540:1920:(ow-iw)/2:(oh-ih)/2,setsar=1[vb];" + "[1:v]scale=540:1920:force_original_aspect_ratio=decrease,pad=540:1920:(ow-iw)/2:(oh-ih)/2,setsar=1[vb];"