2023-09-13 21:42:31 +07:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"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"
|
|
|
|
"github.com/lib/pq"
|
2023-09-14 14:58:16 +07:00
|
|
|
ysqlc "github.com/yiplee/sqlc"
|
2023-09-13 21:42:31 +07:00
|
|
|
)
|
|
|
|
|
|
|
|
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"`
|
|
|
|
GoogleMapsLink string `form:"google_maps_link"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (server *Server) createLocation(ctx *gin.Context) {
|
|
|
|
var req createLocationReq
|
|
|
|
var imgPath string
|
|
|
|
|
|
|
|
var thumbnail, _ = ctx.FormFile("thumbnail")
|
|
|
|
|
|
|
|
if err := ctx.Bind(&req); err != nil {
|
|
|
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if thumbnail != nil {
|
|
|
|
img := thumbnail
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
arg := db.CreateLocationParams{
|
|
|
|
Address: req.Address,
|
|
|
|
Name: req.Name,
|
|
|
|
SubmittedBy: req.SubmittedBy,
|
|
|
|
RegencyID: req.RegencyID,
|
|
|
|
GoogleMapsLink: sql.NullString{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := server.Store.CreateLocation(ctx, arg)
|
2023-09-14 13:49:03 +07:00
|
|
|
|
2023-09-13 21:42:31 +07:00
|
|
|
if err != nil {
|
|
|
|
if pqErr, ok := err.(*pq.Error); ok {
|
2023-09-14 13:49:03 +07:00
|
|
|
switch pqErr.Code.Name() {
|
|
|
|
case "foreign_key_violation", "unique_violation":
|
|
|
|
if pqErr.Constraint == "locations_regency_id_fkey" {
|
|
|
|
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no regency with id: %d", arg.RegencyID)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if pqErr.Constraint == "submitted_by_fkey" {
|
|
|
|
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no user with id: %d", arg.SubmittedBy)))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2023-09-13 21:42:31 +07:00
|
|
|
}
|
|
|
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
|
|
|
}))
|
2023-09-13 21:42:31 +07:00
|
|
|
|
|
|
|
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"`
|
|
|
|
RegionType int16 `form:"region_type" binding:"numeric,min=0,max=7"`
|
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
|
|
|
RegionType: sql.NullInt16{Valid: req.RegionType > 0, Int16: req.RegionType},
|
|
|
|
}
|
|
|
|
|
|
|
|
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"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) {
|
|
|
|
var req getListRecentLocationsWithRatingsReq
|
|
|
|
if err := ctx.ShouldBindQuery(&req); err != nil {
|
|
|
|
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
locations, err := server.Store.GetListRecentLocationsWithRatings(ctx, req.Page)
|
|
|
|
if err != nil {
|
|
|
|
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx.JSON(http.StatusOK, locations)
|
|
|
|
}
|
|
|
|
|
2023-09-13 21:42:31 +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 {
|
2023-09-14 13:49:03 +07:00
|
|
|
if err == sql.ErrNoRows {
|
|
|
|
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"))
|
2023-09-13 21:42:31 +07:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2023-09-27 21:58:14 +07:00
|
|
|
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{
|
2023-09-27 21:58:14 +07:00
|
|
|
"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)
|
2023-09-13 21:42:31 +07:00
|
|
|
}
|
2023-09-27 21:58:14 +07:00
|
|
|
|
|
|
|
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 {
|
|
|
|
if err == sql.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 {
|
|
|
|
if err == sql.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)
|
|
|
|
|
|
|
|
}
|