mirror of
https://github.com/wgh136/nysoure.git
synced 2025-09-27 04:17:23 +00:00
Improve Comment model to support multiple comment type.
This commit is contained in:
@@ -571,13 +571,13 @@ class Network {
|
|||||||
return `${this.apiBaseUrl}/files/download/${fileId}?cf_token=${cfToken}`;
|
return `${this.apiBaseUrl}/files/download/${fileId}?cf_token=${cfToken}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createComment(
|
async createResourceComment(
|
||||||
resourceID: number,
|
resourceID: number,
|
||||||
content: string,
|
content: string,
|
||||||
images: number[],
|
images: number[],
|
||||||
): Promise<Response<any>> {
|
): Promise<Response<any>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.post(`${this.apiBaseUrl}/comments/${resourceID}`, {
|
axios.post(`${this.apiBaseUrl}/comments/resource/${resourceID}`, {
|
||||||
content,
|
content,
|
||||||
images,
|
images,
|
||||||
}),
|
}),
|
||||||
@@ -597,12 +597,12 @@ class Network {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async listComments(
|
async listResourceComments(
|
||||||
resourceID: number,
|
resourceID: number,
|
||||||
page: number = 1,
|
page: number = 1,
|
||||||
): Promise<PageResponse<Comment>> {
|
): Promise<PageResponse<Comment>> {
|
||||||
return this._callApi(() =>
|
return this._callApi(() =>
|
||||||
axios.get(`${this.apiBaseUrl}/comments/${resourceID}`, {
|
axios.get(`${this.apiBaseUrl}/comments/resource/${resourceID}`, {
|
||||||
params: { page },
|
params: { page },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@@ -1221,7 +1221,7 @@ function CommentInput({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const res = await network.createComment(
|
const res = await network.createResourceComment(
|
||||||
resourceId,
|
resourceId,
|
||||||
commentContent,
|
commentContent,
|
||||||
imageIds,
|
imageIds,
|
||||||
@@ -1339,7 +1339,7 @@ function CommentsList({
|
|||||||
const [comments, setComments] = useState<Comment[] | null>(null);
|
const [comments, setComments] = useState<Comment[] | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
network.listComments(resourceId, page).then((res) => {
|
network.listResourceComments(resourceId, page).then((res) => {
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
setComments(res.data!);
|
setComments(res.data!);
|
||||||
maxPageCallback(res.totalPages || 1);
|
maxPageCallback(res.totalPages || 1);
|
||||||
|
@@ -11,14 +11,16 @@ import (
|
|||||||
|
|
||||||
func AddCommentRoutes(router fiber.Router) {
|
func AddCommentRoutes(router fiber.Router) {
|
||||||
api := router.Group("/comments")
|
api := router.Group("/comments")
|
||||||
api.Post("/:resourceID", createComment)
|
api.Post("/resource/:resourceID", createResourceComment)
|
||||||
api.Get("/:resourceID", listComments)
|
api.Post("/reply/:commentID", createReplyComment)
|
||||||
api.Get("/user/:username", listCommentsWithUser)
|
api.Get("/resource/:resourceID", listResourceComments)
|
||||||
|
api.Get("/reply/:commentID", listResourceComments)
|
||||||
|
api.Get("/user/:username", listCommentsByUser)
|
||||||
api.Put("/:commentID", updateComment)
|
api.Put("/:commentID", updateComment)
|
||||||
api.Delete("/:commentID", deleteComment)
|
api.Delete("/:commentID", deleteComment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createComment(c fiber.Ctx) error {
|
func createResourceComment(c fiber.Ctx) error {
|
||||||
userID, ok := c.Locals("uid").(uint)
|
userID, ok := c.Locals("uid").(uint)
|
||||||
if !ok {
|
if !ok {
|
||||||
return model.NewRequestError("You must be logged in to comment")
|
return model.NewRequestError("You must be logged in to comment")
|
||||||
@@ -38,7 +40,7 @@ func createComment(c fiber.Ctx) error {
|
|||||||
return model.NewRequestError("Content cannot be empty")
|
return model.NewRequestError("Content cannot be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
comment, err := service.CreateComment(req, userID, uint(resourceID), c.IP())
|
comment, err := service.CreateComment(req, userID, uint(resourceID), c.IP(), model.CommentTypeResource)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -49,7 +51,38 @@ func createComment(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func listComments(c fiber.Ctx) error {
|
func createReplyComment(c fiber.Ctx) error {
|
||||||
|
userID, ok := c.Locals("uid").(uint)
|
||||||
|
if !ok {
|
||||||
|
return model.NewRequestError("You must be logged in to reply")
|
||||||
|
}
|
||||||
|
commentIDStr := c.Params("commentID")
|
||||||
|
commentID, err := strconv.Atoi(commentIDStr)
|
||||||
|
if err != nil {
|
||||||
|
return model.NewRequestError("Invalid comment ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
var req service.CommentRequest
|
||||||
|
if err := c.Bind().JSON(&req); err != nil {
|
||||||
|
return model.NewRequestError("Invalid request format")
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Content == "" {
|
||||||
|
return model.NewRequestError("Content cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
comment, err := service.CreateComment(req, userID, uint(commentID), c.IP(), model.CommentTypeReply)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.Status(fiber.StatusCreated).JSON(model.Response[model.CommentView]{
|
||||||
|
Success: true,
|
||||||
|
Data: *comment,
|
||||||
|
Message: "Reply created successfully",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func listResourceComments(c fiber.Ctx) error {
|
||||||
resourceIDStr := c.Params("resourceID")
|
resourceIDStr := c.Params("resourceID")
|
||||||
resourceID, err := strconv.Atoi(resourceIDStr)
|
resourceID, err := strconv.Atoi(resourceIDStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -60,7 +93,7 @@ func listComments(c fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return model.NewRequestError("Invalid page number")
|
return model.NewRequestError("Invalid page number")
|
||||||
}
|
}
|
||||||
comments, totalPages, err := service.ListComments(uint(resourceID), page)
|
comments, totalPages, err := service.ListResourceComments(uint(resourceID), page)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -72,7 +105,7 @@ func listComments(c fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func listCommentsWithUser(c fiber.Ctx) error {
|
func listCommentsByUser(c fiber.Ctx) error {
|
||||||
username := c.Params("username")
|
username := c.Params("username")
|
||||||
if username == "" {
|
if username == "" {
|
||||||
return model.NewRequestError("Username is required")
|
return model.NewRequestError("Username is required")
|
||||||
|
@@ -6,13 +6,14 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func CreateComment(content string, userID uint, resourceID uint, imageIDs []uint) (model.Comment, error) {
|
func CreateComment(content string, userID uint, resourceID uint, imageIDs []uint, cType model.CommentType) (model.Comment, error) {
|
||||||
var comment model.Comment
|
var comment model.Comment
|
||||||
err := db.Transaction(func(tx *gorm.DB) error {
|
err := db.Transaction(func(tx *gorm.DB) error {
|
||||||
comment = model.Comment{
|
comment = model.Comment{
|
||||||
Content: content,
|
Content: content,
|
||||||
UserID: userID,
|
UserID: userID,
|
||||||
ResourceID: resourceID,
|
RefID: resourceID,
|
||||||
|
Type: cType,
|
||||||
}
|
}
|
||||||
if err := tx.Create(&comment).Error; err != nil {
|
if err := tx.Create(&comment).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -49,11 +50,24 @@ func GetCommentByResourceID(resourceID uint, page, pageSize int) ([]model.Commen
|
|||||||
var comments []model.Comment
|
var comments []model.Comment
|
||||||
var total int64
|
var total int64
|
||||||
|
|
||||||
if err := db.Model(&model.Comment{}).Where("resource_id = ?", resourceID).Count(&total).Error; err != nil {
|
if err := db.
|
||||||
|
Model(&model.Comment{}).
|
||||||
|
Where("type = ?", model.CommentTypeResource).
|
||||||
|
Where("ref_id = ?", resourceID).
|
||||||
|
Count(&total).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := db.Where("resource_id = ?", resourceID).Offset((page - 1) * pageSize).Limit(pageSize).Preload("User").Preload("Images").Order("created_at DESC").Find(&comments).Error; err != nil {
|
if err := db.
|
||||||
|
Model(&model.Comment{}).
|
||||||
|
Where("type = ?", model.CommentTypeResource).
|
||||||
|
Where("ref_id = ?", resourceID).
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).
|
||||||
|
Preload("User").
|
||||||
|
Preload("Images").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&comments).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,10 +84,22 @@ func GetCommentsWithUser(username string, page, pageSize int) ([]model.Comment,
|
|||||||
}
|
}
|
||||||
var comments []model.Comment
|
var comments []model.Comment
|
||||||
var total int64
|
var total int64
|
||||||
if err := db.Model(&model.Comment{}).Where("user_id = ?", user.ID).Count(&total).Error; err != nil {
|
if err := db.
|
||||||
|
Model(&model.Comment{}).
|
||||||
|
Where("type = ?", model.CommentTypeResource).
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
Count(&total).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
if err := db.Where("user_id = ?", user.ID).Offset((page - 1) * pageSize).Limit(pageSize).Preload("User").Preload("Resource").Preload("Images").Order("created_at DESC").Find(&comments).Error; err != nil {
|
if err := db.
|
||||||
|
Model(&model.Comment{}).
|
||||||
|
Where("type = ?", model.CommentTypeResource).
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
Offset((page - 1) * pageSize).
|
||||||
|
Limit(pageSize).Preload("User").
|
||||||
|
Preload("Images").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&comments).Error; err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
totalPages := (int(total) + pageSize - 1) / pageSize
|
totalPages := (int(total) + pageSize - 1) / pageSize
|
||||||
@@ -82,7 +108,7 @@ func GetCommentsWithUser(username string, page, pageSize int) ([]model.Comment,
|
|||||||
|
|
||||||
func GetCommentByID(commentID uint) (*model.Comment, error) {
|
func GetCommentByID(commentID uint) (*model.Comment, error) {
|
||||||
var comment model.Comment
|
var comment model.Comment
|
||||||
if err := db.Preload("User").Preload("Resource").Preload("Images").First(&comment, commentID).Error; err != nil {
|
if err := db.Preload("User").Preload("Images").First(&comment, commentID).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &comment, nil
|
return &comment, nil
|
||||||
|
@@ -8,14 +8,21 @@ import (
|
|||||||
|
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
Content string `gorm:"not null"`
|
Content string `gorm:"not null"`
|
||||||
ResourceID uint `gorm:"not null"`
|
RefID uint `gorm:"not null;index:idx_refid_type,priority:1"`
|
||||||
UserID uint `gorm:"not null"`
|
Type CommentType `gorm:"not null;index:idx_refid_type,priority:2"`
|
||||||
User User `gorm:"foreignKey:UserID"`
|
UserID uint `gorm:"not null"`
|
||||||
Resource Resource `gorm:"foreignKey:ResourceID"`
|
User User `gorm:"foreignKey:UserID"`
|
||||||
Images []Image `gorm:"many2many:comment_images;"`
|
Images []Image `gorm:"many2many:comment_images;"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommentType uint
|
||||||
|
|
||||||
|
const (
|
||||||
|
CommentTypeResource CommentType = iota + 1
|
||||||
|
CommentTypeReply
|
||||||
|
)
|
||||||
|
|
||||||
type CommentView struct {
|
type CommentView struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
@@ -48,7 +55,7 @@ type CommentWithResourceView struct {
|
|||||||
Images []ImageView `json:"images"`
|
Images []ImageView `json:"images"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) ToViewWithResource() *CommentWithResourceView {
|
func (c *Comment) ToViewWithResource(r *Resource) *CommentWithResourceView {
|
||||||
imageViews := make([]ImageView, 0, len(c.Images))
|
imageViews := make([]ImageView, 0, len(c.Images))
|
||||||
for _, img := range c.Images {
|
for _, img := range c.Images {
|
||||||
imageViews = append(imageViews, img.ToView())
|
imageViews = append(imageViews, img.ToView())
|
||||||
@@ -58,7 +65,7 @@ func (c *Comment) ToViewWithResource() *CommentWithResourceView {
|
|||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
Content: c.Content,
|
Content: c.Content,
|
||||||
CreatedAt: c.CreatedAt,
|
CreatedAt: c.CreatedAt,
|
||||||
Resource: c.Resource.ToView(),
|
Resource: r.ToView(),
|
||||||
User: c.User.ToView(),
|
User: c.User.ToView(),
|
||||||
Images: imageViews,
|
Images: imageViews,
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,11 @@ func GetActivityList(page int) ([]model.ActivityView, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
comment = c.ToViewWithResource()
|
r, err := dao.GetResourceByID(c.RefID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
comment = c.ToViewWithResource(&r)
|
||||||
} else if activity.Type == model.ActivityTypeNewResource || activity.Type == model.ActivityTypeUpdateResource {
|
} else if activity.Type == model.ActivityTypeNewResource || activity.Type == model.ActivityTypeUpdateResource {
|
||||||
r, err := dao.GetResourceByID(activity.RefID)
|
r, err := dao.GetResourceByID(activity.RefID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -24,7 +24,7 @@ type CommentRequest struct {
|
|||||||
Images []uint `json:"images"`
|
Images []uint `json:"images"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string) (*model.CommentView, error) {
|
func CreateComment(req CommentRequest, userID uint, refID uint, ip string, cType model.CommentType) (*model.CommentView, error) {
|
||||||
if !commentsLimiter.AllowRequest(ip) {
|
if !commentsLimiter.AllowRequest(ip) {
|
||||||
log.Warnf("IP %s has exceeded the comment limit of %d comments per day", ip, maxCommentsPerIP)
|
log.Warnf("IP %s has exceeded the comment limit of %d comments per day", ip, maxCommentsPerIP)
|
||||||
return nil, model.NewRequestError("Too many comments from this IP address, please try again later")
|
return nil, model.NewRequestError("Too many comments from this IP address, please try again later")
|
||||||
@@ -40,7 +40,7 @@ func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string)
|
|||||||
if len(req.Images) > maxImagePerComment {
|
if len(req.Images) > maxImagePerComment {
|
||||||
return nil, model.NewRequestError("Too many images, maximum is 9")
|
return nil, model.NewRequestError("Too many images, maximum is 9")
|
||||||
}
|
}
|
||||||
resourceExists, err := dao.ExistsResource(resourceID)
|
resourceExists, err := dao.ExistsResource(refID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error checking resource existence:", err)
|
log.Error("Error checking resource existence:", err)
|
||||||
return nil, model.NewInternalServerError("Error checking resource existence")
|
return nil, model.NewInternalServerError("Error checking resource existence")
|
||||||
@@ -56,7 +56,7 @@ func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string)
|
|||||||
if !userExists {
|
if !userExists {
|
||||||
return nil, model.NewNotFoundError("User not found")
|
return nil, model.NewNotFoundError("User not found")
|
||||||
}
|
}
|
||||||
c, err := dao.CreateComment(req.Content, userID, resourceID, req.Images)
|
c, err := dao.CreateComment(req.Content, userID, refID, req.Images, cType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error creating comment:", err)
|
log.Error("Error creating comment:", err)
|
||||||
return nil, model.NewInternalServerError("Error creating comment")
|
return nil, model.NewInternalServerError("Error creating comment")
|
||||||
@@ -68,7 +68,7 @@ func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string)
|
|||||||
return c.ToView(), nil
|
return c.ToView(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListComments(resourceID uint, page int) ([]model.CommentView, int, error) {
|
func ListResourceComments(resourceID uint, page int) ([]model.CommentView, int, error) {
|
||||||
resourceExists, err := dao.ExistsResource(resourceID)
|
resourceExists, err := dao.ExistsResource(resourceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Error checking resource existence:", err)
|
log.Error("Error checking resource existence:", err)
|
||||||
@@ -97,7 +97,12 @@ func ListCommentsWithUser(username string, page int) ([]model.CommentWithResourc
|
|||||||
}
|
}
|
||||||
res := make([]model.CommentWithResourceView, 0, len(comments))
|
res := make([]model.CommentWithResourceView, 0, len(comments))
|
||||||
for _, c := range comments {
|
for _, c := range comments {
|
||||||
res = append(res, *c.ToViewWithResource())
|
r, err := dao.GetResourceByID(c.RefID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Error getting resource for comment:", err)
|
||||||
|
return nil, 0, model.NewInternalServerError("Error getting resource for comment")
|
||||||
|
}
|
||||||
|
res = append(res, *c.ToViewWithResource(&r))
|
||||||
}
|
}
|
||||||
return res, totalPages, nil
|
return res, totalPages, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user