fix: 修復圖片 URL 生成邏輯,確保返回完整的 Google Cloud Storage URLs
- 注入 IImageStorageService 到 FlashcardsController - 添加 GetImageUrlAsync 方法統一處理圖片 URL 生成 - 重構 GetFlashcards 從 LINQ 改為 foreach 迴圈支援異步操作 - 修復 GetFlashcard 方法的圖片 URL 處理邏輯 - 確保前端接收到完整的 GCS URLs 而非相對路徑 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
1a20a562d2
commit
a953509ba8
|
|
@ -6,6 +6,7 @@ using DramaLing.Api.Contracts.Services.Review;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using DramaLing.Api.Utils;
|
using DramaLing.Api.Utils;
|
||||||
using DramaLing.Api.Services;
|
using DramaLing.Api.Services;
|
||||||
|
using DramaLing.Api.Services.Storage;
|
||||||
using DramaLing.Api.Data;
|
using DramaLing.Api.Data;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
|
|
@ -18,17 +19,33 @@ public class FlashcardsController : BaseController
|
||||||
private readonly IFlashcardRepository _flashcardRepository;
|
private readonly IFlashcardRepository _flashcardRepository;
|
||||||
private readonly IReviewService _reviewService;
|
private readonly IReviewService _reviewService;
|
||||||
private readonly DramaLingDbContext _context;
|
private readonly DramaLingDbContext _context;
|
||||||
|
private readonly IImageStorageService _imageStorageService;
|
||||||
|
|
||||||
public FlashcardsController(
|
public FlashcardsController(
|
||||||
IFlashcardRepository flashcardRepository,
|
IFlashcardRepository flashcardRepository,
|
||||||
IReviewService reviewService,
|
IReviewService reviewService,
|
||||||
DramaLingDbContext context,
|
DramaLingDbContext context,
|
||||||
|
IImageStorageService imageStorageService,
|
||||||
IAuthService authService,
|
IAuthService authService,
|
||||||
ILogger<FlashcardsController> logger) : base(logger, authService)
|
ILogger<FlashcardsController> logger) : base(logger, authService)
|
||||||
{
|
{
|
||||||
_flashcardRepository = flashcardRepository;
|
_flashcardRepository = flashcardRepository;
|
||||||
_reviewService = reviewService;
|
_reviewService = reviewService;
|
||||||
_context = context;
|
_context = context;
|
||||||
|
_imageStorageService = imageStorageService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string?> GetImageUrlAsync(string? relativePath)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(relativePath))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// 確保路徑包含 examples/ 前綴
|
||||||
|
var fullPath = relativePath.StartsWith("examples/")
|
||||||
|
? relativePath
|
||||||
|
: $"examples/{relativePath}";
|
||||||
|
|
||||||
|
return await _imageStorageService.GetImageUrlAsync(fullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
|
@ -47,11 +64,22 @@ public class FlashcardsController : BaseController
|
||||||
.Where(fr => fr.UserId == userId && flashcardIds.Contains(fr.FlashcardId))
|
.Where(fr => fr.UserId == userId && flashcardIds.Contains(fr.FlashcardId))
|
||||||
.ToDictionaryAsync(fr => fr.FlashcardId);
|
.ToDictionaryAsync(fr => fr.FlashcardId);
|
||||||
|
|
||||||
var flashcardData = new
|
// 重構為 foreach 迴圈,支援異步 URL 處理
|
||||||
|
var flashcardList = new List<object>();
|
||||||
|
|
||||||
|
foreach (var f in flashcards)
|
||||||
{
|
{
|
||||||
Flashcards = flashcards.Select(f => {
|
|
||||||
reviews.TryGetValue(f.Id, out var review);
|
reviews.TryGetValue(f.Id, out var review);
|
||||||
return new
|
|
||||||
|
// 取得主要圖片的相對路徑並轉換為完整 URL
|
||||||
|
var primaryImageRelativePath = f.FlashcardExampleImages
|
||||||
|
.Where(fei => fei.IsPrimary)
|
||||||
|
.Select(fei => fei.ExampleImage.RelativePath)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
var primaryImageUrl = await GetImageUrlAsync(primaryImageRelativePath);
|
||||||
|
|
||||||
|
flashcardList.Add(new
|
||||||
{
|
{
|
||||||
f.Id,
|
f.Id,
|
||||||
f.Word,
|
f.Word,
|
||||||
|
|
@ -73,12 +101,13 @@ public class FlashcardsController : BaseController
|
||||||
MasteryLevel = review?.SuccessCount ?? 0,
|
MasteryLevel = review?.SuccessCount ?? 0,
|
||||||
// 添加圖片相關屬性
|
// 添加圖片相關屬性
|
||||||
HasExampleImage = f.FlashcardExampleImages.Any(),
|
HasExampleImage = f.FlashcardExampleImages.Any(),
|
||||||
PrimaryImageUrl = f.FlashcardExampleImages
|
PrimaryImageUrl = primaryImageUrl
|
||||||
.Where(fei => fei.IsPrimary)
|
});
|
||||||
.Select(fei => $"/images/examples/{fei.ExampleImage.RelativePath}")
|
}
|
||||||
.FirstOrDefault()
|
|
||||||
};
|
var flashcardData = new
|
||||||
}),
|
{
|
||||||
|
Flashcards = flashcardList,
|
||||||
Count = flashcards.Count()
|
Count = flashcards.Count()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -181,10 +210,10 @@ public class FlashcardsController : BaseController
|
||||||
MasteryLevel = review?.SuccessCount ?? 0,
|
MasteryLevel = review?.SuccessCount ?? 0,
|
||||||
// 添加圖片相關屬性
|
// 添加圖片相關屬性
|
||||||
HasExampleImage = flashcard.FlashcardExampleImages.Any(),
|
HasExampleImage = flashcard.FlashcardExampleImages.Any(),
|
||||||
PrimaryImageUrl = flashcard.FlashcardExampleImages
|
PrimaryImageUrl = await GetImageUrlAsync(flashcard.FlashcardExampleImages
|
||||||
.Where(fei => fei.IsPrimary)
|
.Where(fei => fei.IsPrimary)
|
||||||
.Select(fei => $"/images/examples/{fei.ExampleImage.RelativePath}")
|
.Select(fei => fei.ExampleImage.RelativePath)
|
||||||
.FirstOrDefault(),
|
.FirstOrDefault()),
|
||||||
// 保留完整的圖片關聯數據供前端使用
|
// 保留完整的圖片關聯數據供前端使用
|
||||||
FlashcardExampleImages = flashcard.FlashcardExampleImages
|
FlashcardExampleImages = flashcard.FlashcardExampleImages
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue