Improve Comment model to support multiple comment type.

This commit is contained in:
2025-06-26 20:54:35 +08:00
parent f18465bba3
commit 5d0b201fde
7 changed files with 112 additions and 37 deletions

View File

@@ -571,13 +571,13 @@ class Network {
return `${this.apiBaseUrl}/files/download/${fileId}?cf_token=${cfToken}`;
}
async createComment(
async createResourceComment(
resourceID: number,
content: string,
images: number[],
): Promise<Response<any>> {
return this._callApi(() =>
axios.post(`${this.apiBaseUrl}/comments/${resourceID}`, {
axios.post(`${this.apiBaseUrl}/comments/resource/${resourceID}`, {
content,
images,
}),
@@ -597,12 +597,12 @@ class Network {
);
}
async listComments(
async listResourceComments(
resourceID: number,
page: number = 1,
): Promise<PageResponse<Comment>> {
return this._callApi(() =>
axios.get(`${this.apiBaseUrl}/comments/${resourceID}`, {
axios.get(`${this.apiBaseUrl}/comments/resource/${resourceID}`, {
params: { page },
}),
);

View File

@@ -1221,7 +1221,7 @@ function CommentInput({
return;
}
}
const res = await network.createComment(
const res = await network.createResourceComment(
resourceId,
commentContent,
imageIds,
@@ -1339,7 +1339,7 @@ function CommentsList({
const [comments, setComments] = useState<Comment[] | null>(null);
useEffect(() => {
network.listComments(resourceId, page).then((res) => {
network.listResourceComments(resourceId, page).then((res) => {
if (res.success) {
setComments(res.data!);
maxPageCallback(res.totalPages || 1);

View File

@@ -11,14 +11,16 @@ import (
func AddCommentRoutes(router fiber.Router) {
api := router.Group("/comments")
api.Post("/:resourceID", createComment)
api.Get("/:resourceID", listComments)
api.Get("/user/:username", listCommentsWithUser)
api.Post("/resource/:resourceID", createResourceComment)
api.Post("/reply/:commentID", createReplyComment)
api.Get("/resource/:resourceID", listResourceComments)
api.Get("/reply/:commentID", listResourceComments)
api.Get("/user/:username", listCommentsByUser)
api.Put("/:commentID", updateComment)
api.Delete("/:commentID", deleteComment)
}
func createComment(c fiber.Ctx) error {
func createResourceComment(c fiber.Ctx) error {
userID, ok := c.Locals("uid").(uint)
if !ok {
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")
}
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 {
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")
resourceID, err := strconv.Atoi(resourceIDStr)
if err != nil {
@@ -60,7 +93,7 @@ func listComments(c fiber.Ctx) error {
if err != nil {
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 {
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")
if username == "" {
return model.NewRequestError("Username is required")

View File

@@ -6,13 +6,14 @@ import (
"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
err := db.Transaction(func(tx *gorm.DB) error {
comment = model.Comment{
Content: content,
UserID: userID,
ResourceID: resourceID,
Content: content,
UserID: userID,
RefID: resourceID,
Type: cType,
}
if err := tx.Create(&comment).Error; err != nil {
return err
@@ -49,11 +50,24 @@ func GetCommentByResourceID(resourceID uint, page, pageSize int) ([]model.Commen
var comments []model.Comment
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
}
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
}
@@ -70,10 +84,22 @@ func GetCommentsWithUser(username string, page, pageSize int) ([]model.Comment,
}
var comments []model.Comment
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
}
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
}
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) {
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 &comment, nil

View File

@@ -8,14 +8,21 @@ import (
type Comment struct {
gorm.Model
Content string `gorm:"not null"`
ResourceID uint `gorm:"not null"`
UserID uint `gorm:"not null"`
User User `gorm:"foreignKey:UserID"`
Resource Resource `gorm:"foreignKey:ResourceID"`
Images []Image `gorm:"many2many:comment_images;"`
Content string `gorm:"not null"`
RefID uint `gorm:"not null;index:idx_refid_type,priority:1"`
Type CommentType `gorm:"not null;index:idx_refid_type,priority:2"`
UserID uint `gorm:"not null"`
User User `gorm:"foreignKey:UserID"`
Images []Image `gorm:"many2many:comment_images;"`
}
type CommentType uint
const (
CommentTypeResource CommentType = iota + 1
CommentTypeReply
)
type CommentView struct {
ID uint `json:"id"`
Content string `json:"content"`
@@ -48,7 +55,7 @@ type CommentWithResourceView struct {
Images []ImageView `json:"images"`
}
func (c *Comment) ToViewWithResource() *CommentWithResourceView {
func (c *Comment) ToViewWithResource(r *Resource) *CommentWithResourceView {
imageViews := make([]ImageView, 0, len(c.Images))
for _, img := range c.Images {
imageViews = append(imageViews, img.ToView())
@@ -58,7 +65,7 @@ func (c *Comment) ToViewWithResource() *CommentWithResourceView {
ID: c.ID,
Content: c.Content,
CreatedAt: c.CreatedAt,
Resource: c.Resource.ToView(),
Resource: r.ToView(),
User: c.User.ToView(),
Images: imageViews,
}

View File

@@ -27,7 +27,11 @@ func GetActivityList(page int) ([]model.ActivityView, int, error) {
if err != nil {
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 {
r, err := dao.GetResourceByID(activity.RefID)
if err != nil {

View File

@@ -24,7 +24,7 @@ type CommentRequest struct {
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) {
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")
@@ -40,7 +40,7 @@ func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string)
if len(req.Images) > maxImagePerComment {
return nil, model.NewRequestError("Too many images, maximum is 9")
}
resourceExists, err := dao.ExistsResource(resourceID)
resourceExists, err := dao.ExistsResource(refID)
if err != nil {
log.Error("Error checking resource existence:", err)
return nil, model.NewInternalServerError("Error checking resource existence")
@@ -56,7 +56,7 @@ func CreateComment(req CommentRequest, userID uint, resourceID uint, ip string)
if !userExists {
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 {
log.Error("Error creating comment:", err)
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
}
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)
if err != nil {
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))
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
}