fix: 完成 Gitea 目录改组收尾(import、构建与 LICENSE)

同步包路径与路由,去掉 SPA 构建步骤,对齐 Gitea 式 LICENSE,并更新规格/规则与占位 SSR。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-29 04:01:20 +08:00
parent 9fe299a45f
commit 3f50316ad0
93 changed files with 1472 additions and 1522 deletions

View File

@@ -1,9 +1,9 @@
package service
package services
import (
"errors"
"git.iioio.com/freefire/jiang13-forum/model"
"git.iioio.com/freefire/jiang13-forum/models"
"gorm.io/gorm"
)
@@ -19,27 +19,27 @@ const bountyRefundBlockReason = "已有用户回复,无法自行取消悬赏
// CountEligibleBountyReplies 统计他人已发布的有效回复数(不含楼主)
func CountEligibleBountyReplies(db *gorm.DB, postID, authorID uint) (int64, error) {
if db == nil {
db = model.DB
db = models.DB
}
var n int64
err := db.Model(&model.Comment{}).
Where("post_id = ? AND status = ? AND user_id != ?", postID, model.ContentStatusPublished, authorID).
err := db.Model(&models.Comment{}).
Where("post_id = ? AND status = ? AND user_id != ?", postID, models.ContentStatusPublished, authorID).
Count(&n).Error
return n, err
}
// CanRefundBounty 当前查看者是否可取消悬赏(管理员始终可强制取消)
func CanRefundBounty(post *model.Post, viewerIsAdmin bool) (bool, string) {
if post == nil || post.PostType != model.PostTypeBounty {
func CanRefundBounty(post *models.Post, viewerIsAdmin bool) (bool, string) {
if post == nil || post.PostType != models.PostTypeBounty {
return false, ""
}
if post.BountyStatus != model.BountyStatusOpen || post.BountyPoints < 1 {
if post.BountyStatus != models.BountyStatusOpen || post.BountyPoints < 1 {
return false, ""
}
if viewerIsAdmin {
return true, ""
}
n, err := CountEligibleBountyReplies(model.DB, post.ID, post.UserID)
n, err := CountEligibleBountyReplies(models.DB, post.ID, post.UserID)
if err != nil {
return false, ""
}
@@ -54,42 +54,42 @@ func EscrowBounty(tx *gorm.DB, userID, postID uint, points int) error {
if points < 1 {
return ErrBountyInvalidPoint
}
_, err := AdjustPointsTx(tx, userID, -points, model.PointReasonBountyEscrow, "post", postID, "发布悬赏帖")
_, err := AdjustPointsTx(tx, userID, -points, models.PointReasonBountyEscrow, "post", postID, "发布悬赏帖")
return err
}
// AwardBounty 采纳评论并发放悬赏
func AwardBounty(postID, operatorID uint, isAdmin bool, commentID uint) error {
var post model.Post
if err := model.DB.First(&post, postID).Error; err != nil {
var post models.Post
if err := models.DB.First(&post, postID).Error; err != nil {
return ErrPostNotFound
}
if post.PostType != model.PostTypeBounty {
if post.PostType != models.PostTypeBounty {
return errors.New("非悬赏帖")
}
if !isAdmin && post.UserID != operatorID {
return ErrPermissionDenied
}
if post.BountyStatus != model.BountyStatusOpen || post.BountyPoints < 1 {
if post.BountyStatus != models.BountyStatusOpen || post.BountyPoints < 1 {
return ErrBountyNotOpen
}
var comment model.Comment
if err := model.DB.First(&comment, commentID).Error; err != nil {
var comment models.Comment
if err := models.DB.First(&comment, commentID).Error; err != nil {
return errors.New("评论不存在")
}
if comment.PostID != postID || comment.Status != model.ContentStatusPublished {
if comment.PostID != postID || comment.Status != models.ContentStatusPublished {
return errors.New("评论无效")
}
if comment.UserID == post.UserID {
return ErrBountySelfAward
}
points := post.BountyPoints
return model.DB.Transaction(func(tx *gorm.DB) error {
if _, err := AdjustPointsTx(tx, comment.UserID, points, model.PointReasonBountyAward, "post", postID, "悬赏采纳"); err != nil {
return models.DB.Transaction(func(tx *gorm.DB) error {
if _, err := AdjustPointsTx(tx, comment.UserID, points, models.PointReasonBountyAward, "post", postID, "悬赏采纳"); err != nil {
return err
}
return tx.Model(&post).Updates(map[string]interface{}{
"bounty_status": model.BountyStatusAwarded,
"bounty_status": models.BountyStatusAwarded,
"bounty_comment_id": commentID,
}).Error
})
@@ -97,21 +97,21 @@ func AwardBounty(postID, operatorID uint, isAdmin bool, commentID uint) error {
// RefundBounty 取消悬赏并退回积分
func RefundBounty(postID, operatorID uint, isAdmin bool) error {
var post model.Post
if err := model.DB.First(&post, postID).Error; err != nil {
var post models.Post
if err := models.DB.First(&post, postID).Error; err != nil {
return ErrPostNotFound
}
if post.PostType != model.PostTypeBounty {
if post.PostType != models.PostTypeBounty {
return errors.New("非悬赏帖")
}
if !isAdmin && post.UserID != operatorID {
return ErrPermissionDenied
}
if post.BountyStatus != model.BountyStatusOpen || post.BountyPoints < 1 {
if post.BountyStatus != models.BountyStatusOpen || post.BountyPoints < 1 {
return ErrBountyNotOpen
}
if !isAdmin {
n, err := CountEligibleBountyReplies(model.DB, post.ID, post.UserID)
n, err := CountEligibleBountyReplies(models.DB, post.ID, post.UserID)
if err != nil {
return err
}
@@ -120,31 +120,31 @@ func RefundBounty(postID, operatorID uint, isAdmin bool) error {
}
}
points := post.BountyPoints
return model.DB.Transaction(func(tx *gorm.DB) error {
if _, err := AdjustPointsTx(tx, post.UserID, points, model.PointReasonBountyRefund, "post", postID, "悬赏退回"); err != nil {
return models.DB.Transaction(func(tx *gorm.DB) error {
if _, err := AdjustPointsTx(tx, post.UserID, points, models.PointReasonBountyRefund, "post", postID, "悬赏退回"); err != nil {
return err
}
return tx.Model(&post).Updates(map[string]interface{}{
"bounty_status": model.BountyStatusRefunded,
"bounty_status": models.BountyStatusRefunded,
"bounty_points": 0,
}).Error
})
}
// RefundBountyIfOpen 删帖时自动退回未采纳悬赏
func RefundBountyIfOpen(tx *gorm.DB, post *model.Post) error {
if post == nil || post.PostType != model.PostTypeBounty {
func RefundBountyIfOpen(tx *gorm.DB, post *models.Post) error {
if post == nil || post.PostType != models.PostTypeBounty {
return nil
}
if post.BountyStatus != model.BountyStatusOpen || post.BountyPoints < 1 {
if post.BountyStatus != models.BountyStatusOpen || post.BountyPoints < 1 {
return nil
}
points := post.BountyPoints
if _, err := AdjustPointsTx(tx, post.UserID, points, model.PointReasonBountyRefund, "post", post.ID, "删帖退回悬赏"); err != nil {
if _, err := AdjustPointsTx(tx, post.UserID, points, models.PointReasonBountyRefund, "post", post.ID, "删帖退回悬赏"); err != nil {
return err
}
return tx.Model(post).Updates(map[string]interface{}{
"bounty_status": model.BountyStatusRefunded,
"bounty_status": models.BountyStatusRefunded,
"bounty_points": 0,
}).Error
}