hiling_go/api/location.go

348 lines
9.5 KiB
Go
Raw Normal View History

package api
import (
"fmt"
"net/http"
"os"
"path/filepath"
"time"
db "git.nochill.in/nochill/hiling_go/db/sqlc"
"git.nochill.in/nochill/hiling_go/util"
"github.com/gin-gonic/gin"
2024-03-04 16:09:55 +07:00
"github.com/jackc/pgx/v5"
2024-02-06 11:55:25 +07:00
"github.com/jackc/pgx/v5/pgtype"
"github.com/meilisearch/meilisearch-go"
2023-09-14 14:58:16 +07:00
ysqlc "github.com/yiplee/sqlc"
)
type createLocationReq struct {
Address string `form:"address" binding:"required"`
Name string `form:"name" binding:"required"`
SubmittedBy int32 `form:"submitted_by" binding:"required,number"`
RegencyID int16 `form:"regency_id" binding:"required,number"`
2023-10-03 14:56:57 +07:00
LocationType string `form:"location_type" binding:"required"`
GoogleMapsLink string `form:"google_maps_link"`
}
func (server *Server) createLocation(ctx *gin.Context) {
var req createLocationReq
var imgPath string
2023-10-03 19:44:31 +07:00
var tempImg []db.CreateImageParams
if err := ctx.Bind(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
2023-10-03 14:56:57 +07:00
form, _ := ctx.MultipartForm()
thumbnails := form.File["thumbnail"]
if len(thumbnails) > 0 {
for _, img := range thumbnails {
fileExt := filepath.Ext(img.Filename)
now := time.Now()
dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name)
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
imgPath = fmt.Sprintf("%s/%s", dir, osFilename)
if _, err := os.Stat(dir); os.IsNotExist(err) {
os.Mkdir(dir, 0775)
}
if err := ctx.SaveUploadedFile(img, imgPath); err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
return
}
2023-10-03 19:44:31 +07:00
}
2023-10-03 14:56:57 +07:00
2023-10-03 19:44:31 +07:00
arg := db.CreateLocationTxParams{
Address: req.Address,
Name: req.Name,
LocationType: db.LocationType(req.LocationType),
SubmittedBy: req.SubmittedBy,
RegencyID: req.RegencyID,
IsDeleted: false,
2024-03-04 16:09:55 +07:00
ApprovedBy: pgtype.Int4{Int32: 0, Valid: false},
2024-02-06 11:55:25 +07:00
GoogleMapsLink: pgtype.Text{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
2023-10-03 19:44:31 +07:00
Thumbnail: tempImg,
2023-10-03 14:56:57 +07:00
}
2023-10-03 19:44:31 +07:00
err := server.Store.CreateLocationTx(ctx, arg)
2023-10-03 14:56:57 +07:00
if err != nil {
2023-10-03 19:44:31 +07:00
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save Location"))
2023-10-03 14:56:57 +07:00
return
}
2023-10-03 19:44:31 +07:00
2023-10-03 14:56:57 +07:00
}
ctx.Writer.WriteHeader(http.StatusOK)
}
type getListLocationsReq struct {
Page int32 `form:"page" binding:"required,min=1"`
PageSize int32 `form:"page_size" binding:"required,min=5"`
}
func (server *Server) getListLocations(ctx *gin.Context) {
var req getListLocationsReq
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
2023-09-14 14:58:16 +07:00
locations, err := server.Store.GetListLocations(ysqlc.Build(ctx, func(builder *ysqlc.Builder) {
2023-09-14 13:49:03 +07:00
builder.Limit(int(req.PageSize))
builder.Offset(int(req.Page-1) * int(req.PageSize))
builder.Order("created_at ASC")
}))
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
return
}
ctx.JSON(http.StatusOK, locations)
}
2023-09-17 16:32:48 +07:00
type getTopListLocationsReq struct {
Page int32 `form:"page" binding:"required,min=1"`
PageSize int32 `form:"page_size" binding:"required,min=5"`
OrderBy int16 `form:"order_by" binding:"numeric,min=1,max=3"`
2024-03-04 16:09:55 +07:00
RegionType int32 `form:"region_type" binding:"numeric,min=0,max=7"`
2023-09-17 16:32:48 +07:00
}
func (server *Server) getTopListLocations(ctx *gin.Context) {
var req getTopListLocationsReq
var orderby string
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
switch req.OrderBy {
case 1:
orderby = "iq2.avg_bayes"
case 2:
orderby = "iq2.critic_bayes"
case 3:
orderby = "iq2.user_bayes"
}
arg := db.GetTopListLocationsParams{
Limit: req.PageSize,
Offset: (req.Page - 1) * req.PageSize,
OrderBy: orderby,
2024-03-04 16:09:55 +07:00
RegionType: pgtype.Int4{Valid: req.RegionType > 0, Int32: req.RegionType},
2023-09-17 16:32:48 +07:00
}
locations, err := server.Store.GetTopListLocations(ctx, arg)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
return
}
ctx.JSON(http.StatusOK, locations)
2023-09-23 15:24:21 +07:00
// str, err := ctx.Cookie("kek123429")
// if err != nil {
// ctx.JSON(http.StatusUnauthorized, ErrorResponse(err, ""))
// }
// ctx.JSON(http.StatusOK, gin.H{
// "str": str,
// "res": locations,
// })
2023-09-17 16:32:48 +07:00
}
2023-09-14 22:51:01 +07:00
type getListRecentLocationsWithRatingsReq struct {
Page int32 `form:"page_size" binding:"required,min=1"`
LocationType string `form:"location_type"`
Region string `form:"regions"`
2023-09-14 22:51:01 +07:00
}
func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) {
var req getListRecentLocationsWithRatingsReq
2023-09-14 22:51:01 +07:00
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
locations, err := server.Store.GetListRecentLocationsWithRatings(ctx, db.GetListRecentLocationsParams{
Limit: req.Page,
Regions: req.Region,
LocationTypes: req.LocationType,
})
2023-09-14 22:51:01 +07:00
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
return
}
ctx.JSON(http.StatusOK, locations)
2023-09-14 22:51:01 +07:00
}
type getLocationReq struct {
ID int32 `uri:"location_id" binding:"required"`
}
func (server *Server) getLocation(ctx *gin.Context) {
var req getLocationReq
if err := ctx.ShouldBindUri(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
location, err := server.Store.GetLocation(ctx, req.ID)
if err != nil {
2024-03-04 16:09:55 +07:00
if err == pgx.ErrNoRows {
2023-09-14 13:49:03 +07:00
ctx.JSON(http.StatusNotFound, ErrorResponse(err, ""))
return
}
2023-09-20 13:37:14 +07:00
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while retrieving location detail"))
return
}
2023-09-20 13:37:14 +07:00
tags, err := server.Store.GetLocationTag(ctx, req.ID)
if err != nil {
ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "Something went wrong while retrieving location tags"))
return
}
users_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{
LocationID: req.ID,
Limit: 5,
Offset: 0,
IsCritics: false,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
return
}
critics_reviews, err := server.Store.GetListLocationReviews(ctx, db.GetListLocationReviewsParams{
LocationID: req.ID,
Limit: 20,
Offset: 0,
IsCritics: true,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to receive user reviews for this location"))
return
}
2023-09-20 13:37:14 +07:00
res := gin.H{
"tags": tags,
"detail": location,
"users_review": users_reviews,
"critics_review": critics_reviews,
2023-09-20 13:37:14 +07:00
}
ctx.JSON(http.StatusOK, res)
}
type getListLocationReviewsReq struct {
PageSize int8 `form:"page_size" binding:"required"`
Page int8 `form:"page" binding:"required,min=1"`
Type int8 `form:"type" binding:"required"` // 0 = all, 1 = critics,2 = users
LocationID int32 `form:"location_id" binding:"required"`
}
type getListLocationReviewsRes struct {
CriticsReviews []db.GetListLocationReviewsRow `json:"critics_reviews"`
UsersReviews []db.GetListLocationReviewsRow `json:"users_reviews"`
}
func (server *Server) getListLocationReviews(ctx *gin.Context) {
var req getListLocationReviewsReq
var res getListLocationReviewsRes
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
if req.Type-1 == 1 || req.Type-1 == 0 {
arg := db.GetListLocationReviewsParams{
LocationID: req.LocationID,
Offset: (req.Page - 1) * req.PageSize,
Limit: req.PageSize,
IsCritics: true,
}
reviews, err := server.Store.GetListLocationReviews(ctx, arg)
if err != nil {
2024-03-04 16:09:55 +07:00
if err == pgx.ErrNoRows {
ctx.JSON(http.StatusNotFound, ErrorResponse(err, "There's no critics reviews for this location yet"))
return
}
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get critics reviews for this location"))
return
}
res.CriticsReviews = reviews
}
if req.Type-1 == 2 || req.Type-1 == 0 {
arg := db.GetListLocationReviewsParams{
LocationID: req.LocationID,
Limit: req.PageSize,
Offset: (req.Page - 1) * req.PageSize,
IsCritics: false,
}
reviews, err := server.Store.GetListLocationReviews(ctx, arg)
if err != nil {
2024-03-04 16:09:55 +07:00
if err == pgx.ErrNoRows {
ctx.JSON(http.StatusNotFound, ErrorResponse(err, "There's no critics reviews for this location yet"))
return
}
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get critics reviews for this location"))
return
}
res.UsersReviews = reviews
}
ctx.JSON(http.StatusOK, res)
}
type searchLocationsParams struct {
Name string `form:"name"`
Filter string `form:"filter"`
}
func (server *Server) searchLocations(ctx *gin.Context) {
var req searchLocationsParams
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
searchRequest := meilisearch.SearchRequest{
Limit: 7,
Offset: 0,
Filter: req.Filter,
}
searchRes, err := server.MeilisearchClient.Index("locations").Search(req.Name, &searchRequest)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, fmt.Sprintf("Something went wrong while try to search locations %s", req.Name)))
return
}
ctx.JSON(http.StatusOK, searchRes.Hits)
}