diff --git a/cmd/server/handler/sos_post_handler.go b/cmd/server/handler/sos_post_handler.go index 8a3218d0..74c494a7 100644 --- a/cmd/server/handler/sos_post_handler.go +++ b/cmd/server/handler/sos_post_handler.go @@ -31,7 +31,7 @@ func NewSOSPostHandler(sosPostService service.SOSPostService, authService servic // @Produce json // @Param request body sospost.WriteSOSPostRequest true "돌봄급구 게시글 업로드 요청" // @Security FirebaseAuth -// @Success 201 {object} sospost.WriteSOSPostView +// @Success 201 {object} sospost.DetailView // @Router /posts/sos [post] func (h *SOSPostHandler) WriteSOSPost(c echo.Context) error { foundUser, err := h.authService.VerifyAuthAndGetUser(c.Request().Context(), c.Request().Header.Get("Authorization")) diff --git a/internal/common/format.go b/internal/common/format.go index cf193729..b2e909fb 100644 --- a/internal/common/format.go +++ b/internal/common/format.go @@ -2,20 +2,31 @@ package utils import "time" -// FormatDate formats datetime string to date string. +// FormatDateString formats datetime string to date string. // Example: 2021-01-01T00:00:00Z -> 2021-01-01 -func FormatDate(datetimeStr string) string { +func FormatDateString(datetimeStr string) string { return datetimeStr[:10] } -// FormatTime formats datetime time.Time to date string. +// FormatTimeFromTime formats datetime time.Time to date string. // Example: 2021-01-01T10:06:23Z -> 10:06 -func FormatTime(datetime time.Time) string { +func FormatTimeFromTime(datetime time.Time) string { return datetime.Format("15:04") } -// FormatDateTime formats datetime time.Time to datetime string. +// FormatDateTimeFromTime formats datetime time.Time to datetime string. // Example: 2021-01-01T10:06:23.9999999Z -> 2021-01-01T10:06:23 -func FormatDateTime(datetime time.Time) string { +func FormatDateTimeFromTime(datetime time.Time) string { return datetime.Format("2006-01-02T15:04:05") } + +// FormatDateTimeString formats datetime string to datetime string. +// Example: 2021-01-01T10:06:23.9999999Z -> 2021-01-01T10:06:23 +func FormatDateTimeString(datetimeStr string) time.Time { + t, err := time.Parse(time.RFC3339Nano, datetimeStr) + if err != nil { + return time.Time{} + } + + return t +} diff --git a/internal/common/null.go b/internal/common/null.go index 9488b160..977d4af0 100644 --- a/internal/common/null.go +++ b/internal/common/null.go @@ -2,8 +2,9 @@ package utils import ( "database/sql" - pnd "github.com/pet-sitter/pets-next-door-api/api" "time" + + pnd "github.com/pet-sitter/pets-next-door-api/api" ) func DerefOrEmpty[T any](val *T) T { diff --git a/internal/domain/sospost/model.go b/internal/domain/sospost/model.go index 142729dd..f28b4520 100644 --- a/internal/domain/sospost/model.go +++ b/internal/domain/sospost/model.go @@ -2,9 +2,15 @@ package sospost import ( "context" - "github.com/pet-sitter/pets-next-door-api/internal/domain/commonvo" + "encoding/json" + "log" "time" + utils "github.com/pet-sitter/pets-next-door-api/internal/common" + databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen" + + "github.com/pet-sitter/pets-next-door-api/internal/domain/commonvo" + "github.com/pet-sitter/pets-next-door-api/internal/domain/soscondition" pnd "github.com/pet-sitter/pets-next-door-api/api" @@ -55,6 +61,85 @@ type SOSPostInfoList struct { *pnd.PaginatedView[SOSPostInfo] } +func ToInfoFromFindRow(row databasegen.FindSOSPostsRow) *SOSPostInfo { + return &SOSPostInfo{ + ID: int(row.ID), + AuthorID: int(*utils.NullInt64ToInt64Ptr(row.AuthorID)), + Title: utils.NullStrToStr(row.Title), + Content: utils.NullStrToStr(row.Content), + Media: ParseMediaList(row.MediaInfo.RawMessage), + Conditions: ParseConditionsList(row.ConditionsInfo.RawMessage), + Pets: ParsePetsList(row.PetsInfo.RawMessage), + Reward: utils.NullStrToStr(row.Reward), + Dates: ParseSOSDatesList(row.Dates), + CareType: commonvo.CareType(row.CareType.String), + CarerGender: commonvo.CarerGender(row.CarerGender.String), + RewardType: commonvo.RewardType(row.RewardType.String), + ThumbnailID: &row.ThumbnailID.Int64, + CreatedAt: row.CreatedAt, + UpdatedAt: row.UpdatedAt, + } +} + +func ToInfoListFromFindRow(rows []databasegen.FindSOSPostsRow, page, size int) *SOSPostInfoList { + sl := NewSOSPostInfoList(page, size) + for _, row := range rows { + sl.Items = append(sl.Items, *ToInfoFromFindRow(row)) + } + sl.CalcLastPage() + return sl +} + +func ToInfoFromFindAuthorIDRow(row databasegen.FindSOSPostsByAuthorIDRow) *SOSPostInfo { + return &SOSPostInfo{ + ID: int(row.ID), + AuthorID: int(*utils.NullInt64ToInt64Ptr(row.AuthorID)), + Title: utils.NullStrToStr(row.Title), + Content: utils.NullStrToStr(row.Content), + Media: ParseMediaList(row.MediaInfo.RawMessage), + Conditions: ParseConditionsList(row.ConditionsInfo.RawMessage), + Pets: ParsePetsList(row.PetsInfo.RawMessage), + Reward: utils.NullStrToStr(row.Reward), + Dates: ParseSOSDatesList(row.Dates), + CareType: commonvo.CareType(row.CareType.String), + CarerGender: commonvo.CarerGender(row.CarerGender.String), + RewardType: commonvo.RewardType(row.RewardType.String), + ThumbnailID: &row.ThumbnailID.Int64, + CreatedAt: row.CreatedAt, + UpdatedAt: row.UpdatedAt, + } +} + +func ToInfoListFromFindAuthorIDRow(rows []databasegen.FindSOSPostsByAuthorIDRow, page, size int) *SOSPostInfoList { + sl := NewSOSPostInfoList(page, size) + for _, row := range rows { + sl.Items = append(sl.Items, *ToInfoFromFindAuthorIDRow(row)) + } + + sl.CalcLastPage() + return sl +} + +func ToInfoFromFindByIDRow(row databasegen.FindSOSPostByIDRow) *SOSPostInfo { + return &SOSPostInfo{ + ID: int(row.ID), + AuthorID: int(*utils.NullInt64ToInt64Ptr(row.AuthorID)), + Title: utils.NullStrToStr(row.Title), + Content: utils.NullStrToStr(row.Content), + Media: ParseMediaList(row.MediaInfo.RawMessage), + Conditions: ParseConditionsList(row.ConditionsInfo.RawMessage), + Pets: ParsePetsList(row.PetsInfo.RawMessage), + Reward: utils.NullStrToStr(row.Reward), + Dates: ParseSOSDatesList(row.Dates), + CareType: commonvo.CareType(row.CareType.String), + CarerGender: commonvo.CarerGender(row.CarerGender.String), + RewardType: commonvo.RewardType(row.RewardType.String), + ThumbnailID: &row.ThumbnailID.Int64, + CreatedAt: row.CreatedAt, + UpdatedAt: row.UpdatedAt, + } +} + func NewSOSPostList(page, size int) *SOSPostList { return &SOSPostList{PaginatedView: pnd.NewPaginatedView( page, size, false, make([]SOSPost, 0), @@ -67,6 +152,67 @@ func NewSOSPostInfoList(page, size int) *SOSPostInfoList { )} } +const ( + JSONNullString = "null" + JSONEmptyArray = "[]" +) + +func ParseMediaList(rows json.RawMessage) media.ViewListForSOSPost { + var mediaList media.ViewListForSOSPost + if len(rows) == 0 || string(rows) == JSONNullString || string(rows) == JSONEmptyArray { + return mediaList + } + + if err := json.Unmarshal(rows, &mediaList); err != nil { + log.Println("Error unmarshalling media:", err) + return mediaList + } + + return mediaList +} + +func ParseConditionsList(rows json.RawMessage) soscondition.ViewListForSOSPost { + var conditionsList soscondition.ViewListForSOSPost + if len(rows) == 0 || string(rows) == JSONNullString || string(rows) == JSONEmptyArray { + return conditionsList + } + + if err := json.Unmarshal(rows, &conditionsList); err != nil { + log.Println("Error unmarshalling conditions:", err) + return conditionsList + } + return conditionsList +} + +func ParsePetsList(rows json.RawMessage) pet.ViewListForSOSPost { + var petList pet.ViewListForSOSPost + + if len(rows) == 0 || string(rows) == JSONNullString || string(rows) == JSONEmptyArray { + return petList + } + + if err := json.Unmarshal(rows, &petList); err != nil { + log.Println("Error unmarshalling pets:", err) + return petList + } + + return petList +} + +func ParseSOSDatesList(rows json.RawMessage) SOSDatesList { + var sosDatesList SOSDatesList + + if len(rows) == 0 || string(rows) == JSONNullString || string(rows) == JSONEmptyArray { + return sosDatesList + } + if err := json.Unmarshal(rows, &sosDatesList); err != nil { + log.Println("Error unmarshalling sosDates:", err) + return sosDatesList + } + + return sosDatesList +} + type SOSDates struct { ID int `field:"id" json:"id"` DateStartAt string `field:"date_start_at" json:"date_start_at"` diff --git a/internal/domain/sospost/request.go b/internal/domain/sospost/request.go new file mode 100644 index 00000000..ce2a4056 --- /dev/null +++ b/internal/domain/sospost/request.go @@ -0,0 +1,30 @@ +package sospost + +import "github.com/pet-sitter/pets-next-door-api/internal/domain/commonvo" + +type WriteSOSPostRequest struct { + Title string `json:"title" validate:"required"` + Content string `json:"content" validate:"required"` + ImageIDs []int64 `json:"imageIds" validate:"required"` + Reward string `json:"reward" validate:"required"` + Dates []SOSDateView `json:"dates" validate:"required,gte=1"` + CareType commonvo.CareType `json:"careType" validate:"required,oneof=foster visiting"` + CarerGender commonvo.CarerGender `json:"carerGender" validate:"required,oneof=male female all"` + RewardType commonvo.RewardType `json:"rewardType" validate:"required,oneof=fee gifticon negotiable"` + ConditionIDs []int `json:"conditionIds" validate:"required"` + PetIDs []int64 `json:"petIds" validate:"required,gte=1"` +} + +type UpdateSOSPostRequest struct { + ID int `json:"id" validate:"required"` + Title string `json:"title" validate:"required"` + Content string `json:"content" validate:"required"` + ImageIDs []int64 `json:"imageIds" validate:"required"` + Dates []SOSDateView `json:"dates" validate:"required,gte=1"` + Reward string `json:"reward" validate:"required"` + CareType commonvo.CareType `json:"careType" validate:"required,oneof=foster visiting"` + CarerGender commonvo.CarerGender `json:"carerGender" validate:"required,oneof=male female all"` + RewardType commonvo.RewardType `json:"rewardType" validate:"required,oneof=fee gifticon negotiable"` + ConditionIDs []int `json:"conditionIds" validate:"required"` + PetIDs []int64 `json:"petIds" validate:"required,gte=1"` +} diff --git a/internal/domain/sospost/view.go b/internal/domain/sospost/view.go index ffe3caf0..22f00df6 100644 --- a/internal/domain/sospost/view.go +++ b/internal/domain/sospost/view.go @@ -11,20 +11,43 @@ import ( databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen" ) -type WriteSOSPostRequest struct { - Title string `json:"title" validate:"required"` - Content string `json:"content" validate:"required"` - ImageIDs []int64 `json:"imageIds" validate:"required"` - Reward string `json:"reward" validate:"required"` - Dates []SOSDateView `json:"dates" validate:"required,gte=1"` - CareType commonvo.CareType `json:"careType" validate:"required,oneof=foster visiting"` - CarerGender commonvo.CarerGender `json:"carerGender" validate:"required,oneof=male female all"` - RewardType commonvo.RewardType `json:"rewardType" validate:"required,oneof=fee gifticon negotiable"` - ConditionIDs []int `json:"conditionIds" validate:"required"` - PetIDs []int64 `json:"petIds" validate:"required,gte=1"` -} - -type WriteSOSPostView struct { +type ViewParams struct { + ID int + AuthorID int + Title string + Content string + MediaList media.ListView + Conditions soscondition.ListView + Pets []pet.DetailView + Reward string + SOSDates []SOSDateView + CareType commonvo.CareType + CarerGender commonvo.CarerGender + RewardType commonvo.RewardType + ThumbnailID *int64 + CreatedAt string + UpdatedAt string +} + +type ViewParamsInput struct { + ID int64 + AuthorID int64 + Title string + Content string + MediaList media.ListView + Conditions soscondition.ListView + Pets []pet.DetailView + Reward string + SOSDates []SOSDateView + CareType string + CarerGender string + RewardType string + ThumbnailID *int64 + CreatedAt string + UpdatedAt string +} + +type DetailView struct { ID int `json:"id"` AuthorID int `json:"authorId"` Title string `json:"title"` @@ -42,30 +65,72 @@ type WriteSOSPostView struct { UpdatedAt string `json:"updatedAt"` } -func ToWriteSOSPostView( +func ToDetailView(params ViewParams) *DetailView { + return &DetailView{ + ID: params.ID, + AuthorID: params.AuthorID, + Title: params.Title, + Content: params.Content, + Media: params.MediaList, + Conditions: params.Conditions, + Pets: params.Pets, + Reward: params.Reward, + Dates: params.SOSDates, + CareType: params.CareType, + CarerGender: params.CarerGender, + RewardType: params.RewardType, + ThumbnailID: params.ThumbnailID, + CreatedAt: params.CreatedAt, + UpdatedAt: params.UpdatedAt, + } +} + +func CreateViewParams(input ViewParamsInput) ViewParams { + return ViewParams{ + ID: int(input.ID), + AuthorID: int(input.AuthorID), + Title: input.Title, + Content: input.Content, + MediaList: input.MediaList, + Conditions: input.Conditions, + Pets: input.Pets, + Reward: input.Reward, + SOSDates: input.SOSDates, + CareType: commonvo.CareType(input.CareType), + CarerGender: commonvo.CarerGender(input.CarerGender), + RewardType: commonvo.RewardType(input.RewardType), + ThumbnailID: input.ThumbnailID, + CreatedAt: input.CreatedAt, + UpdatedAt: input.UpdatedAt, + } +} + +func CreateDetailView( sosPost databasegen.WriteSOSPostRow, mediaList media.ListView, conditions soscondition.ListView, pets []pet.DetailView, sosDates []SOSDateView, -) *WriteSOSPostView { - return &WriteSOSPostView{ - ID: int(sosPost.ID), - AuthorID: int(sosPost.AuthorID.Int64), +) *DetailView { + input := ViewParamsInput{ + ID: int64(sosPost.ID), + AuthorID: sosPost.AuthorID.Int64, Title: utils.NullStrToStr(sosPost.Title), Content: utils.NullStrToStr(sosPost.Content), - Media: mediaList, + MediaList: mediaList, Conditions: conditions, Pets: pets, Reward: utils.NullStrToStr(sosPost.Reward), - Dates: sosDates, - CareType: commonvo.CareType(sosPost.CareType.String), - CarerGender: commonvo.CarerGender(sosPost.CarerGender.String), - RewardType: commonvo.RewardType(sosPost.RewardType.String), + SOSDates: sosDates, + CareType: sosPost.CareType.String, + CarerGender: sosPost.CarerGender.String, + RewardType: sosPost.RewardType.String, ThumbnailID: &sosPost.ThumbnailID.Int64, - CreatedAt: utils.FormatDateTime(sosPost.CreatedAt), - UpdatedAt: utils.FormatDateTime(sosPost.UpdatedAt), + CreatedAt: utils.FormatTimeFromTime(sosPost.CreatedAt), + UpdatedAt: utils.FormatTimeFromTime(sosPost.UpdatedAt), } + params := CreateViewParams(input) + return ToDetailView(params) } type FindSOSPostView struct { @@ -107,8 +172,8 @@ func (p *SOSPost) ToFindSOSPostView( CarerGender: p.CarerGender, RewardType: p.RewardType, ThumbnailID: p.ThumbnailID, - CreatedAt: utils.FormatDateTime(p.CreatedAt), - UpdatedAt: utils.FormatDateTime(p.UpdatedAt), + CreatedAt: utils.FormatDateTimeFromTime(p.CreatedAt), + UpdatedAt: utils.FormatDateTimeFromTime(p.UpdatedAt), } } @@ -153,67 +218,36 @@ func (p *SOSPostInfo) ToFindSOSPostInfoView( CarerGender: p.CarerGender, RewardType: p.RewardType, ThumbnailID: p.ThumbnailID, - CreatedAt: utils.FormatDateTime(p.CreatedAt), - UpdatedAt: utils.FormatDateTime(p.UpdatedAt), + CreatedAt: utils.FormatDateTimeFromTime(p.CreatedAt), + UpdatedAt: utils.FormatDateTimeFromTime(p.UpdatedAt), } } -type UpdateSOSPostRequest struct { - ID int `json:"id" validate:"required"` - Title string `json:"title" validate:"required"` - Content string `json:"content" validate:"required"` - ImageIDs []int64 `json:"imageIds" validate:"required"` - Dates []SOSDateView `json:"dates" validate:"required,gte=1"` - Reward string `json:"reward" validate:"required"` - CareType commonvo.CareType `json:"careType" validate:"required,oneof=foster visiting"` - CarerGender commonvo.CarerGender `json:"carerGender" validate:"required,oneof=male female all"` - RewardType commonvo.RewardType `json:"rewardType" validate:"required,oneof=fee gifticon negotiable"` - ConditionIDs []int `json:"conditionIds" validate:"required"` - PetIDs []int64 `json:"petIds" validate:"required,gte=1"` -} - -type UpdateSOSPostView struct { - ID int `json:"id"` - AuthorID int `json:"authorId"` - Title string `json:"title"` - Content string `json:"content"` - Media media.ListView `json:"media"` - Conditions soscondition.ListView `json:"conditions"` - Pets []pet.DetailView `json:"pets"` - Reward string `json:"reward"` - Dates []SOSDateView `json:"dates"` - CareType commonvo.CareType `json:"careType"` - CarerGender commonvo.CarerGender `json:"carerGender"` - RewardType commonvo.RewardType `json:"rewardType"` - ThumbnailID *int64 `json:"thumbnailId"` - CreatedAt string `json:"createdAt"` - UpdatedAt string `json:"updatedAt"` -} - -func ToUpdateSOSPostView( - updateSOSPost databasegen.UpdateSOSPostRow, +func UpdateDetailView( + sosPost databasegen.UpdateSOSPostRow, mediaList media.ListView, conditions soscondition.ListView, pets []pet.DetailView, sosDates []SOSDateView, -) *UpdateSOSPostView { - return &UpdateSOSPostView{ - ID: int(updateSOSPost.ID), - AuthorID: int(updateSOSPost.AuthorID.Int64), - Title: utils.NullStrToStr(updateSOSPost.Title), - Content: utils.NullStrToStr(updateSOSPost.Content), - Media: mediaList, +) *DetailView { + params := ViewParams{ + ID: int(sosPost.ID), + AuthorID: int(sosPost.AuthorID.Int64), + Title: utils.NullStrToStr(sosPost.Title), + Content: utils.NullStrToStr(sosPost.Content), + MediaList: mediaList, Conditions: conditions, Pets: pets, - Reward: utils.NullStrToStr(updateSOSPost.Reward), - Dates: sosDates, - CareType: commonvo.CareType(updateSOSPost.CareType.String), - CarerGender: commonvo.CarerGender(updateSOSPost.CarerGender.String), - RewardType: commonvo.RewardType(updateSOSPost.RewardType.String), - ThumbnailID: &updateSOSPost.ThumbnailID.Int64, - CreatedAt: utils.FormatDateTime(updateSOSPost.CreatedAt), - UpdatedAt: utils.FormatDateTime(updateSOSPost.UpdatedAt), + Reward: utils.NullStrToStr(sosPost.Reward), + SOSDates: sosDates, + CareType: commonvo.CareType(sosPost.CareType.String), + CarerGender: commonvo.CarerGender(sosPost.CarerGender.String), + RewardType: commonvo.RewardType(sosPost.RewardType.String), + ThumbnailID: &sosPost.ThumbnailID.Int64, + CreatedAt: utils.FormatDateTimeFromTime(sosPost.CreatedAt), + UpdatedAt: utils.FormatDateTimeFromTime(sosPost.UpdatedAt), } + return ToDetailView(params) } type SOSDateView struct { @@ -223,8 +257,8 @@ type SOSDateView struct { func (d *SOSDates) ToSOSDateView() SOSDateView { return SOSDateView{ - DateStartAt: utils.FormatDate(d.DateStartAt), - DateEndAt: utils.FormatDate(d.DateEndAt), + DateStartAt: utils.FormatDateString(d.DateStartAt), + DateEndAt: utils.FormatDateString(d.DateEndAt), } } diff --git a/internal/infra/database/gen/sos_posts.sql.go b/internal/infra/database/gen/sos_posts.sql.go index dec6c52c..fa3c16b0 100644 --- a/internal/infra/database/gen/sos_posts.sql.go +++ b/internal/infra/database/gen/sos_posts.sql.go @@ -81,8 +81,8 @@ type FindDatesBySOSPostIDRow struct { UpdatedAt sql.NullTime } -func (q *Queries) FindDatesBySOSPostID(ctx context.Context, sosPostID sql.NullInt64) ([]FindDatesBySOSPostIDRow, error) { - rows, err := q.db.QueryContext(ctx, findDatesBySOSPostID, sosPostID) +func (q *Queries) FindDatesBySOSPostID(ctx context.Context, id sql.NullInt64) ([]FindDatesBySOSPostIDRow, error) { + rows, err := q.db.QueryContext(ctx, findDatesBySOSPostID, id) if err != nil { return nil, err } @@ -154,7 +154,7 @@ type FindSOSPostByIDRow struct { ConditionsInfo pqtype.NullRawMessage } -func (q *Queries) FindSOSPostByID(ctx context.Context, id int32) (FindSOSPostByIDRow, error) { +func (q *Queries) FindSOSPostByID(ctx context.Context, id sql.NullInt32) (FindSOSPostByIDRow, error) { row := q.db.QueryRowContext(ctx, findSOSPostByID, id) var i FindSOSPostByIDRow err := row.Scan( @@ -201,19 +201,23 @@ FROM LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id WHERE v_sos_posts.earliest_date_start_at >= $1 - AND ($2) + AND ($2 = 'all' OR NOT EXISTS + (SELECT 1 + FROM unnest(pet_type_list) AS pet_type + WHERE pet_type <> $2)) ORDER BY - $3 -LIMIT $4 - OFFSET $5 + CASE WHEN $3 = 'newest' THEN v_sos_posts.created_at END DESC, + CASE WHEN $3 = 'deadline' THEN v_sos_posts.earliest_date_start_at END +LIMIT $5 + OFFSET $4 ` type FindSOSPostsParams struct { EarliestDateStartAt interface{} - Column2 interface{} - Column3 interface{} - Limit int32 - Offset int32 + PetType interface{} + SortBy interface{} + Offset sql.NullInt32 + Limit sql.NullInt32 } type FindSOSPostsRow struct { @@ -237,10 +241,10 @@ type FindSOSPostsRow struct { func (q *Queries) FindSOSPosts(ctx context.Context, arg FindSOSPostsParams) ([]FindSOSPostsRow, error) { rows, err := q.db.QueryContext(ctx, findSOSPosts, arg.EarliestDateStartAt, - arg.Column2, - arg.Column3, - arg.Limit, + arg.PetType, + arg.SortBy, arg.Offset, + arg.Limit, ) if err != nil { return nil, err @@ -304,20 +308,24 @@ FROM WHERE v_sos_posts.earliest_date_start_at >= $1 AND v_sos_posts.author_id = $2 - AND ($3) + AND ($3 = 'all' OR NOT EXISTS + (SELECT 1 + FROM unnest(pet_type_list) AS pet_type + WHERE pet_type <> $3)) ORDER BY - $4 -LIMIT $5 - OFFSET $6 + CASE WHEN $4 = 'newest' THEN v_sos_posts.created_at END DESC, + CASE WHEN $4 = 'deadline' THEN v_sos_posts.earliest_date_start_at END +LIMIT $6 + OFFSET $5 ` type FindSOSPostsByAuthorIDParams struct { EarliestDateStartAt interface{} AuthorID sql.NullInt64 - Column3 interface{} - Column4 interface{} - Limit int32 - Offset int32 + PetType interface{} + SortBy interface{} + Offset sql.NullInt32 + Limit sql.NullInt32 } type FindSOSPostsByAuthorIDRow struct { @@ -342,10 +350,10 @@ func (q *Queries) FindSOSPostsByAuthorID(ctx context.Context, arg FindSOSPostsBy rows, err := q.db.QueryContext(ctx, findSOSPostsByAuthorID, arg.EarliestDateStartAt, arg.AuthorID, - arg.Column3, - arg.Column4, - arg.Limit, + arg.PetType, + arg.SortBy, arg.Offset, + arg.Limit, ) if err != nil { return nil, err diff --git a/internal/postgres/sos_post_store.go b/internal/postgres/sos_post_store.go deleted file mode 100644 index f4d1ab3d..00000000 --- a/internal/postgres/sos_post_store.go +++ /dev/null @@ -1,289 +0,0 @@ -package postgres - -import ( - "context" - "database/sql" - "encoding/json" - "fmt" - "time" - - "github.com/pet-sitter/pets-next-door-api/internal/domain/media" - - "github.com/pet-sitter/pets-next-door-api/internal/domain/soscondition" - - utils "github.com/pet-sitter/pets-next-door-api/internal/common" - - pnd "github.com/pet-sitter/pets-next-door-api/api" - "github.com/pet-sitter/pets-next-door-api/internal/domain/sospost" - "github.com/pet-sitter/pets-next-door-api/internal/infra/database" -) - -func readSOSPostRows(rows *sql.Rows, page, size int) (*sospost.SOSPostInfoList, *pnd.AppError) { - sosPostList := sospost.NewSOSPostInfoList(page, size) - - for rows.Next() { - sosPost := sospost.SOSPostInfo{} - var datesData, petsData, mediaData, conditionsData []byte - if err := rows.Scan( - &sosPost.ID, - &sosPost.Title, - &sosPost.Content, - &sosPost.Reward, - &sosPost.RewardType, - &sosPost.CareType, - &sosPost.CarerGender, - &sosPost.ThumbnailID, - &sosPost.AuthorID, - &sosPost.CreatedAt, - &sosPost.UpdatedAt, - &datesData, - &petsData, - &mediaData, - &conditionsData); err != nil { - return nil, pnd.FromPostgresError(err) - } - - if err := json.Unmarshal(datesData, &sosPost.Dates); err != nil { - return nil, pnd.FromPostgresError(err) - } - if err := json.Unmarshal(petsData, &sosPost.Pets); err != nil { - return nil, pnd.FromPostgresError(err) - } - if len(mediaData) > 0 { - if err := json.Unmarshal(mediaData, &sosPost.Media); err != nil { - return nil, pnd.FromPostgresError(err) - } - } else { - sosPost.Media = media.ViewListForSOSPost{} - } - if len(conditionsData) > 0 { - if err := json.Unmarshal(conditionsData, &sosPost.Conditions); err != nil { - return nil, pnd.FromPostgresError(err) - } - } else { - sosPost.Conditions = soscondition.ViewListForSOSPost{} - } - sosPostList.Items = append(sosPostList.Items, sosPost) - } - - if err := rows.Err(); err != nil { - return nil, pnd.FromPostgresError(err) - } - sosPostList.CalcLastPage() - - return sosPostList, nil -} - -func FindSOSPosts( - ctx context.Context, tx *database.Tx, page, size int, sortBy, filterType string, -) (*sospost.SOSPostInfoList, *pnd.AppError) { - var sortString string - switch sortBy { - case "newest": - sortString = "v_sos_posts.created_at DESC" - case "deadline": - sortString = "v_sos_posts.earliest_date_start_at" - } - - filterString := buildFilterString(filterType) - - query := fmt.Sprintf(` - SELECT - v_sos_posts.id, - v_sos_posts.title, - v_sos_posts.content, - v_sos_posts.reward, - v_sos_posts.reward_type, - v_sos_posts.care_type, - v_sos_posts.carer_gender, - v_sos_posts.thumbnail_id, - v_sos_posts.author_id, - v_sos_posts.created_at, - v_sos_posts.updated_at, - v_sos_posts.dates, - v_pets_for_sos_posts.pets_info, - v_media_for_sos_posts.media_info, - v_conditions.conditions_info - FROM - v_sos_posts - LEFT JOIN v_pets_for_sos_posts ON v_sos_posts.id = v_pets_for_sos_posts.sos_post_id - LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id - LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id - WHERE - v_sos_posts.earliest_date_start_at >= '%s' - %s - ORDER BY - %s - LIMIT $1 - OFFSET $2; - - `, - utils.FormatDate(time.Now().String()), - filterString, - sortString, - ) - - rows, err := tx.QueryContext(ctx, query, size+1, (page-1)*size) - if err != nil { - return &sospost.SOSPostInfoList{}, pnd.FromPostgresError(err) - } - defer rows.Close() - - sosPostList, err2 := readSOSPostRows(rows, page, size) - if err2 != nil { - return nil, err2 - } - - return sosPostList, nil -} - -func FindSOSPostsByAuthorID( - ctx context.Context, tx *database.Tx, authorID, page, size int, sortBy, filterType string, -) (*sospost.SOSPostInfoList, *pnd.AppError) { - var sortString string - switch sortBy { - case "newest": - sortString = "v_sos_posts.created_at DESC" - case "deadline": - sortString = "v_sos_posts.earliest_date_start_at" - } - - filterString := buildFilterString(filterType) - - query := fmt.Sprintf(` - SELECT - v_sos_posts.id, - v_sos_posts.title, - v_sos_posts.content, - v_sos_posts.reward, - v_sos_posts.reward_type, - v_sos_posts.care_type, - v_sos_posts.carer_gender, - v_sos_posts.thumbnail_id, - v_sos_posts.author_id, - v_sos_posts.created_at, - v_sos_posts.updated_at, - v_sos_posts.dates, - v_pets_for_sos_posts.pets_info, - v_media_for_sos_posts.media_info, - v_conditions.conditions_info - FROM - v_sos_posts - LEFT JOIN v_pets_for_sos_posts ON v_sos_posts.id = v_pets_for_sos_posts.sos_post_id - LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id - LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id - WHERE - v_sos_posts.earliest_date_start_at >= '%s' - AND v_sos_posts.author_id = $1 - %s - ORDER BY - %s - LIMIT $2 - OFFSET $3; - - `, - utils.FormatDate(time.Now().String()), - filterString, - sortString, - ) - - rows, err := tx.QueryContext(ctx, query, authorID, size+1, (page-1)*size) - if err != nil { - return nil, pnd.FromPostgresError(err) - } - defer rows.Close() - - sosPostList, err2 := readSOSPostRows(rows, page, size) - if err2 != nil { - return nil, err2 - } - - return sosPostList, nil -} - -func FindSOSPostByID(ctx context.Context, tx *database.Tx, id int) (*sospost.SOSPostInfo, *pnd.AppError) { - query := ` - SELECT - v_sos_posts.id, - v_sos_posts.title, - v_sos_posts.content, - v_sos_posts.reward, - v_sos_posts.reward_type, - v_sos_posts.care_type, - v_sos_posts.carer_gender, - v_sos_posts.thumbnail_id, - v_sos_posts.author_id, - v_sos_posts.created_at, - v_sos_posts.updated_at, - v_sos_posts.dates, - v_pets_for_sos_posts.pets_info, - v_media_for_sos_posts.media_info, - v_conditions.conditions_info - FROM - v_sos_posts - LEFT JOIN v_pets_for_sos_posts ON v_sos_posts.id = v_pets_for_sos_posts.sos_post_id - LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id - LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id - WHERE - v_sos_posts.id = $1; - ` - - row := tx.QueryRowContext(ctx, query, id) - - sosPost := sospost.SOSPostInfo{} - - var datesData, petsData, mediaData, conditionsData []byte - if err := row.Scan( - &sosPost.ID, - &sosPost.Title, - &sosPost.Content, - &sosPost.Reward, - &sosPost.RewardType, - &sosPost.CareType, - &sosPost.CarerGender, - &sosPost.ThumbnailID, - &sosPost.AuthorID, - &sosPost.CreatedAt, - &sosPost.UpdatedAt, - &datesData, - &petsData, - &mediaData, - &conditionsData); err != nil { - return nil, pnd.FromPostgresError(err) - } - - if err := json.Unmarshal(datesData, &sosPost.Dates); err != nil { - return nil, pnd.FromPostgresError(err) - } - if err := json.Unmarshal(petsData, &sosPost.Pets); err != nil { - return nil, pnd.FromPostgresError(err) - } - if len(mediaData) > 0 { - if err := json.Unmarshal(mediaData, &sosPost.Media); err != nil { - return nil, pnd.FromPostgresError(err) - } - } else { - sosPost.Media = media.ViewListForSOSPost{} - } - if len(conditionsData) > 0 { - if err := json.Unmarshal(conditionsData, &sosPost.Conditions); err != nil { - return nil, pnd.FromPostgresError(err) - } - } else { - sosPost.Conditions = soscondition.ViewListForSOSPost{} - } - - return &sosPost, nil -} - -func buildFilterString(petType string) string { - if petType == "all" { - return "" - } - return fmt.Sprintf(` -AND NOT EXISTS - (SELECT 1 - FROM unnest(pet_type_list) AS pet_type - WHERE pet_type <> '%s') -`, petType) -} diff --git a/internal/service/sos_post_service.go b/internal/service/sos_post_service.go index ed9cc014..3532fa9b 100644 --- a/internal/service/sos_post_service.go +++ b/internal/service/sos_post_service.go @@ -2,6 +2,7 @@ package service import ( "context" + "time" "github.com/pet-sitter/pets-next-door-api/internal/domain/soscondition" @@ -14,7 +15,6 @@ import ( databasegen "github.com/pet-sitter/pets-next-door-api/internal/infra/database/gen" "github.com/pet-sitter/pets-next-door-api/internal/infra/database" - "github.com/pet-sitter/pets-next-door-api/internal/postgres" pnd "github.com/pet-sitter/pets-next-door-api/api" "github.com/pet-sitter/pets-next-door-api/internal/domain/media" @@ -33,7 +33,7 @@ func NewSOSPostService(conn *database.DB) *SOSPostService { func (service *SOSPostService) WriteSOSPost( ctx context.Context, fbUID string, request *sospost.WriteSOSPostRequest, -) (*sospost.WriteSOSPostView, *pnd.AppError) { +) (*sospost.DetailView, *pnd.AppError) { tx, err := service.conn.BeginTx(ctx) defer tx.Rollback() if err != nil { @@ -49,62 +49,13 @@ func (service *SOSPostService) WriteSOSPost( } thumbnailID := setThumbnailID(request.ImageIDs) - - // SOSPost 저장 - sosPost := databasegen.WriteSOSPostRow{} - var err3 error - if thumbnailID == nil { - sosPost, err3 = q.WriteSOSPost(ctx, databasegen.WriteSOSPostParams{ - AuthorID: utils.IntToNullInt64(int(userData.ID)), - Title: utils.StrToNullStr(request.Title), - Content: utils.StrToNullStr(request.Content), - Reward: utils.StrToNullStr(request.Reward), - CareType: utils.StrToNullStr(string(request.CareType)), - CarerGender: utils.StrToNullStr(request.CarerGender.String()), - RewardType: utils.StrToNullStr(request.RewardType.String()), - }) - if err3 != nil { - return nil, pnd.FromPostgresError(err3) - } - } - if thumbnailID != nil { - sosPost, err3 = q.WriteSOSPost(ctx, databasegen.WriteSOSPostParams{ - AuthorID: utils.IntToNullInt64(int(userData.ID)), - Title: utils.StrToNullStr(request.Title), - Content: utils.StrToNullStr(request.Content), - Reward: utils.StrToNullStr(request.Reward), - CareType: utils.StrToNullStr(string(request.CareType)), - CarerGender: utils.StrToNullStr(request.CarerGender.String()), - RewardType: utils.StrToNullStr(request.RewardType.String()), - ThumbnailID: utils.IntToNullInt64(int(*thumbnailID)), - }) - if err3 != nil { - return nil, pnd.FromPostgresError(err3) - } - } - - // 날짜 리스트 저장 - err4 := service.SaveSOSDates(ctx, q, request.Dates, int(sosPost.ID)) - if err4 != nil { - return nil, err4 - } - - // 이미지와 SOSPost 다대다 저장 - err5 := service.SaveLinkSOSPostImage(ctx, q, request.ImageIDs, int(sosPost.ID)) - if err5 != nil { - return nil, err5 - } - - // 조건과 SOSPost 다대다 저장 - err6 := service.SaveLinkConditions(ctx, q, request.ConditionIDs, int(sosPost.ID)) - if err6 != nil { - return nil, err6 + sosPost, err := service.createSOSPost(ctx, q, int64(userData.ID), request, thumbnailID) + if err != nil { + return nil, err } - // 펫과 SOSPost 다대다 저장 - err7 := service.SaveLinkPets(ctx, q, request.PetIDs, int(sosPost.ID)) - if err7 != nil { - return nil, err7 + if err := service.saveAllLinks(ctx, q, request, int(sosPost.ID)); err != nil { + return nil, err } mediaData, err2 := q.FindResourceMedia(ctx, databasegen.FindResourceMediaParams{ @@ -127,16 +78,16 @@ func (service *SOSPostService) WriteSOSPost( return nil, pnd.FromPostgresError(err2) } - dates, err3 := q.FindDatesBySOSPostID(ctx, utils.IntToNullInt64(int(sosPost.ID))) - if err3 != nil { - return nil, pnd.FromPostgresError(err3) + dates, err2 := q.FindDatesBySOSPostID(ctx, utils.IntToNullInt64(int(sosPost.ID))) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } if err := tx.Commit(); err != nil { return nil, err } - return sospost.ToWriteSOSPostView( + return sospost.CreateDetailView( sosPost, media.ToListViewFromResourceMediaRows(mediaData), soscondition.ToListViewFromSOSPostConditions(conditionList), @@ -145,6 +96,46 @@ func (service *SOSPostService) WriteSOSPost( ), nil } +func (service *SOSPostService) createSOSPost( + ctx context.Context, q *databasegen.Queries, authorID int64, request *sospost.WriteSOSPostRequest, thumbnailID *int64, +) (databasegen.WriteSOSPostRow, *pnd.AppError) { + params := databasegen.WriteSOSPostParams{ + AuthorID: utils.IntToNullInt64(int(authorID)), + Title: utils.StrToNullStr(request.Title), + Content: utils.StrToNullStr(request.Content), + Reward: utils.StrToNullStr(request.Reward), + CareType: utils.StrToNullStr(string(request.CareType)), + CarerGender: utils.StrToNullStr(request.CarerGender.String()), + RewardType: utils.StrToNullStr(request.RewardType.String()), + } + + if thumbnailID != nil { + params.ThumbnailID = utils.IntToNullInt64(int(*thumbnailID)) + } + + sosPost, err := q.WriteSOSPost(ctx, params) + if err != nil { + return databasegen.WriteSOSPostRow{}, pnd.FromPostgresError(err) + } + + return sosPost, nil +} + +func (service *SOSPostService) saveAllLinks( + ctx context.Context, q *databasegen.Queries, request *sospost.WriteSOSPostRequest, sosPostID int, +) *pnd.AppError { + if err := service.SaveSOSDates(ctx, q, request.Dates, sosPostID); err != nil { + return err + } + if err := service.SaveLinkSOSPostImage(ctx, q, request.ImageIDs, sosPostID); err != nil { + return err + } + if err := service.SaveLinkConditions(ctx, q, request.ConditionIDs, sosPostID); err != nil { + return err + } + return service.SaveLinkPets(ctx, q, request.PetIDs, sosPostID) +} + func (service *SOSPostService) FindSOSPosts( ctx context.Context, page, size int, sortBy, filterType string, ) (*sospost.FindSOSPostListView, *pnd.AppError) { @@ -154,14 +145,21 @@ func (service *SOSPostService) FindSOSPosts( return nil, err } - sosPosts, err := postgres.FindSOSPosts(ctx, tx, page, size, sortBy, filterType) - if err != nil { - return nil, err + sosPosts, err2 := databasegen.New(tx).FindSOSPosts(ctx, databasegen.FindSOSPostsParams{ + EarliestDateStartAt: utils.FormatDateString(time.Now().String()), + PetType: utils.StrToNullStr(filterType), + SortBy: utils.StrToNullStr(sortBy), + Limit: utils.IntToNullInt32(size + 1), + Offset: utils.IntToNullInt32((page - 1) * size), + }) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } - sosPostViews := sospost.FromEmptySOSPostInfoList(sosPosts) + sosPostInfoList := sospost.ToInfoListFromFindRow(sosPosts, page, size) + sosPostViews := sospost.FromEmptySOSPostInfoList(sosPostInfoList) - for _, sosPost := range sosPosts.Items { + for _, sosPost := range sosPostInfoList.Items { author, err := databasegen.New(tx).FindUser(ctx, databasegen.FindUserParams{ ID: utils.IntToNullInt32(sosPost.AuthorID), IncludeDeleted: true, @@ -169,7 +167,6 @@ func (service *SOSPostService) FindSOSPosts( if err != nil { return nil, pnd.FromPostgresError(err) } - sosPostView := sosPost.ToFindSOSPostInfoView( &user.WithoutPrivateInfo{ ID: int64(author.ID), @@ -181,7 +178,6 @@ func (service *SOSPostService) FindSOSPosts( sosPost.Pets.ToDetailViewList(), sosPost.Dates.ToSOSDateViewList(), ) - sosPostViews.Items = append(sosPostViews.Items, *sosPostView) } @@ -197,13 +193,22 @@ func (service *SOSPostService) FindSOSPostsByAuthorID( return nil, err } - sosPosts, err := postgres.FindSOSPostsByAuthorID(ctx, tx, authorID, page, size, sortBy, filterType) - if err != nil { - return nil, err + sosPosts, err2 := databasegen.New(tx).FindSOSPostsByAuthorID(ctx, databasegen.FindSOSPostsByAuthorIDParams{ + EarliestDateStartAt: utils.FormatDateString(time.Now().String()), + PetType: utils.StrToNullStr(filterType), + AuthorID: utils.IntToNullInt64(authorID), + SortBy: utils.StrToNullStr(sortBy), + Limit: utils.IntToNullInt32(size + 1), + Offset: utils.IntToNullInt32((page - 1) * size), + }) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } - sosPostViews := sospost.FromEmptySOSPostInfoList(sosPosts) - for _, sosPost := range sosPosts.Items { + sosPostInfoList := sospost.ToInfoListFromFindAuthorIDRow(sosPosts, page, size) + sosPostViews := sospost.FromEmptySOSPostInfoList(sosPostInfoList) + + for _, sosPost := range sosPostInfoList.Items { author, err := databasegen.New(tx).FindUser(ctx, databasegen.FindUserParams{ ID: utils.IntToNullInt32(sosPost.AuthorID), IncludeDeleted: true, @@ -236,13 +241,14 @@ func (service *SOSPostService) FindSOSPostByID(ctx context.Context, id int) (*so return nil, err } - sosPost, err := postgres.FindSOSPostByID(ctx, tx, id) - if err != nil { - return nil, err + sosPost, err2 := databasegen.New(tx).FindSOSPostByID(ctx, utils.IntToNullInt32(id)) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } + sosPostInfo := sospost.ToInfoFromFindByIDRow(sosPost) author, err2 := databasegen.New(tx).FindUser(ctx, databasegen.FindUserParams{ - ID: utils.IntToNullInt32(sosPost.AuthorID), + ID: utils.IntToNullInt32(sosPostInfo.AuthorID), IncludeDeleted: true, }) if err2 != nil { @@ -253,137 +259,69 @@ func (service *SOSPostService) FindSOSPostByID(ctx context.Context, id int) (*so return nil, err } - return sosPost.ToFindSOSPostInfoView( + return sosPostInfo.ToFindSOSPostInfoView( &user.WithoutPrivateInfo{ ID: int64(author.ID), Nickname: author.Nickname, ProfileImageURL: utils.NullStrToStrPtr(author.ProfileImageUrl), }, - media.ToListViewFromViewListForSOSPost(sosPost.Media), - soscondition.ToListViewFromViewForSOSPost(sosPost.Conditions), - sosPost.Pets.ToDetailViewList(), - sosPost.Dates.ToSOSDateViewList(), + media.ToListViewFromViewListForSOSPost(sosPostInfo.Media), + soscondition.ToListViewFromViewForSOSPost(sosPostInfo.Conditions), + sosPostInfo.Pets.ToDetailViewList(), + sosPostInfo.Dates.ToSOSDateViewList(), ), nil } func (service *SOSPostService) UpdateSOSPost( ctx context.Context, request *sospost.UpdateSOSPostRequest, -) (*sospost.UpdateSOSPostView, *pnd.AppError) { +) (*sospost.DetailView, *pnd.AppError) { tx, err := service.conn.BeginTx(ctx) defer tx.Rollback() if err != nil { return nil, err } q := databasegen.New(tx) - // 날짜 업데이트 - // 날짜 삭제 - err2 := service.DeleteLinkSOSPostDates(ctx, q, request.ID) - if err2 != nil { + + if err2 := service.updateAllLinks(ctx, q, request); err2 != nil { return nil, err2 } - // 날짜 저장 - err3 := service.SaveSOSDates(ctx, q, request.Dates, request.ID) - if err3 != nil { - return nil, err3 - } - // 이미지 업데이트 - // 이미지 삭제 - err4 := service.DeleteLinkSOSPostImages(ctx, q, request.ID) - if err4 != nil { - return nil, err4 - } - // 이미지 저장 - err5 := service.SaveLinkSOSPostImage(ctx, q, request.ImageIDs, request.ID) - if err5 != nil { - return nil, err5 - } - // 조건 업데이트 - // 조건 삭제 - err6 := service.DeleteLinkSOSPostConditions(ctx, q, request.ID) - if err6 != nil { - return nil, err6 - } - // 조건 저장 - err7 := service.SaveLinkConditions(ctx, q, request.ConditionIDs, request.ID) - if err7 != nil { - return nil, err7 - } - // 펫 업데이트 - // 펫 삭제 - err8 := service.DeleteLinkSOSPostPets(ctx, q, request.ID) - if err8 != nil { - return nil, err8 - } - // 펫 저장 - err9 := service.SaveLinkPets(ctx, q, request.PetIDs, request.ID) - if err9 != nil { - return nil, err9 - } - - // SOSPost 업데이트 + thumbnailID := setThumbnailID(request.ImageIDs) - updateSOSPost := databasegen.UpdateSOSPostRow{} - var err10 error - if thumbnailID == nil { - updateSOSPost, err10 = q.UpdateSOSPost(ctx, databasegen.UpdateSOSPostParams{ - ID: int32(request.ID), - Title: utils.StrToNullStr(request.Title), - Content: utils.StrToNullStr(request.Content), - Reward: utils.StrToNullStr(request.Reward), - CareType: utils.StrToNullStr(string(request.CareType)), - CarerGender: utils.StrToNullStr(request.CarerGender.String()), - RewardType: utils.StrToNullStr(request.RewardType.String()), - }) - if err10 != nil { - return nil, pnd.FromPostgresError(err10) - } - } - if thumbnailID != nil { - updateSOSPost, err10 = q.UpdateSOSPost(ctx, databasegen.UpdateSOSPostParams{ - ID: int32(request.ID), - Title: utils.StrToNullStr(request.Title), - Content: utils.StrToNullStr(request.Content), - Reward: utils.StrToNullStr(request.Reward), - CareType: utils.StrToNullStr(string(request.CareType)), - CarerGender: utils.StrToNullStr(request.CarerGender.String()), - RewardType: utils.StrToNullStr(request.RewardType.String()), - ThumbnailID: utils.IntToNullInt64(int(*thumbnailID)), - }) - if err10 != nil { - return nil, pnd.FromPostgresError(err10) - } + updateSOSPost, err := service.updateSOSPost(ctx, q, request, thumbnailID) + if err != nil { + return nil, err } - mediaData, err11 := databasegen.New(tx).FindResourceMedia(ctx, databasegen.FindResourceMediaParams{ + mediaData, err2 := databasegen.New(tx).FindResourceMedia(ctx, databasegen.FindResourceMediaParams{ ResourceID: utils.IntToNullInt64(int(updateSOSPost.ID)), ResourceType: utils.StrToNullStr(resourcemedia.SOSResourceType.String()), }) if err2 != nil { - return nil, pnd.FromPostgresError(err11) + return nil, pnd.FromPostgresError(err2) } - conditionList, err11 := databasegen.New(tx).FindSOSPostConditions(ctx, databasegen.FindSOSPostConditionsParams{ + conditionList, err2 := databasegen.New(tx).FindSOSPostConditions(ctx, databasegen.FindSOSPostConditionsParams{ SosPostID: utils.IntToNullInt64(int(updateSOSPost.ID)), }) - if err11 != nil { - return nil, pnd.FromPostgresError(err11) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } - petRows, err12 := databasegen.New(tx).FindPetsBySOSPostID(ctx, utils.IntToNullInt64(int(updateSOSPost.ID))) - if err12 != nil { - return nil, pnd.FromPostgresError(err12) + petRows, err2 := databasegen.New(tx).FindPetsBySOSPostID(ctx, utils.IntToNullInt64(int(updateSOSPost.ID))) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } - dates, err13 := q.FindDatesBySOSPostID(ctx, utils.IntToNullInt64(request.ID)) - if err3 != nil { - return nil, pnd.FromPostgresError(err13) + dates, err2 := q.FindDatesBySOSPostID(ctx, utils.IntToNullInt64(request.ID)) + if err2 != nil { + return nil, pnd.FromPostgresError(err2) } if err := tx.Commit(); err != nil { return nil, err } - return sospost.ToUpdateSOSPostView( + return sospost.UpdateDetailView( updateSOSPost, media.ToListViewFromResourceMediaRows(mediaData), soscondition.ToListViewFromSOSPostConditions(conditionList), @@ -392,6 +330,61 @@ func (service *SOSPostService) UpdateSOSPost( ), nil } +func (service *SOSPostService) updateSOSPost( + ctx context.Context, q *databasegen.Queries, request *sospost.UpdateSOSPostRequest, thumbnailID *int64, +) (databasegen.UpdateSOSPostRow, *pnd.AppError) { + params := databasegen.UpdateSOSPostParams{ + ID: int32(request.ID), + Title: utils.StrToNullStr(request.Title), + Content: utils.StrToNullStr(request.Content), + Reward: utils.StrToNullStr(request.Reward), + CareType: utils.StrToNullStr(string(request.CareType)), + CarerGender: utils.StrToNullStr(request.CarerGender.String()), + RewardType: utils.StrToNullStr(request.RewardType.String()), + } + + if thumbnailID != nil { + params.ThumbnailID = utils.IntToNullInt64(int(*thumbnailID)) + } + + updateSOSPost, err := q.UpdateSOSPost(ctx, params) + if err != nil { + return databasegen.UpdateSOSPostRow{}, pnd.FromPostgresError(err) + } + + return updateSOSPost, nil +} + +func (service *SOSPostService) updateAllLinks( + ctx context.Context, q *databasegen.Queries, request *sospost.UpdateSOSPostRequest, +) *pnd.AppError { + if err := service.DeleteLinkSOSPostDates(ctx, q, request.ID); err != nil { + return err + } + if err := service.SaveSOSDates(ctx, q, request.Dates, request.ID); err != nil { + return err + } + + if err := service.DeleteLinkSOSPostImages(ctx, q, request.ID); err != nil { + return err + } + if err := service.SaveLinkSOSPostImage(ctx, q, request.ImageIDs, request.ID); err != nil { + return err + } + + if err := service.DeleteLinkSOSPostConditions(ctx, q, request.ID); err != nil { + return err + } + if err := service.SaveLinkConditions(ctx, q, request.ConditionIDs, request.ID); err != nil { + return err + } + + if err := service.DeleteLinkSOSPostPets(ctx, q, request.ID); err != nil { + return err + } + return service.SaveLinkPets(ctx, q, request.PetIDs, request.ID) +} + func (service *SOSPostService) CheckUpdatePermission( ctx context.Context, fbUID string, sosPostID int, ) (bool, *pnd.AppError) { @@ -408,22 +401,23 @@ func (service *SOSPostService) CheckUpdatePermission( return false, pnd.FromPostgresError(err2) } - sosPost, err := postgres.FindSOSPostByID(ctx, tx, sosPostID) - if err != nil { - return false, err + sosPost, err2 := databasegen.New(tx).FindSOSPostByID(ctx, utils.IntToNullInt32(sosPostID)) + if err2 != nil { + return false, pnd.FromPostgresError(err2) } + sosPostInfo := sospost.ToInfoFromFindByIDRow(sosPost) if err := tx.Commit(); err != nil { return false, err } - return int(userData.ID) == sosPost.AuthorID, nil + return int(userData.ID) == sosPostInfo.AuthorID, nil } func (service *SOSPostService) SaveSOSDates( - ctx context.Context, tx *databasegen.Queries, Dates []sospost.SOSDateView, sosPostID int, + ctx context.Context, tx *databasegen.Queries, dates []sospost.SOSDateView, sosPostID int, ) *pnd.AppError { - for _, date := range Dates { + for _, date := range dates { dateStartAt, err := utils.StrToNullTime(date.DateStartAt) if err != nil { return err @@ -441,7 +435,6 @@ func (service *SOSPostService) SaveSOSDates( return pnd.FromPostgresError(err2) } - // 날짜와 SOSPost 다대다 저장 err3 := tx.LinkSOSPostDate(ctx, databasegen.LinkSOSPostDateParams{ SosPostID: utils.IntToNullInt64(sosPostID), SosDatesID: utils.IntToNullInt64(int(d.ID)), @@ -454,9 +447,9 @@ func (service *SOSPostService) SaveSOSDates( } func (service *SOSPostService) SaveLinkSOSPostImage( - ctx context.Context, tx *databasegen.Queries, ImageIDs []int64, sosPostID int, + ctx context.Context, tx *databasegen.Queries, imageIDs []int64, sosPostID int, ) *pnd.AppError { - for _, mediaID := range ImageIDs { + for _, mediaID := range imageIDs { err := tx.LinkResourceMedia(ctx, databasegen.LinkResourceMediaParams{ MediaID: utils.IntToNullInt64(int(mediaID)), ResourceID: utils.IntToNullInt64(sosPostID), @@ -470,9 +463,9 @@ func (service *SOSPostService) SaveLinkSOSPostImage( } func (service *SOSPostService) SaveLinkConditions( - ctx context.Context, tx *databasegen.Queries, ConditionIDs []int, sosPostID int, + ctx context.Context, tx *databasegen.Queries, conditionIDs []int, sosPostID int, ) *pnd.AppError { - for _, conditionID := range ConditionIDs { + for _, conditionID := range conditionIDs { err := tx.LinkSOSPostCondition(ctx, databasegen.LinkSOSPostConditionParams{ SosPostID: utils.IntToNullInt64(sosPostID), SosConditionID: utils.IntToNullInt64(conditionID), @@ -485,9 +478,9 @@ func (service *SOSPostService) SaveLinkConditions( } func (service *SOSPostService) SaveLinkPets( - ctx context.Context, tx *databasegen.Queries, PetIDs []int64, sosPostID int, + ctx context.Context, tx *databasegen.Queries, petIDs []int64, sosPostID int, ) *pnd.AppError { - for _, petID := range PetIDs { + for _, petID := range petIDs { err := tx.LinkSOSPostPet(ctx, databasegen.LinkSOSPostPetParams{ SosPostID: utils.IntToNullInt64(sosPostID), PetID: utils.IntToNullInt64(int(petID)), diff --git a/internal/service/tests/sos_post_service_test.go b/internal/service/tests/sos_post_service_test.go index c8c95119..beffb466 100644 --- a/internal/service/tests/sos_post_service_test.go +++ b/internal/service/tests/sos_post_service_test.go @@ -107,7 +107,7 @@ func TestFindSOSPosts(t *testing.T) { conditionIDs := []int{1, 2} sosPostRequests := make([]sospost.WriteSOSPostRequest, 0) - var sosPosts []sospost.WriteSOSPostView + var sosPosts []sospost.DetailView for i := 1; i < 4; i++ { request := tests.NewDummyWriteSOSPostRequest(imageIDs, petIDs, i) sosPost, _ := sosPostService.WriteSOSPost( diff --git a/queries/media.sql b/queries/media.sql index 27ca3b58..24cbdaed 100644 --- a/queries/media.sql +++ b/queries/media.sql @@ -17,3 +17,4 @@ FROM media WHERE (id = sqlc.narg('id') OR sqlc.narg('id') IS NULL) AND (sqlc.arg('include_deleted')::BOOLEAN = TRUE OR (sqlc.arg('include_deleted')::BOOLEAN = FALSE AND deleted_at IS NULL)); + diff --git a/queries/sos_posts.sql b/queries/sos_posts.sql index 511df195..53181fbe 100644 --- a/queries/sos_posts.sql +++ b/queries/sos_posts.sql @@ -78,12 +78,17 @@ FROM LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id WHERE - v_sos_posts.earliest_date_start_at >= $1 - AND ($2) + v_sos_posts.earliest_date_start_at >= sqlc.narg('earliest_date_start_at') + AND (sqlc.narg('pet_type') = 'all' OR NOT EXISTS + (SELECT 1 + FROM unnest(pet_type_list) AS pet_type + WHERE pet_type <> sqlc.narg('pet_type'))) ORDER BY - $3 -LIMIT $4 - OFFSET $5; + CASE WHEN sqlc.narg('sort_by') = 'newest' THEN v_sos_posts.created_at END DESC, + CASE WHEN sqlc.narg('sort_by') = 'deadline' THEN v_sos_posts.earliest_date_start_at END +LIMIT sqlc.narg('limit') + OFFSET sqlc.narg('offset'); + -- name: FindSOSPostsByAuthorID :many SELECT @@ -108,13 +113,17 @@ FROM LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id WHERE - v_sos_posts.earliest_date_start_at >= $1 - AND v_sos_posts.author_id = $2 - AND ($3) + v_sos_posts.earliest_date_start_at >= sqlc.narg('earliest_date_start_at') + AND v_sos_posts.author_id = sqlc.narg('author_id') + AND (sqlc.narg('pet_type') = 'all' OR NOT EXISTS + (SELECT 1 + FROM unnest(pet_type_list) AS pet_type + WHERE pet_type <> sqlc.narg('pet_type'))) ORDER BY - $4 -LIMIT $5 - OFFSET $6; + CASE WHEN sqlc.narg('sort_by') = 'newest' THEN v_sos_posts.created_at END DESC, + CASE WHEN sqlc.narg('sort_by') = 'deadline' THEN v_sos_posts.earliest_date_start_at END +LIMIT sqlc.narg('limit') + OFFSET sqlc.narg('offset'); -- name: FindSOSPostByID :one SELECT @@ -139,7 +148,7 @@ FROM LEFT JOIN v_media_for_sos_posts ON v_sos_posts.id = v_media_for_sos_posts.sos_post_id LEFT JOIN v_conditions ON v_sos_posts.id = v_conditions.sos_post_id WHERE - v_sos_posts.id = $1; + v_sos_posts.id = sqlc.narg('id'); -- name: FindDatesBySOSPostID :many SELECT @@ -154,7 +163,7 @@ FROM sos_posts_dates ON sos_dates.id = sos_posts_dates.sos_dates_id WHERE - sos_posts_dates.sos_post_id = $1 AND + sos_posts_dates.sos_post_id = sqlc.narg('id') AND sos_posts_dates.deleted_at IS NULL; -- name: UpdateSOSPost :one