mirror of
https://github.com/Super-Badmen-Viper/NSMusicS
synced 2025-04-29 16:17:28 +08:00
update fix NineSong Scene_Audio_Music api
This commit is contained in:
parent
0f2f583470
commit
ba0dd943c2
2
.gitignore
vendored
2
.gitignore
vendored
@ -39,4 +39,4 @@ NSMusicS-Electron/resources/mpv-x86_64-20241124
|
||||
NSMusicS-Electron/resources/mpv-0.39.0
|
||||
NSMusicS-Electron/src_example
|
||||
|
||||
NSMusicS-Go/tmp/main.exe
|
||||
NineSong/tmp/main.exe
|
@ -7,11 +7,11 @@
|
||||
APP_ENV=development
|
||||
|
||||
# ===== 容器命名配置 | Container naming configuration =====
|
||||
APP_CONTAINER_NAME=nsmusics # 前端容器名称(可修改)
|
||||
APP_CONTAINER_NAME=ninesong # 前端容器名称(可修改)
|
||||
# Front-end container name (modifiable)
|
||||
WEB_CONTAINER_NAME=nsmusics-go # 后端容器名称(可修改): 请保持WEB_CONTAINER_NAME与BACKEND_SERVICE中域名一致
|
||||
WEB_CONTAINER_NAME=ninesong-go # 后端容器名称(可修改): 请保持WEB_CONTAINER_NAME与BACKEND_SERVICE中域名一致
|
||||
# Back-end container name (modifiable)
|
||||
MONGO_CONTAINER_NAME=nsmusics-mongodb # 数据库容器名称(可修改)
|
||||
MONGO_CONTAINER_NAME=ninesong-mongodb # 数据库容器名称(可修改)
|
||||
# Database container name (modifiable)
|
||||
|
||||
# ===== 端口配置 | port configuration =====
|
||||
@ -21,7 +21,7 @@ SERVER_PORT=8082 # 后端端口(可修改): 请
|
||||
# Back-end port (modifiable): please keep SERVER_PORT consistent with SERVER_ADDRESS
|
||||
SERVER_ADDRESS=:8082 # 后端地址(可修改): 请保持SERVER_PORT与SERVER_ADDRESS一致
|
||||
# Back-end address (modifiable): please keep SERVER_PORT consistent with SERVER_ADDRESS
|
||||
BACKEND_SERVICE=http://nsmusics-go:8082 # 前端请求后端地址(可修改)
|
||||
BACKEND_SERVICE=http://ninesong-go:8082 # 前端请求后端地址(可修改)
|
||||
# Front-end request back-end address (modifiable)
|
||||
CONTEXT_TIMEOUT=10
|
||||
|
||||
@ -34,7 +34,7 @@ DB_USER=jiuge01 # 数据库用户名(可修改):此处请设置高
|
||||
# Database user name (modifiable): please set high-strength fields here. Simple fields or default fields are vulnerable to attack
|
||||
DB_PASS=jiuge01 # 数据库用户密码(可修改):此处请设置高强度字段,简单字段或默认字段容易遭受攻击
|
||||
# Database user password (modifiable): please set high-strength fields here. Simple fields or default fields are vulnerable to attack
|
||||
DB_NAME=nsmusics-go-db
|
||||
DB_NAME=ninesong-go-db
|
||||
|
||||
LIBRARY_PATH=/data/library
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package controller_file_entity
|
||||
package scene_audio_db_api_controller
|
||||
|
||||
import (
|
||||
"context"
|
@ -0,0 +1,70 @@
|
||||
package scene_audio_route_api_controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AlbumController struct {
|
||||
AlbumUsecase scene_audio_route_interface.AlbumRepository
|
||||
}
|
||||
|
||||
func NewAlbumController(uc scene_audio_route_interface.AlbumRepository) *AlbumController {
|
||||
return &AlbumController{AlbumUsecase: uc}
|
||||
}
|
||||
|
||||
func (ac *AlbumController) GetAlbumItems(c *gin.Context) {
|
||||
params := struct {
|
||||
Start string `form:"start"`
|
||||
End string `form:"end"`
|
||||
Sort string `form:"sort"`
|
||||
Order string `form:"order"`
|
||||
Search string `form:"search"`
|
||||
Starred string `form:"starred"`
|
||||
ArtistID string `form:"artist_id"`
|
||||
}{
|
||||
Start: c.DefaultQuery("start", "0"),
|
||||
End: c.DefaultQuery("end", "50"),
|
||||
Sort: c.DefaultQuery("sort", "name"),
|
||||
Order: c.DefaultQuery("order", "asc"),
|
||||
Search: c.Query("search"),
|
||||
Starred: c.Query("starred"),
|
||||
ArtistID: c.Query("artist_id"),
|
||||
}
|
||||
|
||||
albums, err := ac.AlbumUsecase.GetAlbumItems(
|
||||
c.Request.Context(),
|
||||
params.End,
|
||||
params.Order,
|
||||
params.Sort,
|
||||
params.Start,
|
||||
params.Search,
|
||||
params.Starred,
|
||||
params.ArtistID,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
handleAlbumError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, scene_audio_route_models.AlbumListResponse{
|
||||
Albums: albums,
|
||||
Count: len(albums),
|
||||
})
|
||||
}
|
||||
|
||||
func handleAlbumError(c *gin.Context, err error) {
|
||||
switch err.Error() {
|
||||
case "invalid start parameter",
|
||||
"invalid end parameter",
|
||||
"invalid starred parameter",
|
||||
"invalid artist id format":
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package scene_audio_route_api_controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ArtistController struct {
|
||||
ArtistUsecase scene_audio_route_interface.ArtistRepository
|
||||
}
|
||||
|
||||
func NewArtistController(uc scene_audio_route_interface.ArtistRepository) *ArtistController {
|
||||
return &ArtistController{ArtistUsecase: uc}
|
||||
}
|
||||
|
||||
func (c *ArtistController) GetArtists(ctx *gin.Context) {
|
||||
params := struct {
|
||||
Start string `form:"start"`
|
||||
End string `form:"end"`
|
||||
Sort string `form:"sort"`
|
||||
Order string `form:"order"`
|
||||
Search string `form:"search"`
|
||||
Starred string `form:"starred"`
|
||||
}{
|
||||
Start: ctx.DefaultQuery("start", "0"),
|
||||
End: ctx.DefaultQuery("end", "50"),
|
||||
Sort: ctx.DefaultQuery("sort", "name"),
|
||||
Order: ctx.DefaultQuery("order", "asc"),
|
||||
Search: ctx.Query("search"),
|
||||
Starred: ctx.Query("starred"),
|
||||
}
|
||||
|
||||
artists, err := c.ArtistUsecase.GetArtistItems(
|
||||
ctx.Request.Context(),
|
||||
params.End,
|
||||
params.Order,
|
||||
params.Sort,
|
||||
params.Start,
|
||||
params.Search,
|
||||
params.Starred,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, scene_audio_route_models.ArtistListResponse{
|
||||
Artists: artists,
|
||||
Count: len(artists),
|
||||
})
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package scene_audio_route_api_controller
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MediaFileController struct {
|
||||
MediaFileUsecase scene_audio_route_interface.MediaFileRepository
|
||||
}
|
||||
|
||||
func NewMediaFileController(uc scene_audio_route_interface.MediaFileRepository) *MediaFileController {
|
||||
return &MediaFileController{MediaFileUsecase: uc}
|
||||
}
|
||||
|
||||
func (c *MediaFileController) GetMediaFiles(ctx *gin.Context) {
|
||||
params := struct {
|
||||
Start string `form:"start"`
|
||||
End string `form:"end"`
|
||||
Sort string `form:"sort"`
|
||||
Order string `form:"order"`
|
||||
Search string `form:"search"`
|
||||
Starred string `form:"starred"`
|
||||
AlbumID string `form:"album_id"`
|
||||
ArtistID string `form:"artist_id"`
|
||||
Year string `form:"year"`
|
||||
}{
|
||||
Start: ctx.DefaultQuery("start", "0"),
|
||||
End: ctx.DefaultQuery("end", "100"),
|
||||
Sort: ctx.DefaultQuery("sort", "title"),
|
||||
Order: ctx.DefaultQuery("order", "asc"),
|
||||
Search: ctx.Query("search"),
|
||||
Starred: ctx.Query("starred"),
|
||||
AlbumID: ctx.Query("album_id"),
|
||||
ArtistID: ctx.Query("artist_id"),
|
||||
Year: ctx.Query("year"),
|
||||
}
|
||||
|
||||
mediaFiles, err := c.MediaFileUsecase.GetMediaFileItems(
|
||||
ctx.Request.Context(),
|
||||
params.End,
|
||||
params.Order,
|
||||
params.Sort,
|
||||
params.Start,
|
||||
params.Search,
|
||||
params.Starred,
|
||||
params.AlbumID,
|
||||
params.ArtistID,
|
||||
params.Year,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
handleMediaFileError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, scene_audio_route_models.MediaFileListResponse{
|
||||
MediaFiles: mediaFiles,
|
||||
Count: len(mediaFiles),
|
||||
})
|
||||
}
|
||||
|
||||
func handleMediaFileError(ctx *gin.Context, err error) {
|
||||
switch err.Error() {
|
||||
case "invalid start parameter",
|
||||
"invalid end parameter",
|
||||
"invalid starred parameter",
|
||||
"invalid album id format",
|
||||
"invalid artist id format",
|
||||
"year must be integer":
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
default:
|
||||
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "服务器内部错误"})
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package scene_audio_route_api_controller
|
||||
|
||||
import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PlaylistTrackController struct {
|
||||
PlaylistTrackUsecase scene_audio_route_interface.PlaylistTrackRepository
|
||||
}
|
||||
|
||||
func NewPlaylistTrackController(uc scene_audio_route_interface.PlaylistTrackRepository) *PlaylistTrackController {
|
||||
return &PlaylistTrackController{PlaylistTrackUsecase: uc}
|
||||
}
|
||||
|
||||
func (c *PlaylistTrackController) GetPlaylistTracks(ctx *gin.Context) {
|
||||
params := struct {
|
||||
Start string `form:"start"`
|
||||
End string `form:"end"`
|
||||
Sort string `form:"sort"`
|
||||
Order string `form:"order"`
|
||||
Search string `form:"search"`
|
||||
Starred string `form:"starred"`
|
||||
AlbumId string `form:"albumId"`
|
||||
ArtistId string `form:"artistId"`
|
||||
Year string `form:"year"`
|
||||
PlaylistId string `form:"playlistId"`
|
||||
}{
|
||||
Start: ctx.DefaultQuery("start", "0"),
|
||||
End: ctx.DefaultQuery("end", "50"),
|
||||
Sort: ctx.DefaultQuery("sort", "created_at"),
|
||||
Order: ctx.DefaultQuery("order", "asc"),
|
||||
Search: ctx.Query("search"),
|
||||
Starred: ctx.Query("starred"),
|
||||
AlbumId: ctx.Query("albumId"),
|
||||
ArtistId: ctx.Query("artistId"),
|
||||
Year: ctx.Query("year"),
|
||||
PlaylistId: ctx.Param("playlistId"),
|
||||
}
|
||||
|
||||
tracks, err := c.PlaylistTrackUsecase.GetPlaylistTrackItems(
|
||||
ctx.Request.Context(),
|
||||
params.End,
|
||||
params.Order,
|
||||
params.Sort,
|
||||
params.Start,
|
||||
params.Search,
|
||||
params.Starred,
|
||||
params.AlbumId,
|
||||
params.ArtistId,
|
||||
params.Year,
|
||||
params.PlaylistId,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, scene_audio_route_models.PlaylistTrackListResponse{
|
||||
PlaylistTracks: tracks,
|
||||
Count: len(tracks),
|
||||
})
|
||||
}
|
@ -4,7 +4,8 @@ import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_app/route_app_config"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_app/route_app_library"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_auth"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_file_entity"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_file_entity/scene_audio_db_api_route"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_file_entity/scene_audio_route_api_route"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/route/route_system"
|
||||
"time"
|
||||
|
||||
@ -50,5 +51,7 @@ func RouterPrivate(env *bootstrap.Env, timeout time.Duration, db mongo.Database,
|
||||
// app library
|
||||
route_app_library.NewAppMediaFileLibraryRouter(timeout, db, protectedRouter)
|
||||
// file entity
|
||||
route_file_entity.NewFileEntityRouter(timeout, db, protectedRouter)
|
||||
scene_audio_db_api_route.NewFileEntityRouter(timeout, db, protectedRouter)
|
||||
// scene audio
|
||||
scene_audio_route_api_route.NewArtistRouter(env, timeout, db, protectedRouter)
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
package route_file_entity
|
||||
package scene_audio_db_api_route
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity/scene_audio_db_api_controller"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
@ -35,7 +35,7 @@ func NewFileEntityRouter(timeout time.Duration, db mongo.Database, group *gin.Ro
|
||||
)
|
||||
|
||||
// 注册控制器
|
||||
ctrl := controller_file_entity.NewFileController(uc)
|
||||
ctrl := scene_audio_db_api_controller.NewFileController(uc)
|
||||
|
||||
// 路由配置
|
||||
group.Use(requestLogger())
|
@ -0,0 +1,32 @@
|
||||
package scene_audio_route_api_route
|
||||
|
||||
import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity/scene_audio_route_api_controller"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/repository/repository_file_entity/scene_audio/scene_audio_route_repository"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/usecase/usecase_file_entity/scene_audio/scene_audio_route_usecase"
|
||||
"time"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/bootstrap"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewAlbumRouter(
|
||||
env *bootstrap.Env,
|
||||
timeout time.Duration,
|
||||
db mongo.Database,
|
||||
group *gin.RouterGroup,
|
||||
) {
|
||||
repo := scene_audio_route_repository.NewAlbumRepository(db, domain.CollectionFileEntityAudioAlbum)
|
||||
|
||||
usecase := scene_audio_route_usecase.NewAlbumUsecase(repo, timeout)
|
||||
ctrl := scene_audio_route_api_controller.NewAlbumController(usecase)
|
||||
|
||||
albumGroup := group.Group("/albums")
|
||||
{
|
||||
albumGroup.GET("", ctrl.GetAlbumItems)
|
||||
// 可扩展其他路由
|
||||
// albumGroup.GET("/:id", ctrl.GetAlbumDetail)
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package scene_audio_route_api_route
|
||||
|
||||
import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity/scene_audio_route_api_controller"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/repository/repository_file_entity/scene_audio/scene_audio_route_repository"
|
||||
"time"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/bootstrap"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewArtistRouter(
|
||||
env *bootstrap.Env,
|
||||
timeout time.Duration,
|
||||
db mongo.Database,
|
||||
group *gin.RouterGroup,
|
||||
) {
|
||||
repo := scene_audio_route_repository.NewArtistRepository(db, domain.CollectionFileEntityAudioArtist)
|
||||
|
||||
ctrl := scene_audio_route_api_controller.NewArtistController(repo)
|
||||
|
||||
artistGroup := group.Group("/artists")
|
||||
{
|
||||
artistGroup.GET("", ctrl.GetArtists)
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package scene_audio_route_api_route
|
||||
|
||||
import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity/scene_audio_route_api_controller"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/repository/repository_file_entity/scene_audio/scene_audio_route_repository"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/usecase/usecase_file_entity/scene_audio/scene_audio_route_usecase"
|
||||
"time"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/bootstrap"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func NewMediaFileRouter(
|
||||
env *bootstrap.Env,
|
||||
timeout time.Duration,
|
||||
db mongo.Database,
|
||||
group *gin.RouterGroup,
|
||||
) {
|
||||
repo := scene_audio_route_repository.NewMediaFileRepository(db, domain.CollectionFileEntityAudioMediaFile)
|
||||
|
||||
usecase := scene_audio_route_usecase.NewMediaFileUsecase(repo, timeout)
|
||||
ctrl := scene_audio_route_api_controller.NewMediaFileController(usecase)
|
||||
|
||||
mediaGroup := group.Group("/mediafiles")
|
||||
{
|
||||
mediaGroup.GET("", ctrl.GetMediaFiles)
|
||||
// 可扩展路由示例
|
||||
// mediaGroup.GET("/:id", ctrl.GetMediaFileDetail)
|
||||
// mediaGroup.PUT("/:id/star", ctrl.StarMediaFile)
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
package scene_audio_route_api_route
|
||||
|
||||
import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/api/controller/controller_file_entity/scene_audio_route_api_controller"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/bootstrap"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/repository/repository_file_entity/scene_audio/scene_audio_route_repository"
|
||||
"github.com/gin-gonic/gin"
|
||||
"time"
|
||||
)
|
||||
|
||||
func NewPlaylistTrackRouter(
|
||||
env *bootstrap.Env,
|
||||
timeout time.Duration,
|
||||
db mongo.Database,
|
||||
group *gin.RouterGroup,
|
||||
) {
|
||||
repo := scene_audio_route_repository.NewPlaylistTrackRepository(db, domain.CollectionFileEntityPlaylistTrack)
|
||||
|
||||
ctrl := scene_audio_route_api_controller.NewPlaylistTrackController(repo)
|
||||
|
||||
playlistTrackGroup := group.Group("/playlists/:playlistId/tracks")
|
||||
{
|
||||
playlistTrackGroup.GET("", ctrl.GetPlaylistTracks)
|
||||
}
|
||||
}
|
@ -107,6 +107,14 @@ func (si *Initializer) executeInitialization(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := si.initFileEntityAudioPlaylist(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := si.initFileEntityAudioPlaylistTrack(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return si.markInitialized(ctx)
|
||||
}
|
||||
|
||||
@ -898,3 +906,40 @@ func (si *Initializer) initFileEntityAudioAnnotation(ctx context.Context) error
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (si *Initializer) initFileEntityAudioPlaylist(ctx context.Context) error {
|
||||
coll := si.db.Collection(domain.CollectionFileEntityPlaylist)
|
||||
|
||||
dummyID := primitive.NewObjectID()
|
||||
emptyDoc := &scene_audio_db_models.PlaylistMetadata{
|
||||
ID: dummyID,
|
||||
}
|
||||
|
||||
if _, err := coll.InsertOne(ctx, emptyDoc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := coll.DeleteOne(ctx, bson.M{"_id": dummyID}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (si *Initializer) initFileEntityAudioPlaylistTrack(ctx context.Context) error {
|
||||
coll := si.db.Collection(domain.CollectionFileEntityPlaylistTrack)
|
||||
|
||||
emptyDoc := &scene_audio_db_models.PlaylistTrackMetadata{
|
||||
ID: 0,
|
||||
}
|
||||
|
||||
if _, err := coll.InsertOne(ctx, emptyDoc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := coll.DeleteOne(ctx, bson.M{"_id": 0}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -56,3 +56,9 @@ const (
|
||||
const (
|
||||
CollectionFileEntityAnnotation = "file_entity_audio_annotation"
|
||||
)
|
||||
const (
|
||||
CollectionFileEntityPlaylist = "file_entity_audio_playlist"
|
||||
)
|
||||
const (
|
||||
CollectionFileEntityPlaylistTrack = "file_entity_audio_playlist_track"
|
||||
)
|
||||
|
@ -30,3 +30,8 @@ type AlbumMetadata struct {
|
||||
AllArtistIDs string `bson:"all_artist_ids"`
|
||||
ImageFiles string `bson:"image_files"` // 为空则不存在cover封面,从媒体文件中提取
|
||||
}
|
||||
|
||||
type AlbumListResponse struct {
|
||||
Albums []AlbumMetadata `json:"albums"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
@ -21,3 +21,8 @@ type ArtistMetadata struct {
|
||||
|
||||
ImageFiles string `bson:"image_files"` // 为空则不存在cover封面,从媒体文件中提取
|
||||
}
|
||||
|
||||
type ArtistListResponse struct {
|
||||
Artists []ArtistMetadata `json:"artists"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
@ -34,22 +34,7 @@ type MediaFileMetadata struct {
|
||||
Channels int `bson:"channels"`
|
||||
}
|
||||
|
||||
// FilterParams 基础过滤参数
|
||||
type FilterParams struct {
|
||||
Genres []string
|
||||
MinYear int
|
||||
MaxYear int
|
||||
ArtistIDs []string
|
||||
MinDuration float64
|
||||
MaxDuration float64
|
||||
AlbumTypes []string
|
||||
HasLyrics *bool
|
||||
BitrateRange [2]int
|
||||
}
|
||||
|
||||
// SortParams 排序参数
|
||||
type SortParams struct {
|
||||
Field string // 支持字段:title, artist, year, duration, bit_rate 等
|
||||
Ascending bool
|
||||
Collation string // 排序规则(如:nocase)
|
||||
type MediaFileListResponse struct {
|
||||
MediaFiles []MediaFileMetadata `json:"media_files"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
@ -10,3 +10,8 @@ type PlaylistTrackMetadata struct {
|
||||
AlbumID primitive.ObjectID `bson:"album_id"`
|
||||
ArtistID primitive.ObjectID `bson:"artist_id"`
|
||||
}
|
||||
|
||||
type PlaylistTrackListResponse struct {
|
||||
PlaylistTracks []PlaylistTrackMetadata `json:"playlist_tracks"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
@ -2,18 +2,14 @@ package scene_audio_route_repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_db/scene_audio_db_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type albumRepository struct {
|
||||
@ -28,110 +24,63 @@ func NewAlbumRepository(db mongo.Database, collection string) scene_audio_route_
|
||||
}
|
||||
}
|
||||
|
||||
func (a albumRepository) GetAlbumItems(
|
||||
func (r *albumRepository) GetAlbumItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred, artistId string,
|
||||
) ([]scene_audio_route_models.AlbumMetadata, error) {
|
||||
collection := a.db.Collection(a.collection)
|
||||
collection := r.db.Collection(r.collection)
|
||||
|
||||
// 构建复合查询条件
|
||||
filter := bson.M{}
|
||||
|
||||
// 处理艺术家ID过滤
|
||||
if artistId != "" {
|
||||
filter["artist_id"] = artistId
|
||||
}
|
||||
|
||||
// 处理搜索条件
|
||||
if search != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"name": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"artist": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"name": primitive.Regex{Pattern: search, Options: "i"}},
|
||||
{"artist": primitive.Regex{Pattern: search, Options: "i"}},
|
||||
}
|
||||
}
|
||||
|
||||
// 处理收藏过滤
|
||||
if starred != "" {
|
||||
isStarred, err := strconv.ParseBool(starred)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid starred parameter")
|
||||
return nil, fmt.Errorf("invalid starred parameter: %w", err)
|
||||
}
|
||||
filter["starred"] = isStarred
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
skip, limit := getPagination(start, end)
|
||||
|
||||
// 排序处理
|
||||
sortOption := getSortOption_Album(sort, order)
|
||||
|
||||
// 执行查询
|
||||
findOptions := options.Find().
|
||||
SetSort(sortOption).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, findOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %v", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
// 使用组合结构体解码数据库模型
|
||||
var results []struct {
|
||||
scene_audio_db_models.AlbumMetadata `bson:",inline"`
|
||||
MergedImageURL string `bson:"-"`
|
||||
skip, _ := strconv.Atoi(start)
|
||||
limit, _ := strconv.Atoi(end)
|
||||
if limit == 0 || limit > 100 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode results: %v", err)
|
||||
validSortFields := map[string]bool{"name": true, "song_count": true, "created_at": true}
|
||||
if !validSortFields[sort] {
|
||||
sort = "name"
|
||||
}
|
||||
|
||||
// 转换为路由模型
|
||||
routeResults := make([]scene_audio_route_models.AlbumMetadata, len(results))
|
||||
for i, item := range results {
|
||||
routeResults[i] = scene_audio_route_models.AlbumMetadata{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
ArtistID: item.ArtistID,
|
||||
Artist: item.Artist,
|
||||
AlbumArtist: item.AlbumArtist,
|
||||
MinYear: item.MinYear,
|
||||
MaxYear: item.MaxYear,
|
||||
SongCount: item.SongCount,
|
||||
Duration: item.Duration,
|
||||
Genre: item.Genre,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
AlbumArtistID: item.AlbumArtistID,
|
||||
Comment: item.Comment,
|
||||
AllArtistIDs: item.AllArtistIDs,
|
||||
ImageFiles: "", // 待填充Music场景Metadata查找资源逻辑
|
||||
PlayCount: 0, // 待填充Annotation交互逻辑
|
||||
Rating: 0, // 待填充Annotation交互逻辑
|
||||
Starred: false, // 待填充Annotation交互逻辑
|
||||
StarredAt: time.Time{}, // 待填充Annotation交互逻辑
|
||||
}
|
||||
}
|
||||
|
||||
if routeResults == nil {
|
||||
return []scene_audio_route_models.AlbumMetadata{}, nil
|
||||
}
|
||||
|
||||
return routeResults, nil
|
||||
}
|
||||
|
||||
// 排序参数处理
|
||||
func getSortOption_Album(sortField, order string) bson.D {
|
||||
sortOrder := 1 // 默认升序
|
||||
sortOrder := 1
|
||||
if order == "desc" {
|
||||
sortOrder = -1
|
||||
}
|
||||
|
||||
// 设置默认排序字段
|
||||
if sortField == "" {
|
||||
sortField = "name"
|
||||
opts := options.Find().
|
||||
SetSort(bson.D{{Key: sort, Value: sortOrder}}).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %w", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var results []scene_audio_route_models.AlbumMetadata
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
return bson.D{{Key: sortField, Value: sortOrder}}
|
||||
return results, nil
|
||||
}
|
||||
|
@ -2,17 +2,13 @@ package scene_audio_route_repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_db/scene_audio_db_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type artistRepository struct {
|
||||
@ -27,107 +23,52 @@ func NewArtistRepository(db mongo.Database, collection string) scene_audio_route
|
||||
}
|
||||
}
|
||||
|
||||
func (a artistRepository) GetArtistItems(
|
||||
func (r *artistRepository) GetArtistItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred string,
|
||||
) ([]scene_audio_route_models.ArtistMetadata, error) {
|
||||
collection := a.db.Collection(a.collection)
|
||||
collection := r.db.Collection(r.collection)
|
||||
|
||||
// 构建查询条件
|
||||
filter := bson.M{}
|
||||
|
||||
// 处理搜索条件
|
||||
if search != "" {
|
||||
filter["name"] = bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}
|
||||
filter["name"] = bson.M{"$regex": search, "$options": "i"}
|
||||
}
|
||||
|
||||
// 处理收藏过滤
|
||||
if starred != "" {
|
||||
isStarred, err := strconv.ParseBool(starred)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid starred parameter")
|
||||
return nil, fmt.Errorf("invalid starred parameter: %w", err)
|
||||
}
|
||||
filter["starred"] = isStarred
|
||||
}
|
||||
|
||||
// 处理分页
|
||||
skip, limit := getPagination(start, end)
|
||||
skip, _ := strconv.Atoi(start)
|
||||
limit, _ := strconv.Atoi(end)
|
||||
if limit == 0 || limit > 100 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
// 构建排序
|
||||
sortOption := getSortOption_Artist(sort, order)
|
||||
|
||||
// 执行查询
|
||||
findOptions := options.Find().
|
||||
SetSort(sortOption).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, findOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %v", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var results []struct {
|
||||
scene_audio_db_models.ArtistMetadata `bson:",inline"`
|
||||
MergedImageURL string `bson:"-"` // 用于接收合并后的值
|
||||
}
|
||||
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode results: %v", err)
|
||||
}
|
||||
|
||||
// 转换为目标结构
|
||||
routeResults := make([]scene_audio_route_models.ArtistMetadata, len(results))
|
||||
for i, item := range results {
|
||||
routeResults[i] = scene_audio_route_models.ArtistMetadata{
|
||||
ID: item.ID,
|
||||
Name: item.Name,
|
||||
AlbumCount: item.AlbumCount,
|
||||
SongCount: item.SongCount,
|
||||
Size: item.Size,
|
||||
ImageFiles: "", // 待填充Music场景Metadata查找资源逻辑
|
||||
PlayCount: 0, // 待填充Annotation交互逻辑
|
||||
Rating: 0, // 待填充Annotation交互逻辑
|
||||
Starred: false, // 待填充Annotation交互逻辑
|
||||
StarredAt: time.Time{}, // 待填充Annotation交互逻辑
|
||||
}
|
||||
}
|
||||
|
||||
// 处理空结果
|
||||
if routeResults == nil {
|
||||
return []scene_audio_route_models.ArtistMetadata{}, nil
|
||||
}
|
||||
|
||||
return routeResults, nil
|
||||
}
|
||||
|
||||
// 分页参数处理
|
||||
func getPagination(start, end string) (skip int, limit int) {
|
||||
skip, _ = strconv.Atoi(start)
|
||||
limit, _ = strconv.Atoi(end)
|
||||
|
||||
// 设置默认值
|
||||
if limit == 0 {
|
||||
limit = 50 // 默认每页20条
|
||||
}
|
||||
if skip < 0 {
|
||||
skip = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 排序参数处理
|
||||
func getSortOption_Artist(sortField, order string) bson.D {
|
||||
sortOrder := 1 // 默认升序
|
||||
sortOrder := 1
|
||||
if order == "desc" {
|
||||
sortOrder = -1
|
||||
}
|
||||
opts := options.Find().
|
||||
SetSort(bson.D{{Key: sort, Value: sortOrder}}).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
// 设置默认排序字段
|
||||
if sortField == "" {
|
||||
sortField = "name"
|
||||
cursor, err := collection.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %w", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var results []scene_audio_route_models.ArtistMetadata
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
return bson.D{{Key: sortField, Value: sortOrder}}
|
||||
return results, nil
|
||||
}
|
||||
|
@ -3,18 +3,13 @@ package scene_audio_route_repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_db/scene_audio_db_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type mediaFileRepository struct {
|
||||
@ -29,131 +24,101 @@ func NewMediaFileRepository(db mongo.Database, collection string) scene_audio_ro
|
||||
}
|
||||
}
|
||||
|
||||
func (m mediaFileRepository) GetMediaFileItems(
|
||||
func (r *mediaFileRepository) GetMediaFileItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred, albumId, artistId, year string,
|
||||
) ([]scene_audio_route_models.MediaFileMetadata, error) {
|
||||
collection := m.db.Collection(m.collection)
|
||||
collection := r.db.Collection(r.collection)
|
||||
|
||||
// 构建复合查询条件
|
||||
filter := bson.M{}
|
||||
|
||||
// 处理艺术家ID过滤
|
||||
if artistId != "" {
|
||||
filter["artist_id"] = artistId
|
||||
}
|
||||
|
||||
// 处理专辑ID过滤
|
||||
if albumId != "" {
|
||||
filter["album_id"] = albumId
|
||||
}
|
||||
|
||||
// 处理年份过滤
|
||||
if year != "" {
|
||||
yearInt, err := strconv.Atoi(year)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid year format")
|
||||
if yearInt, err := strconv.Atoi(year); err == nil {
|
||||
filter["year"] = yearInt
|
||||
}
|
||||
filter["year"] = yearInt
|
||||
}
|
||||
|
||||
// 处理多字段搜索(标题、艺术家、专辑)
|
||||
if search != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"title": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"artist": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"album": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"title": bson.M{"$regex": search, "$options": "i"}},
|
||||
{"artist": bson.M{"$regex": search, "$options": "i"}},
|
||||
{"album": bson.M{"$regex": search, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
|
||||
// 处理收藏状态
|
||||
if starred != "" {
|
||||
isStarred, err := strconv.ParseBool(starred)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid starred parameter")
|
||||
return nil, fmt.Errorf("invalid starred parameter: %w", err)
|
||||
}
|
||||
filter["starred"] = isStarred
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
skip, limit := getPagination(start, end)
|
||||
|
||||
// 排序处理(支持nocase排序规则)
|
||||
sortOption := getSortOption_MediaFile(sort, order)
|
||||
|
||||
// 执行查询
|
||||
findOptions := options.Find().
|
||||
SetSort(sortOption).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, findOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %v", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
// 使用组合结构体解码数据库模型
|
||||
var results []struct {
|
||||
scene_audio_db_models.MediaFileMetadata `bson:",inline"`
|
||||
MergedImageURL string `bson:"-"`
|
||||
// 处理分页
|
||||
skip, _ := strconv.Atoi(start)
|
||||
limit, _ := strconv.Atoi(end)
|
||||
if limit == 0 || limit > 100 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode results: %v", err)
|
||||
// 安全排序字段白名单
|
||||
validSortFields := map[string]bool{
|
||||
"title": true, "artist": true,
|
||||
"year": true, "duration": true,
|
||||
}
|
||||
if !validSortFields[sort] {
|
||||
sort = "title"
|
||||
}
|
||||
|
||||
// 转换为路由模型
|
||||
routeResults := make([]scene_audio_route_models.MediaFileMetadata, len(results))
|
||||
for i, item := range results {
|
||||
routeResults[i] = scene_audio_route_models.MediaFileMetadata{
|
||||
ID: item.ID,
|
||||
Path: item.Path,
|
||||
Title: item.Title,
|
||||
Album: item.Album,
|
||||
Artist: item.Artist,
|
||||
ArtistID: item.ArtistID,
|
||||
AlbumArtist: item.AlbumArtist,
|
||||
AlbumID: item.AlbumID,
|
||||
HasCoverArt: item.HasCoverArt,
|
||||
Year: item.Year,
|
||||
Size: item.Size,
|
||||
Suffix: item.Suffix,
|
||||
Duration: item.Duration,
|
||||
BitRate: item.BitRate,
|
||||
Genre: item.Genre,
|
||||
CreatedAt: item.CreatedAt,
|
||||
UpdatedAt: item.UpdatedAt,
|
||||
AlbumArtistID: item.AlbumArtistID,
|
||||
Channels: item.Channels,
|
||||
PlayCount: 0, // 待填充Annotation交互逻辑
|
||||
Rating: 0, // 待填充Annotation交互逻辑
|
||||
Starred: false, // 待填充Annotation交互逻辑
|
||||
StarredAt: time.Time{}, // 待填充Annotation交互逻辑
|
||||
}
|
||||
}
|
||||
|
||||
if routeResults == nil {
|
||||
return []scene_audio_route_models.MediaFileMetadata{}, nil
|
||||
}
|
||||
|
||||
return routeResults, nil
|
||||
}
|
||||
|
||||
// 常规排序处理(仅字段顺序)
|
||||
func getSortOption_MediaFile(sortField, order string) bson.D {
|
||||
// 构建排序
|
||||
sortOrder := 1
|
||||
if order == "desc" {
|
||||
sortOrder = -1
|
||||
}
|
||||
opts := options.Find().
|
||||
SetSort(bson.D{{Key: sort, Value: sortOrder}}).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
// 设置默认排序字段
|
||||
if sortField == "" {
|
||||
sortField = "title" // 保持原默认字段
|
||||
cursor, err := collection.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %w", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var results []scene_audio_route_models.MediaFileMetadata
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
// 简化后的排序条件
|
||||
return bson.D{
|
||||
{Key: sortField, Value: sortOrder},
|
||||
// 后处理逻辑
|
||||
for i := range results {
|
||||
if !results[i].HasCoverArt {
|
||||
results[i].HasCoverArt = checkFallbackCover(results[i].AlbumID)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func checkFallbackCover(albumID string) bool {
|
||||
// 实现1: 简单校验专辑ID格式
|
||||
if _, err := strconv.Atoi(albumID); err == nil {
|
||||
return true // 假设有效ID即有封面
|
||||
}
|
||||
return false
|
||||
|
||||
// 实现2: 实际数据库查询(需注入collection)
|
||||
/*
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
var result struct{ HasCover bool }
|
||||
err := collection.FindOne(ctx, bson.M{"album_id": albumID}).Decode(&result)
|
||||
return err == nil && result.HasCover
|
||||
*/
|
||||
}
|
||||
|
@ -3,14 +3,11 @@ package scene_audio_route_repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_db/scene_audio_db_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_interface"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/mongo"
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
"go.mongodb.org/mongo-driver/mongo/options"
|
||||
"strconv"
|
||||
)
|
||||
@ -27,100 +24,61 @@ func NewPlaylistTrackRepository(db mongo.Database, collection string) scene_audi
|
||||
}
|
||||
}
|
||||
|
||||
func (p playlistTrackRepository) GetPlaylistTrackItems(
|
||||
func (r *playlistTrackRepository) GetPlaylistTrackItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred, albumId, artistId, year, playlistId string,
|
||||
) ([]scene_audio_route_models.PlaylistTrackMetadata, error) {
|
||||
collection := p.db.Collection(p.collection)
|
||||
end string, order string, sort string, start string, search string, starred string, albumId string, artistId string, year string,
|
||||
playlistId string) ([]scene_audio_route_models.PlaylistTrackMetadata, error) {
|
||||
|
||||
// 构建基础查询条件
|
||||
filter := bson.M{"playlist_id": playlistId} // 强制关联播放列表ID
|
||||
collection := r.db.Collection(r.collection)
|
||||
|
||||
filter := bson.M{"playlist_id": playlistId}
|
||||
|
||||
// 处理关联实体过滤
|
||||
if albumId != "" {
|
||||
if _, err := primitive.ObjectIDFromHex(albumId); err == nil {
|
||||
filter["album_id"] = albumId
|
||||
}
|
||||
filter["album_id"] = albumId
|
||||
}
|
||||
if artistId != "" {
|
||||
if _, err := primitive.ObjectIDFromHex(artistId); err == nil {
|
||||
filter["artist_id"] = artistId
|
||||
}
|
||||
filter["artist_id"] = artistId
|
||||
}
|
||||
|
||||
// 处理播放类型过滤
|
||||
if search != "" {
|
||||
filter["$or"] = []bson.M{
|
||||
{"play_type": bson.M{"$regex": primitive.Regex{Pattern: search, Options: "i"}}},
|
||||
{"track_name": bson.M{"$regex": search, "$options": "i"}},
|
||||
}
|
||||
}
|
||||
|
||||
// 处理收藏状态
|
||||
if starred != "" {
|
||||
isStarred, err := strconv.ParseBool(starred)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid starred parameter")
|
||||
return nil, fmt.Errorf("invalid starred parameter: %w", err)
|
||||
}
|
||||
filter["starred"] = isStarred
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
skip, limit := getPagination(start, end)
|
||||
|
||||
// 排序处理(默认按ID升序)
|
||||
sortOption := getSortOption_PlaylistTrack(sort, order)
|
||||
|
||||
// 执行查询
|
||||
findOptions := options.Find().
|
||||
SetSort(sortOption).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, findOptions)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %v", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
// 使用组合结构体解码
|
||||
var results []struct {
|
||||
scene_audio_db_models.PlaylistTrackMetadata `bson:",inline"`
|
||||
skip, _ := strconv.Atoi(start)
|
||||
limit, _ := strconv.Atoi(end)
|
||||
if limit == 0 || limit > 100 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode results: %v", err)
|
||||
}
|
||||
|
||||
// 转换为路由模型
|
||||
routeResults := make([]scene_audio_route_models.PlaylistTrackMetadata, len(results))
|
||||
for i, item := range results {
|
||||
routeResults[i] = scene_audio_route_models.PlaylistTrackMetadata{
|
||||
ID: item.ID,
|
||||
PlaylistID: item.PlaylistID,
|
||||
PlayType: item.PlayType,
|
||||
MediaFileID: item.MediaFileID,
|
||||
AlbumID: item.AlbumID,
|
||||
ArtistID: item.ArtistID,
|
||||
}
|
||||
}
|
||||
|
||||
return routeResults, nil
|
||||
}
|
||||
|
||||
// 常规排序处理(仅字段顺序)
|
||||
func getSortOption_PlaylistTrack(sortField, order string) bson.D {
|
||||
sortOrder := 1
|
||||
if order == "desc" {
|
||||
sortOrder = -1
|
||||
}
|
||||
|
||||
// 设置默认排序字段
|
||||
if sortField == "" {
|
||||
sortField = "title" // 保持原默认字段
|
||||
opts := options.Find().
|
||||
SetSort(bson.D{{Key: sort, Value: sortOrder}}).
|
||||
SetSkip(int64(skip)).
|
||||
SetLimit(int64(limit))
|
||||
|
||||
cursor, err := collection.Find(ctx, filter, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("database query failed: %w", err)
|
||||
}
|
||||
defer cursor.Close(ctx)
|
||||
|
||||
var results []scene_audio_route_models.PlaylistTrackMetadata
|
||||
if err := cursor.All(ctx, &results); err != nil {
|
||||
return nil, fmt.Errorf("decode error: %w", err)
|
||||
}
|
||||
|
||||
// 简化后的排序条件
|
||||
return bson.D{
|
||||
{Key: sortField, Value: sortOrder},
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
Binary file not shown.
@ -11,39 +11,43 @@ import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
)
|
||||
|
||||
type albumUsecase struct {
|
||||
albumRepo scene_audio_route_interface.AlbumRepository
|
||||
timeout time.Duration
|
||||
type AlbumUsecase struct {
|
||||
repo scene_audio_route_interface.AlbumRepository
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewAlbumUsecase(repo scene_audio_route_interface.AlbumRepository, timeout time.Duration) scene_audio_route_interface.AlbumRepository {
|
||||
return &albumUsecase{
|
||||
albumRepo: repo,
|
||||
timeout: timeout,
|
||||
func NewAlbumUsecase(repo scene_audio_route_interface.AlbumRepository, timeout time.Duration) *AlbumUsecase {
|
||||
return &AlbumUsecase{
|
||||
repo: repo,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (uc *albumUsecase) GetAlbumItems(
|
||||
func (uc *AlbumUsecase) GetAlbumItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred, artistId string,
|
||||
) ([]scene_audio_route_models.AlbumMetadata, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, uc.timeout)
|
||||
defer cancel()
|
||||
|
||||
// 参数验证
|
||||
if _, err := strconv.Atoi(start); err != nil && start != "" {
|
||||
if _, err := strconv.Atoi(start); start != "" && err != nil {
|
||||
return nil, errors.New("invalid start parameter")
|
||||
}
|
||||
if _, err := strconv.Atoi(end); err != nil && end != "" {
|
||||
|
||||
if _, err := strconv.Atoi(end); end != "" && err != nil {
|
||||
return nil, errors.New("invalid end parameter")
|
||||
}
|
||||
|
||||
// 转换artistId为ObjectID格式
|
||||
if artistId != "" {
|
||||
if _, err := primitive.ObjectIDFromHex(artistId); err != nil {
|
||||
return nil, errors.New("invalid artist id format")
|
||||
}
|
||||
}
|
||||
|
||||
return uc.albumRepo.GetAlbumItems(ctx, end, order, sort, start, search, starred, artistId)
|
||||
validSortFields := map[string]bool{"name": true, "song_count": true, "created_at": true}
|
||||
if !validSortFields[sort] {
|
||||
sort = "name"
|
||||
}
|
||||
|
||||
return uc.repo.GetAlbumItems(ctx, end, order, sort, start, search, starred, artistId)
|
||||
}
|
||||
|
@ -10,32 +10,38 @@ import (
|
||||
"github.com/amitshekhariitbhu/go-backend-clean-architecture/domain/domain_file_entity/scene_audio/scene_audio_route/scene_audio_route_models"
|
||||
)
|
||||
|
||||
type artistUsecase struct {
|
||||
artistRepo scene_audio_route_interface.ArtistRepository
|
||||
timeout time.Duration
|
||||
type ArtistUsecase struct {
|
||||
repo scene_audio_route_interface.ArtistRepository
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewArtistUsecase(repo scene_audio_route_interface.ArtistRepository, timeout time.Duration) scene_audio_route_interface.ArtistRepository {
|
||||
return &artistUsecase{
|
||||
artistRepo: repo,
|
||||
timeout: timeout,
|
||||
func NewArtistUsecase(repo scene_audio_route_interface.ArtistRepository, timeout time.Duration) *ArtistUsecase {
|
||||
return &ArtistUsecase{
|
||||
repo: repo,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
func (uc *artistUsecase) GetArtistItems(
|
||||
func (uc *ArtistUsecase) GetArtistItems(
|
||||
ctx context.Context,
|
||||
end, order, sort, start, search, starred string,
|
||||
) ([]scene_audio_route_models.ArtistMetadata, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, uc.timeout)
|
||||
defer cancel()
|
||||
|
||||
// 参数验证
|
||||
if _, err := strconv.Atoi(start); err != nil && start != "" {
|
||||
if _, err := strconv.Atoi(start); start != "" && err != nil {
|
||||
return nil, errors.New("invalid start parameter")
|
||||
}
|
||||
if _, err := strconv.Atoi(end); err != nil && end != "" {
|
||||
|
||||
if _, err := strconv.Atoi(end); end != "" && err != nil {
|
||||
return nil, errors.New("invalid end parameter")
|
||||
}
|
||||
|
||||
return uc.artistRepo.GetArtistItems(ctx, end, order, sort, start, search, starred)
|
||||
// 参数白名单校验
|
||||
validSortFields := map[string]bool{"name": true, "album_count": true, "song_count": true}
|
||||
if !validSortFields[sort] {
|
||||
sort = "name"
|
||||
}
|
||||
|
||||
return uc.repo.GetArtistItems(ctx, end, order, sort, start, search, starred)
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ type playlistTrackUsecase struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
func NewPlaylistTrackUsecase(repo scene_audio_route_interface.PlaylistTrackRepository, timeout time.Duration) scene_audio_route_interface.PlaylistTrackRepository {
|
||||
func NewPlaylistTrackUsecase(repo scene_audio_route_interface.PlaylistTrackRepository, timeout time.Duration) *playlistTrackUsecase {
|
||||
return &playlistTrackUsecase{
|
||||
repo: repo,
|
||||
timeout: timeout,
|
||||
@ -31,29 +31,19 @@ func (uc *playlistTrackUsecase) GetPlaylistTrackItems(
|
||||
ctx, cancel := context.WithTimeout(ctx, uc.timeout)
|
||||
defer cancel()
|
||||
|
||||
// 参数验证链
|
||||
validations := []func() error{
|
||||
func() error {
|
||||
if _, err := primitive.ObjectIDFromHex(playlistId); err != nil {
|
||||
return errors.New("invalid playlist id format")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func() error {
|
||||
if _, err := strconv.Atoi(start); err != nil && start != "" {
|
||||
return errors.New("invalid start parameter")
|
||||
}
|
||||
if _, err := strconv.Atoi(end); err != nil && end != "" {
|
||||
return errors.New("invalid end parameter")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
if _, err := primitive.ObjectIDFromHex(playlistId); err != nil {
|
||||
return nil, errors.New("invalid playlist id format")
|
||||
}
|
||||
if _, err := strconv.Atoi(start); start != "" && err != nil {
|
||||
return nil, errors.New("invalid start parameter")
|
||||
}
|
||||
if _, err := strconv.Atoi(end); end != "" && err != nil {
|
||||
return nil, errors.New("invalid end parameter")
|
||||
}
|
||||
|
||||
for _, validate := range validations {
|
||||
if err := validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
validSortFields := map[string]bool{"created_at": true, "play_order": true}
|
||||
if !validSortFields[sort] {
|
||||
sort = "created_at"
|
||||
}
|
||||
|
||||
return uc.repo.GetPlaylistTrackItems(ctx, end, order, sort, start, search, starred, albumId, artistId, year, playlistId)
|
||||
|
Loading…
x
Reference in New Issue
Block a user