Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions internal/common/techbloghub.paging.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package common

import "strconv"

const (
DEFAULT_SIZE = 20
MAXIMUM_SIZE = 100

CURSOR_DEFAULT = 0
)

type TechbloghubPaging struct {
Cursor int
Size int
}

func GenerateTechPaging(cursorStr string, sizeStr string) TechbloghubPaging {
return TechbloghubPaging{
Cursor: toCursor(cursorStr),
Size: toSize(sizeStr),
}
}

func toCursor(cursorStr string) int {
cursor, err := strconv.Atoi(cursorStr)
if err != nil {
return CURSOR_DEFAULT
}
return cursor
}

func toSize(sizeStr string) int {
size, err := strconv.Atoi(sizeStr)
if err != nil {
return DEFAULT_SIZE
}
if size >= MAXIMUM_SIZE {
return MAXIMUM_SIZE
}
return size
}

func (t TechbloghubPaging) HasNextPage(size int) bool {
return t.Size < size
}
95 changes: 95 additions & 0 deletions internal/http/handler/postinghandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package handler

import (
"net/http"
"time"

"github.com/gin-gonic/gin"
"github.com/techbloghub/server/ent"
"github.com/techbloghub/server/ent/posting"
"github.com/techbloghub/server/internal/common"
)

type TitleSearchResponse struct {
ID int `json:"posting_id"`
Title string `json:"title"`
Url string `json:"url"`
Company string `json:"company"`
Logo string `json:"logo"`
Tags []string `json:"tags"`
CreateTime time.Time `json:"create_time"`
UpdateTime time.Time `json:"update_time"`
PublishedTime time.Time `json:"published_time"`
}

type PostingSearchResponses struct {
Count int `json:"count"`
Postings []TitleSearchResponse `json:"postings"`
HasNextPage bool `json:"has_next_page"`
}

func GetPostings(client *ent.Client) gin.HandlerFunc {
return func(c *gin.Context) {
titleSearchParam := c.DefaultQuery("title", "")
paging := common.GenerateTechPaging(c.Query("cursor"), c.Query("size"))

totalCount, err := countTotalPostings(client, titleSearchParam, c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

postings, err := fetchPostings(client, titleSearchParam, paging, c)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}

c.JSON(http.StatusOK, PostingSearchResponses{
Count: totalCount,
Postings: convertToTitleSearchResponse(postings),
HasNextPage: paging.HasNextPage(totalCount),
})
}
}

func fetchPostings(client *ent.Client, title string, paging common.TechbloghubPaging, c *gin.Context) ([]*ent.Posting, error) {
query := client.Posting.Query().WithCompany()
if title != "" {
query = query.Where(posting.TitleContainsFold(title))
}
if paging.Cursor > 0 {
query = query.Where(posting.IDLT(paging.Cursor))
}

return query.Order(
ent.Desc(posting.FieldPublishedAt),
ent.Desc(posting.FieldID),
).Limit(paging.Size).All(c)
}

func countTotalPostings(client *ent.Client, title string, c *gin.Context) (int, error) {
query := client.Posting.Query()
if title != "" {
query = query.Where(posting.TitleContainsFold(title))
}
return query.Count(c)
}

func convertToTitleSearchResponse(postings []*ent.Posting) []TitleSearchResponse {
responses := make([]TitleSearchResponse, len(postings))
for i, posting := range postings {
responses[i] = TitleSearchResponse{
ID: posting.ID,
Title: posting.Title,
Url: posting.URL.String(),
Company: posting.Edges.Company.Name,
Logo: posting.Edges.Company.LogoURL.String(),
Tags: posting.Tags.ToStringSlice(),
CreateTime: posting.CreateTime,
UpdateTime: posting.UpdateTime,
PublishedTime: posting.PublishedAt,
}
}
return responses
}
3 changes: 3 additions & 0 deletions internal/http/router/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,7 @@ func InitRouter(r *gin.Engine, client *ent.Client) {

// 태그 전체 목록 조회
r.GET("/tags", handler.GetTags(client))

// 포스팅(게시글 조회)
r.GET("/postings", handler.GetPostings(client))
}
27 changes: 23 additions & 4 deletions internal/schemasupport/posting_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,29 @@ import (

type PostingTags []string

func (s PostingTags) Value() (driver.Value, error) {
return pq.Array(s).Value()
func (t *PostingTags) Scan(value interface{}) error {
if value == nil {
*t = []string{}
return nil
}

var arr []string
if err := pq.Array(&arr).Scan(value); err != nil {
return err
}

*t = arr
return nil
}

func (t PostingTags) Value() (driver.Value, error) {
return pq.Array(t).Value()
}

func (s *PostingTags) Scan(value interface{}) error {
return pq.Array(s).Scan(value)
func (t *PostingTags) ToStringSlice() []string {
tags := make([]string, len(*t))
for i, tag := range *t {
tags[i] = tag
}
return tags
}