修复发布后图片无法看到bug
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"hfb_sys/backend/internal/middleware"
|
||||
filemodule "hfb_sys/backend/internal/modules/file"
|
||||
"hfb_sys/backend/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -13,10 +14,11 @@ import (
|
||||
|
||||
type Handler struct {
|
||||
service *Service
|
||||
storage *filemodule.Storage
|
||||
}
|
||||
|
||||
func NewHandler(service *Service) *Handler {
|
||||
return &Handler{service: service}
|
||||
func NewHandler(service *Service, storage *filemodule.Storage) *Handler {
|
||||
return &Handler{service: service, storage: storage}
|
||||
}
|
||||
|
||||
func (h *Handler) Create(c *gin.Context) {
|
||||
@@ -215,6 +217,59 @@ func (h *Handler) FindPublic(c *gin.Context) {
|
||||
response.OK(c, item)
|
||||
}
|
||||
|
||||
func (h *Handler) Cover(c *gin.Context) {
|
||||
id, ok := parseID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
key, err := h.service.FindPublicCoverKey(id)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
}
|
||||
h.writePublicObject(c, key)
|
||||
}
|
||||
|
||||
func (h *Handler) Screenshot(c *gin.Context) {
|
||||
id, ok := parseID(c)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
index, err := strconv.Atoi(c.Param("index"))
|
||||
if err != nil || index < 0 {
|
||||
response.BadRequest(c, "截图序号不正确")
|
||||
return
|
||||
}
|
||||
key, err := h.service.FindPublicScreenshotKey(id, index)
|
||||
if err != nil {
|
||||
writeListingError(c, err)
|
||||
return
|
||||
}
|
||||
h.writePublicObject(c, key)
|
||||
}
|
||||
|
||||
func (h *Handler) writePublicObject(c *gin.Context, key string) {
|
||||
if h.storage == nil {
|
||||
response.ServiceUnavailable(c, "文件存储未连接")
|
||||
return
|
||||
}
|
||||
object, err := h.storage.Get(c.Request.Context(), key)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusNotFound, "not_found", "图片不存在或暂不可访问")
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = object.Reader.Close()
|
||||
}()
|
||||
contentType := object.ContentType
|
||||
if contentType == "" {
|
||||
contentType = "application/octet-stream"
|
||||
}
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Cache-Control", "public, max-age=300")
|
||||
c.DataFromReader(http.StatusOK, object.Size, contentType, object.Reader, nil)
|
||||
}
|
||||
|
||||
func (h *Handler) ListMine(c *gin.Context) {
|
||||
ownerID, ok := currentUserID(c)
|
||||
if !ok {
|
||||
|
||||
@@ -3,6 +3,8 @@ package listing
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -366,7 +368,7 @@ func (r *Repository) ListPublic() ([]ListingDTO, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rowsToDTO(rows), nil
|
||||
return publicListings(rowsToDTO(rows)), nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
||||
@@ -382,7 +384,33 @@ func (r *Repository) ListMine(ownerID uint64) ([]ListingDTO, error) {
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublic(id uint64) (*ListingDTO, error) {
|
||||
return r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ?", id, "published", "approved")
|
||||
dto, err := r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ?", id, "published", "approved")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
applyPublicListingURLs(dto)
|
||||
return dto, nil
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublicCoverKey(id uint64) (string, error) {
|
||||
return r.FindPublicScreenshotKey(id, 0)
|
||||
}
|
||||
|
||||
func (r *Repository) FindPublicScreenshotKey(id uint64, index int) (string, error) {
|
||||
if index < 0 {
|
||||
return "", gorm.ErrRecordNotFound
|
||||
}
|
||||
dto, err := r.findDTO("l.id = ? AND l.status = ? AND l.review_status = ?", id, "published", "approved")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if index >= len(dto.ScreenshotURLS) {
|
||||
return "", gorm.ErrRecordNotFound
|
||||
}
|
||||
if key := extractListingObjectKey(dto.ScreenshotURLS[index]); key != "" {
|
||||
return key, nil
|
||||
}
|
||||
return "", gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
func (r *Repository) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
@@ -456,6 +484,17 @@ func rowsToDTO(rows []listingRow) []ListingDTO {
|
||||
return items
|
||||
}
|
||||
|
||||
func publicListings(items []ListingDTO) []ListingDTO {
|
||||
for index := range items {
|
||||
applyPublicListingURLs(&items[index])
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func applyPublicListingURLs(item *ListingDTO) {
|
||||
item.ScreenshotURLS = publicScreenshotURLs(item.ID, item.ScreenshotURLS, item.Status, item.ReviewStatus)
|
||||
}
|
||||
|
||||
func (row listingRow) toDTO() ListingDTO {
|
||||
assetSummary := decodeAssetSummary(row.AssetSummary)
|
||||
screenshotURLS := cleanScreenshotURLs(decodeScreenshots(row.ScreenshotURLS))
|
||||
@@ -474,7 +513,7 @@ func (row listingRow) toDTO() ListingDTO {
|
||||
HafCoinAmount: row.HafCoinAmount,
|
||||
AssetSummary: assetSummary,
|
||||
ScreenshotURLS: screenshotURLS,
|
||||
CoverURL: firstScreenshotURL(screenshotURLS),
|
||||
CoverURL: publicCoverURL(row.ID, screenshotURLS, row.Status, row.ReviewStatus),
|
||||
PriceHourly: row.PriceHourly,
|
||||
PriceDaily: row.PriceDaily,
|
||||
PriceWeekly: row.PriceWeekly,
|
||||
@@ -504,7 +543,7 @@ func toDTO(account model.GameAccount, listing model.RentalListing) *ListingDTO {
|
||||
HafCoinAmount: account.HafCoinAmount,
|
||||
AssetSummary: assetSummary,
|
||||
ScreenshotURLS: screenshotURLS,
|
||||
CoverURL: firstScreenshotURL(screenshotURLS),
|
||||
CoverURL: publicCoverURL(listing.ID, screenshotURLS, listing.Status, listing.ReviewStatus),
|
||||
PriceHourly: listing.PriceHourly,
|
||||
PriceDaily: listing.PriceDaily,
|
||||
PriceWeekly: listing.PriceWeekly,
|
||||
@@ -587,6 +626,47 @@ func firstScreenshotURL(urls []string) string {
|
||||
return urls[0]
|
||||
}
|
||||
|
||||
func publicCoverURL(listingID uint64, urls []string, status string, reviewStatus string) string {
|
||||
fallback := firstScreenshotURL(urls)
|
||||
if status != "published" || reviewStatus != "approved" || extractListingObjectKey(fallback) == "" {
|
||||
return fallback
|
||||
}
|
||||
return "/api/listings/" + strconv.FormatUint(listingID, 10) + "/cover"
|
||||
}
|
||||
|
||||
func publicScreenshotURLs(listingID uint64, urls []string, status string, reviewStatus string) []string {
|
||||
if status != "published" || reviewStatus != "approved" {
|
||||
return urls
|
||||
}
|
||||
publicURLs := make([]string, 0, len(urls))
|
||||
for index, fileURL := range urls {
|
||||
if extractListingObjectKey(fileURL) == "" {
|
||||
publicURLs = append(publicURLs, fileURL)
|
||||
continue
|
||||
}
|
||||
publicURLs = append(publicURLs, "/api/listings/"+strconv.FormatUint(listingID, 10)+"/screenshots/"+strconv.Itoa(index))
|
||||
}
|
||||
return publicURLs
|
||||
}
|
||||
|
||||
func extractListingObjectKey(fileURL string) string {
|
||||
if fileURL == "" {
|
||||
return ""
|
||||
}
|
||||
parsed, err := url.Parse(fileURL)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
key := parsed.Query().Get("key")
|
||||
if key == "" {
|
||||
return ""
|
||||
}
|
||||
if !strings.HasPrefix(key, "listing/") || strings.Contains(key, "..") {
|
||||
return ""
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func appendAuditLog(tx *gorm.DB, actorID uint64, action string, bizType string, bizID uint64, meta AuditMeta, detail map[string]any) error {
|
||||
raw, err := json.Marshal(detail)
|
||||
if err != nil {
|
||||
|
||||
@@ -179,6 +179,20 @@ func (s *Service) FindPublic(id uint64) (*ListingDTO, error) {
|
||||
return s.repo.FindPublic(id)
|
||||
}
|
||||
|
||||
func (s *Service) FindPublicCoverKey(id uint64) (string, error) {
|
||||
if s.repo == nil {
|
||||
return "", ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindPublicCoverKey(id)
|
||||
}
|
||||
|
||||
func (s *Service) FindPublicScreenshotKey(id uint64, index int) (string, error) {
|
||||
if s.repo == nil {
|
||||
return "", ErrDependencyUnavailable
|
||||
}
|
||||
return s.repo.FindPublicScreenshotKey(id, index)
|
||||
}
|
||||
|
||||
func (s *Service) FindMine(ownerID uint64, id uint64) (*ListingDTO, error) {
|
||||
if s.repo == nil {
|
||||
return nil, ErrDependencyUnavailable
|
||||
|
||||
Reference in New Issue
Block a user