This commit is contained in:
12600k-rog-d4
2025-12-09 20:33:57 +08:00
parent 144ed3cbdf
commit 8f4d399cfd
1498 changed files with 3778 additions and 3654 deletions

View File

@@ -0,0 +1,237 @@
package api
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/bbsurls"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/spam"
"log/slog"
"strconv"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/jsons"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"github.com/panjf2000/ants/v2"
"github.com/sirupsen/logrus"
"bbs-go/internal/controllers/render"
"bbs-go/internal/models"
"bbs-go/internal/services"
)
type ArticleController struct {
Ctx iris.Context
}
func (c *ArticleController) GetClean() *web.JsonResult {
go func() {
p, _ := ants.NewPool(10)
services.ArticleService.ScanDesc(func(articles []models.Article) {
var ids []int64
for _, article := range articles {
if article.ContentType == constants.ContentTypeHtml {
ids = append(ids, article.Id)
}
}
if len(ids) > 0 {
p.Submit(func() {
sqls.DB().Delete(&models.Article{}, "id in ?", ids)
logrus.Info("清理文章:", ids)
})
}
})
}()
return web.JsonSuccess()
}
// 文章详情
func (c *ArticleController) GetBy(articleId int64) *web.JsonResult {
article := services.ArticleService.Get(articleId)
if article == nil || article.Status == constants.StatusDeleted {
return web.JsonErrorCode(404, "文章不存在")
}
user := services.UserTokenService.GetCurrent(c.Ctx)
// 审核中文章控制展示
if article.Status == constants.StatusReview {
if user != nil {
if article.UserId != user.Id && !user.IsOwnerOrAdmin() {
return web.JsonErrorCode(403, "文章审核中")
}
} else {
return web.JsonErrorCode(403, "文章审核中")
}
}
services.ArticleService.IncrViewCount(articleId) // 增加浏览量
return web.JsonData(render.BuildArticle(article, user))
}
// PostCreate 发表文章
func (c *ArticleController) PostCreate() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
form := models.GetCreateArticleForm(c.Ctx)
if err := spam.CheckArticle(user, form); err != nil {
return web.JsonError(err)
}
article, err := services.ArticleService.Publish(user.Id, form)
if err != nil {
return web.JsonError(err)
}
return web.JsonData(render.BuildArticle(article, user))
}
// 编辑时获取详情
func (c *ArticleController) GetEditBy(articleId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
article := services.ArticleService.Get(articleId)
if article == nil || article.Status == constants.StatusDeleted {
return web.JsonErrorMsg("话题不存在或已被删除")
}
// 非作者、且非管理员
if article.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
tags := services.ArticleService.GetArticleTags(articleId)
var tagNames []string
if len(tags) > 0 {
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
}
var cover *models.ImageDTO
if err := jsons.Parse(article.Cover, &cover); err != nil {
slog.Error(err.Error(), slog.Any("err", err))
}
return web.NewEmptyRspBuilder().
Put("id", article.Id).
Put("articleId", article.Id).
Put("title", article.Title).
Put("content", article.Content).
Put("tags", tagNames).
Put("cover", cover).
JsonResult()
}
// 编辑文章
func (c *ArticleController) PostEditBy(articleId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
var (
tags = params.FormValueStringArray(c.Ctx, "tags")
title = c.Ctx.PostValue("title")
content = c.Ctx.PostValue("content")
cover = models.GetImageDTO(c.Ctx, "cover")
)
article := services.ArticleService.Get(articleId)
if article == nil || article.Status == constants.StatusDeleted {
return web.JsonErrorMsg("文章不存在")
}
// 非作者、且非管理员
if article.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
if err := services.ArticleService.Edit(articleId, tags, title, content, cover); err != nil {
return web.JsonError(err)
}
// 操作日志
services.OperateLogService.AddOperateLog(user.Id, constants.OpTypeUpdate, constants.EntityArticle, articleId,
"", c.Ctx.Request())
return web.NewEmptyRspBuilder().Put("articleId", article.Id).JsonResult()
}
// 删除文章
func (c *ArticleController) PostDeleteBy(articleId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
article := services.ArticleService.Get(articleId)
if article == nil || article.Status == constants.StatusDeleted {
return web.JsonErrorMsg("文章不存在")
}
// 非作者、且非管理员
if article.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
if err := services.ArticleService.Delete(articleId); err != nil {
return web.JsonError(err)
}
// 操作日志
services.OperateLogService.AddOperateLog(user.Id, constants.OpTypeDelete, constants.EntityArticle, articleId,
"", c.Ctx.Request())
return web.JsonSuccess()
}
// 收藏文章
func (c *ArticleController) PostFavoriteBy(articleId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
err := services.FavoriteService.AddArticleFavorite(user.Id, articleId)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 文章跳转链接
func (c *ArticleController) GetRedirectBy(articleId int64) *web.JsonResult {
article := services.ArticleService.Get(articleId)
if article == nil || article.Status != constants.StatusOk {
return web.JsonErrorMsg("文章不存在")
}
return web.NewEmptyRspBuilder().Put("url", bbsurls.ArticleUrl(articleId)).JsonResult()
}
// 用户文章列表
func (c *ArticleController) GetUserArticles() *web.JsonResult {
userId, err := params.FormValueInt64(c.Ctx, "userId")
if err != nil {
return web.JsonError(err)
}
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
articles, cursor, hasMore := services.ArticleService.GetUserArticles(userId, cursor)
return web.JsonCursorData(render.BuildSimpleArticles(articles), strconv.FormatInt(cursor, 10), hasMore)
}
// 文章列表
func (c *ArticleController) GetArticles() *web.JsonResult {
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
articles, cursor, hasMore := services.ArticleService.GetArticles(cursor)
return web.JsonCursorData(render.BuildSimpleArticles(articles), strconv.FormatInt(cursor, 10), hasMore)
}
// 标签文章列表
func (c *ArticleController) GetTagArticles() *web.JsonResult {
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
tagId := params.FormValueInt64Default(c.Ctx, "tagId", 0)
articles, cursor, hasMore := services.ArticleService.GetTagArticles(tagId, cursor)
return web.JsonCursorData(render.BuildSimpleArticles(articles), strconv.FormatInt(cursor, 10), hasMore)
}

View File

@@ -0,0 +1,58 @@
package api
import (
"bbs-go/internal/pkg/bbsurls"
"log/slog"
"github.com/dchest/captcha"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/web"
)
type CaptchaController struct {
Ctx iris.Context
}
func (c *CaptchaController) GetRequest() *web.JsonResult {
captchaId := c.Ctx.FormValue("captchaId")
if strs.IsNotBlank(captchaId) { // reload
if !captcha.Reload(captchaId) {
// reload 失败,重新加载验证码
captchaId = captcha.NewLen(4)
}
} else {
captchaId = captcha.NewLen(4)
}
captchaUrl := bbsurls.AbsUrl("/api/captcha/show?captchaId=" + captchaId + "&r=" + strs.UUID())
return web.NewEmptyRspBuilder().
Put("captchaId", captchaId).
Put("captchaUrl", captchaUrl).
JsonResult()
}
func (c *CaptchaController) GetShow() {
captchaId := c.Ctx.URLParam("captchaId")
if captchaId == "" {
c.Ctx.StatusCode(404)
return
}
if !captcha.Reload(captchaId) {
c.Ctx.StatusCode(404)
return
}
c.Ctx.Header("Content-Type", "image/png")
if err := captcha.WriteImage(c.Ctx.ResponseWriter(), captchaId, captcha.StdWidth, captcha.StdHeight); err != nil {
slog.Error(err.Error(), slog.Any("err", err))
}
}
func (c *CaptchaController) GetVerify() *web.JsonResult {
captchaId := c.Ctx.URLParam("captchaId")
captchaCode := c.Ctx.URLParam("captchaCode")
success := captcha.VerifyString(captchaId, captchaCode)
return web.NewEmptyRspBuilder().Put("success", success).JsonResult()
}

View File

@@ -0,0 +1,58 @@
package api
import (
"bbs-go/internal/cache"
"bbs-go/internal/controllers/render"
"bbs-go/internal/services"
"time"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/web"
)
type CheckinController struct {
Ctx iris.Context
}
// PostCheckin 签到
func (c *CheckinController) PostCheckin() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
err := services.CheckInService.CheckIn(user.Id)
if err == nil {
return web.JsonSuccess()
} else {
return web.JsonError(err)
}
}
// GetCheckin 获取签到信息
func (c *CheckinController) GetCheckin() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonSuccess()
}
checkIn := services.CheckInService.GetByUserId(user.Id)
if checkIn != nil {
today := dates.GetDay(time.Now())
return web.NewRspBuilder(checkIn).
Put("checkIn", checkIn.LatestDayName == today). // 今日是否已签到
JsonResult()
}
return web.JsonSuccess()
}
// GetRank 获取当天签到排行榜(最早签到的排在最前面)
func (c *CheckinController) GetRank() *web.JsonResult {
list := cache.UserCache.GetCheckInRank()
var itemList []map[string]interface{}
for _, checkIn := range list {
itemList = append(itemList, web.NewRspBuilder(checkIn).
Put("user", render.BuildUserInfoDefaultIfNull(checkIn.UserId)).
Build())
}
return web.JsonData(itemList)
}

View File

@@ -0,0 +1,91 @@
package api
import (
"bbs-go/internal/models"
"bbs-go/internal/models/constants"
"bbs-go/internal/spam"
"strconv"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"github.com/panjf2000/ants/v2"
"github.com/sirupsen/logrus"
"bbs-go/internal/controllers/render"
"bbs-go/internal/services"
)
type CommentController struct {
Ctx iris.Context
}
func (c *CommentController) GetClean() *web.JsonResult {
go func() {
p, _ := ants.NewPool(10)
services.CommentService.Scan(func(comments []models.Comment) {
var ids []int64
for _, comment := range comments {
if comment.ContentType == constants.ContentTypeHtml {
ids = append(ids, comment.Id)
}
}
if len(ids) > 0 {
p.Submit(func() {
sqls.DB().Delete(&models.Comment{}, "id in ?", ids)
logrus.Info("清理评论:", ids)
})
}
})
}()
return web.JsonSuccess()
}
func (c *CommentController) GetComments() *web.JsonResult {
var (
err error
cursor int64
entityType string
entityId int64
)
cursor = params.FormValueInt64Default(c.Ctx, "cursor", 0)
if entityType, err = params.FormValueRequired(c.Ctx, "entityType"); err != nil {
return web.JsonError(err)
}
if entityId, err = params.FormValueInt64(c.Ctx, "entityId"); err != nil {
return web.JsonError(err)
}
currentUser := services.UserTokenService.GetCurrent(c.Ctx)
comments, cursor, hasMore := services.CommentService.GetComments(entityType, entityId, cursor)
return web.JsonCursorData(render.BuildComments(comments, currentUser, true, false), strconv.FormatInt(cursor, 10), hasMore)
}
func (c *CommentController) GetReplies() *web.JsonResult {
var (
cursor = params.FormValueInt64Default(c.Ctx, "cursor", 0)
commentId = params.FormValueInt64Default(c.Ctx, "commentId", 0)
)
currentUser := services.UserTokenService.GetCurrent(c.Ctx)
comments, cursor, hasMore := services.CommentService.GetReplies(commentId, cursor, 10)
return web.JsonCursorData(render.BuildComments(comments, currentUser, false, true), strconv.FormatInt(cursor, 10), hasMore)
}
func (c *CommentController) PostCreate() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
form := models.GetCreateCommentForm(c.Ctx)
if err := spam.CheckComment(user, form); err != nil {
return web.JsonError(err)
}
comment, err := services.CommentService.Publish(user.Id, form)
if err != nil {
return web.JsonError(err)
}
return web.JsonData(render.BuildComment(comment))
}

View File

@@ -0,0 +1,17 @@
package api
import (
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"bbs-go/internal/services"
)
type ConfigController struct {
Ctx iris.Context
}
func (c *ConfigController) GetConfigs() *web.JsonResult {
config := services.SysConfigService.GetConfig()
return web.JsonData(config)
}

View File

@@ -0,0 +1,156 @@
package api
import (
"bbs-go/internal/controllers/render"
"bbs-go/internal/models"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/services"
"strconv"
"github.com/emirpasic/gods/sets/hashset"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
)
type FansController struct {
Ctx iris.Context
}
func (c *FansController) PostFollow() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
otherId := params.FormValueInt64Default(c.Ctx, "userId", 0)
if otherId <= 0 {
return web.JsonErrorMsg("param: userId required")
}
err := services.UserFollowService.Follow(user.Id, otherId)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *FansController) PostUnfollow() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
otherId := params.FormValueInt64Default(c.Ctx, "userId", 0)
if otherId <= 0 {
return web.JsonErrorMsg("param: userId required")
}
err := services.UserFollowService.UnFollow(user.Id, otherId)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *FansController) GetIsfollowed() *web.JsonResult {
userId := params.FormValueInt64Default(c.Ctx, "userId", 0)
current := services.UserTokenService.GetCurrent(c.Ctx)
var followed = false
if current != nil && current.Id != userId {
followed = services.UserFollowService.IsFollowed(current.Id, userId)
}
return web.JsonData(followed)
}
func (c *FansController) GetFans() *web.JsonResult {
userId := params.FormValueInt64Default(c.Ctx, "userId", 0)
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
userIds, cursor, hasMore := services.UserFollowService.GetFans(userId, cursor, 10)
current := services.UserTokenService.GetCurrent(c.Ctx)
var followedSet hashset.Set
if current != nil {
followedSet = services.UserFollowService.IsFollowedUsers(current.Id, userIds...)
}
var itemList []*models.UserInfo
for _, id := range userIds {
item := render.BuildUserInfoDefaultIfNull(id)
item.Followed = followedSet.Contains(id)
itemList = append(itemList, item)
}
return web.JsonCursorData(itemList, strconv.FormatInt(cursor, 10), hasMore)
}
func (c *FansController) GetFollowed() *web.JsonResult {
userId := params.FormValueInt64Default(c.Ctx, "userId", 0)
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
userIds, cursor, hasMore := services.UserFollowService.GetFollows(userId, cursor, 10)
current := services.UserTokenService.GetCurrent(c.Ctx)
var followedSet hashset.Set
if current != nil {
if current.Id == userId {
followedSet = *hashset.New()
for _, id := range userIds {
followedSet.Add(id)
}
} else {
followedSet = services.UserFollowService.IsFollowedUsers(current.Id, userIds...)
}
}
var itemList []*models.UserInfo
for _, id := range userIds {
item := render.BuildUserInfoDefaultIfNull(id)
item.Followed = followedSet.Contains(id)
itemList = append(itemList, item)
}
return web.JsonCursorData(itemList, strconv.FormatInt(cursor, 10), hasMore)
}
func (c *FansController) GetRecentFans() *web.JsonResult {
userId := params.FormValueInt64Default(c.Ctx, "userId", 0)
userIds, cursor, hasMore := services.UserFollowService.GetFans(userId, 0, 10)
current := services.UserTokenService.GetCurrent(c.Ctx)
var followedSet hashset.Set
if current != nil {
followedSet = services.UserFollowService.IsFollowedUsers(current.Id, userIds...)
}
var itemList []*models.UserInfo
for _, id := range userIds {
item := render.BuildUserInfoDefaultIfNull(id)
item.Followed = followedSet.Contains(id)
itemList = append(itemList, item)
}
return web.JsonCursorData(itemList, strconv.FormatInt(cursor, 10), hasMore)
}
func (c *FansController) GetRecentFollow() *web.JsonResult {
userId := params.FormValueInt64Default(c.Ctx, "userId", 0)
userIds, cursor, hasMore := services.UserFollowService.GetFollows(userId, 0, 10)
current := services.UserTokenService.GetCurrent(c.Ctx)
var followedSet hashset.Set
if current != nil {
if current.Id == userId {
followedSet = *hashset.New()
for _, id := range userIds {
followedSet.Add(id)
}
} else {
followedSet = services.UserFollowService.IsFollowedUsers(current.Id, userIds...)
}
}
var itemList []*models.UserInfo
for _, id := range userIds {
item := render.BuildUserInfoDefaultIfNull(id)
item.Followed = followedSet.Contains(id)
itemList = append(itemList, item)
}
return web.JsonCursorData(itemList, strconv.FormatInt(cursor, 10), hasMore)
}

View File

@@ -0,0 +1,58 @@
package api
import (
"errors"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/services"
)
type FavoriteController struct {
Ctx iris.Context
}
func (c *FavoriteController) PostAdd() *web.JsonResult {
var (
user = services.UserTokenService.GetCurrent(c.Ctx)
entityType = params.FormValue(c.Ctx, "entityType")
entityId = params.FormValueInt64Default(c.Ctx, "entityId", 0)
)
if user == nil {
return web.JsonError(errs.NotLogin)
}
var err error
if entityType == constants.EntityTopic {
err = services.FavoriteService.AddTopicFavorite(user.Id, entityId)
} else if entityType == constants.EntityArticle {
err = services.FavoriteService.AddArticleFavorite(user.Id, entityId)
} else {
err = errors.New("unsupproted")
}
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 取消收藏
func (c *FavoriteController) PostDelete() *web.JsonResult {
var (
user = services.UserTokenService.GetCurrent(c.Ctx)
entityType = params.FormValue(c.Ctx, "entityType")
entityId = params.FormValueInt64Default(c.Ctx, "entityId", 0)
)
if user == nil {
return web.JsonError(errs.NotLogin)
}
tmp := services.FavoriteService.GetBy(user.Id, entityType, entityId)
if tmp != nil {
services.FavoriteService.Delete(tmp.Id)
}
return web.JsonSuccess()
}

View File

@@ -0,0 +1,89 @@
package api
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/services"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
)
type LikeController struct {
Ctx iris.Context
}
func (c *LikeController) PostLike() *web.JsonResult {
var (
entityType = params.FormValue(c.Ctx, "entityType")
entityId = params.FormValueInt64Default(c.Ctx, "entityId", 0)
user = services.UserTokenService.GetCurrent(c.Ctx)
err error
)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if entityType == constants.EntityTopic {
err = services.UserLikeService.TopicLike(user.Id, entityId)
} else if entityType == constants.EntityArticle {
err = services.UserLikeService.ArticleLike(user.Id, entityId)
} else if entityType == constants.EntityComment {
err = services.UserLikeService.CommentLike(user.Id, entityId)
}
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *LikeController) PostUnlike() *web.JsonResult {
var (
entityType = params.FormValue(c.Ctx, "entityType")
entityId = params.FormValueInt64Default(c.Ctx, "entityId", 0)
user = services.UserTokenService.GetCurrent(c.Ctx)
err error
)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if entityType == constants.EntityTopic {
err = services.UserLikeService.TopicUnLike(user.Id, entityId)
} else if entityType == constants.EntityArticle {
err = services.UserLikeService.ArticleUnLike(user.Id, entityId)
} else if entityType == constants.EntityComment {
err = services.UserLikeService.CommentUnLike(user.Id, entityId)
}
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *LikeController) GetLiked_ids() *web.JsonResult {
var (
user = services.UserTokenService.GetCurrent(c.Ctx)
entityType = params.FormValue(c.Ctx, "entityType")
entityIds = params.FormValueInt64Array(c.Ctx, "entityIds")
likedEntityIds []int64
)
if user != nil {
likedEntityIds = services.UserLikeService.IsLiked(user.Id, entityType, entityIds)
}
return web.JsonData(likedEntityIds)
}
func (c *LikeController) GetLiked() *web.JsonResult {
var (
user = services.UserTokenService.GetCurrent(c.Ctx)
entityType = params.FormValue(c.Ctx, "entityType")
entityId = params.FormValueInt64Default(c.Ctx, "entityId", 0)
)
if user == nil || strs.IsBlank(entityType) || entityId <= 0 {
return web.JsonData(false)
} else {
liked := services.UserLikeService.Exists(user.Id, entityType, entityId)
return web.JsonData(liked)
}
}

View File

@@ -0,0 +1,75 @@
package api
import (
"bbs-go/internal/models/constants"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"bbs-go/internal/models"
"bbs-go/internal/services"
)
type LinkController struct {
Ctx iris.Context
}
func (c *LinkController) GetBy(id int64) *web.JsonResult {
link := services.LinkService.Get(id)
if link == nil || link.Status == constants.StatusDeleted {
return web.JsonErrorMsg("数据不存在")
}
return web.JsonData(c.buildLink(*link))
}
// 列表
func (c *LinkController) GetList() *web.JsonResult {
links := services.LinkService.Find(sqls.NewCnd().
Eq("status", constants.StatusOk).Asc("id"))
var itemList []map[string]interface{}
for _, v := range links {
itemList = append(itemList, c.buildLink(v))
}
return web.JsonData(itemList)
}
// 列表
func (c *LinkController) GetLinks() *web.JsonResult {
page := params.FormValueIntDefault(c.Ctx, "page", 1)
links, paging := services.LinkService.FindPageByCnd(sqls.NewCnd().
Eq("status", constants.StatusOk).Page(page, 20).Asc("id"))
var itemList []map[string]interface{}
for _, v := range links {
itemList = append(itemList, c.buildLink(v))
}
return web.JsonPageData(itemList, paging)
}
// 前10个链接
func (c *LinkController) GetToplinks() *web.JsonResult {
links := services.LinkService.Find(sqls.NewCnd().
Eq("status", constants.StatusOk).Limit(10).Asc("id"))
var itemList []map[string]interface{}
for _, v := range links {
itemList = append(itemList, c.buildLink(v))
}
return web.JsonData(itemList)
}
func (c *LinkController) buildLink(link models.Link) map[string]interface{} {
return map[string]interface{}{
"id": link.Id,
"linkId": link.Id,
"url": link.Url,
"title": link.Title,
"summary": link.Summary,
"logo": link.Logo,
"createTime": link.CreateTime,
}
}

View File

@@ -0,0 +1,75 @@
package api
import (
"bbs-go/internal/controllers/render"
"github.com/dchest/captcha"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/services"
)
type LoginController struct {
Ctx iris.Context
}
// 注册
func (c *LoginController) PostSignup() *web.JsonResult {
var (
captchaId = c.Ctx.PostValueTrim("captchaId")
captchaCode = c.Ctx.PostValueTrim("captchaCode")
email = c.Ctx.PostValueTrim("email")
username = c.Ctx.PostValueTrim("username")
password = c.Ctx.PostValueTrim("password")
rePassword = c.Ctx.PostValueTrim("rePassword")
nickname = c.Ctx.PostValueTrim("nickname")
redirect = c.Ctx.FormValue("redirect")
)
loginMethod := services.SysConfigService.GetLoginMethod()
if !loginMethod.Password {
return web.JsonErrorMsg("账号密码登录/注册已禁用")
}
if !captcha.VerifyString(captchaId, captchaCode) {
return web.JsonError(errs.CaptchaError)
}
user, err := services.UserService.SignUp(username, email, nickname, password, rePassword)
if err != nil {
return web.JsonError(err)
}
return render.BuildLoginSuccess(c.Ctx, user, redirect)
}
// 用户名密码登录
func (c *LoginController) PostSignin() *web.JsonResult {
var (
captchaId = c.Ctx.PostValueTrim("captchaId")
captchaCode = c.Ctx.PostValueTrim("captchaCode")
username = c.Ctx.PostValueTrim("username")
password = c.Ctx.PostValueTrim("password")
redirect = c.Ctx.FormValue("redirect")
)
loginMethod := services.SysConfigService.GetLoginMethod()
if !loginMethod.Password {
return web.JsonErrorMsg("账号密码登录/注册已禁用")
}
if !captcha.VerifyString(captchaId, captchaCode) {
return web.JsonError(errs.CaptchaError)
}
user, err := services.UserService.SignIn(username, password)
if err != nil {
return web.JsonError(err)
}
return render.BuildLoginSuccess(c.Ctx, user, redirect)
}
// 退出登录
func (c *LoginController) GetSignout() *web.JsonResult {
err := services.UserTokenService.Signout(c.Ctx)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}

View File

@@ -0,0 +1,44 @@
package api
import (
"bbs-go/internal/controllers/render"
"bbs-go/internal/models"
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/search"
"bbs-go/internal/services"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"github.com/spf13/cast"
)
type SearchController struct {
Ctx iris.Context
}
func (c *SearchController) AnyReindex() *web.JsonResult {
go services.TopicService.ScanDesc(func(topics []models.Topic) {
for _, topic := range topics {
if topic.Status != constants.StatusDeleted {
search.UpdateTopicIndex(&topic)
}
}
})
return web.JsonSuccess()
}
func (c *SearchController) GetTopic() *web.JsonResult {
var (
cursor = params.FormValueIntDefault(c.Ctx, "cursor", 1)
keyword = params.FormValue(c.Ctx, "keyword")
nodeId = params.FormValueInt64Default(c.Ctx, "nodeId", 0)
timeRange = params.FormValueIntDefault(c.Ctx, "timeRange", 0)
limit = 20
)
list, _, err := search.SearchTopic(keyword, nodeId, timeRange, cursor, limit)
if err != nil {
return web.JsonError(err)
}
return web.JsonCursorData(render.BuildSearchTopics(list), cast.ToString(cursor+1), len(list) >= limit)
}

View File

@@ -0,0 +1,44 @@
package api
import (
"bbs-go/internal/models/constants"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"bbs-go/internal/cache"
"bbs-go/internal/controllers/render"
"bbs-go/internal/services"
)
type TagController struct {
Ctx iris.Context
}
// 标签详情
func (c *TagController) GetBy(tagId int64) *web.JsonResult {
tag := cache.TagCache.Get(tagId)
if tag == nil {
return web.JsonErrorMsg("标签不存在")
}
return web.JsonData(render.BuildTag(tag))
}
// 标签列表
func (c *TagController) GetTags() *web.JsonResult {
page := params.FormValueIntDefault(c.Ctx, "page", 1)
tags, paging := services.TagService.FindPageByCnd(sqls.NewCnd().
Eq("status", constants.StatusOk).
Page(page, 200).Desc("id"))
return web.JsonPageData(render.BuildTags(tags), paging)
}
// 标签自动完成
func (c *TagController) PostAutocomplete() *web.JsonResult {
input := c.Ctx.FormValue("input")
tags := services.TagService.Autocomplete(input)
return web.JsonData(tags)
}

View File

@@ -0,0 +1,374 @@
package api
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/common"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/pkg/markdown"
"bbs-go/internal/spam"
"math/rand"
"strconv"
"strings"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"bbs-go/internal/cache"
"bbs-go/internal/controllers/render"
"bbs-go/internal/models"
"bbs-go/internal/services"
)
type TopicController struct {
Ctx iris.Context
}
func (c *TopicController) GetNode_navs() *web.JsonResult {
nodes := []models.NodeResponse{
{
Id: 0,
Name: "最新",
},
{
Id: -1,
Name: "推荐",
},
{
Id: -2,
Name: "关注",
},
}
realNodes := render.BuildNodes(services.TopicNodeService.GetNodes())
nodes = append(nodes, realNodes...)
return web.JsonData(nodes)
}
// 节点
func (c *TopicController) GetNodes() *web.JsonResult {
nodes := render.BuildNodes(services.TopicNodeService.GetNodes())
return web.JsonData(nodes)
}
// 节点信息
func (c *TopicController) GetNode() *web.JsonResult {
nodeId := params.FormValueInt64Default(c.Ctx, "nodeId", 0)
node := services.TopicNodeService.Get(nodeId)
return web.JsonData(render.BuildNode(node))
}
// 发表帖子
func (c *TopicController) PostCreate() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
form := models.GetCreateTopicForm(c.Ctx)
if err := spam.CheckTopic(user, form); err != nil {
return web.JsonError(err)
}
topic, err := services.TopicPublishService.Publish(user.Id, form)
if err != nil {
return web.JsonError(err)
}
return web.JsonData(render.BuildSimpleTopic(topic))
}
// 编辑时获取详情
func (c *TopicController) GetEditBy(topicId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
topic := services.TopicService.Get(topicId)
if topic == nil || topic.Status != constants.StatusOk {
return web.JsonErrorMsg("话题不存在或已被删除")
}
if topic.Type != constants.TopicTypeTopic {
return web.JsonErrorMsg("当前类型帖子不支持修改")
}
// 非作者、且非管理员
if topic.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
tags := services.TopicService.GetTopicTags(topicId)
var tagNames []string
if len(tags) > 0 {
for _, tag := range tags {
tagNames = append(tagNames, tag.Name)
}
}
return web.NewEmptyRspBuilder().
Put("id", topic.Id).
Put("nodeId", topic.NodeId).
Put("title", topic.Title).
Put("content", topic.Content).
Put("hideContent", topic.HideContent).
Put("tags", tagNames).
JsonResult()
}
// 编辑帖子
func (c *TopicController) PostEditBy(topicId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
topic := services.TopicService.Get(topicId)
if topic == nil || topic.Status != constants.StatusOk {
return web.JsonErrorMsg("话题不存在或已被删除")
}
// 非作者、且非管理员
if topic.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
var (
nodeId = params.FormValueInt64Default(c.Ctx, "nodeId", 0)
title = strings.TrimSpace(params.FormValue(c.Ctx, "title"))
content = strings.TrimSpace(params.FormValue(c.Ctx, "content"))
hideContent = strings.TrimSpace(params.FormValue(c.Ctx, "hideContent"))
tags = params.FormValueStringArray(c.Ctx, "tags")
)
err := services.TopicService.Edit(topicId, nodeId, tags, title, content, hideContent)
if err != nil {
return web.JsonError(err)
}
// 操作日志
services.OperateLogService.AddOperateLog(user.Id, constants.OpTypeUpdate, constants.EntityTopic, topicId,
"", c.Ctx.Request())
return web.JsonData(render.BuildSimpleTopic(topic))
}
// 删除帖子
func (c *TopicController) PostDeleteBy(topicId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
topic := services.TopicService.Get(topicId)
if topic == nil || topic.Status != constants.StatusOk {
return web.JsonSuccess()
}
// 非作者、且非管理员
if topic.UserId != user.Id && !user.HasAnyRole(constants.RoleAdmin, constants.RoleOwner) {
return web.JsonErrorMsg("无权限")
}
if err := services.TopicService.Delete(topicId, user.Id, c.Ctx.Request()); err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// PostRecommendBy 设为推荐
func (c *TopicController) PostRecommendBy(topicId int64) *web.JsonResult {
recommend, err := params.FormValueBool(c.Ctx, "recommend")
if err != nil {
return web.JsonError(err)
}
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if !user.HasAnyRole(constants.RoleOwner, constants.RoleAdmin) {
return web.JsonErrorMsg("无权限")
}
err = services.TopicService.SetRecommend(topicId, recommend)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 帖子详情
func (c *TopicController) GetBy(topicId int64) *web.JsonResult {
topic := services.TopicService.Get(topicId)
if topic == nil || topic.Status == constants.StatusDeleted {
return web.JsonErrorMsg("帖子不存在")
}
// 审核中文章控制展示
user := services.UserTokenService.GetCurrent(c.Ctx)
if topic.Status == constants.StatusReview {
if user != nil {
if topic.UserId != user.Id && !user.IsOwnerOrAdmin() {
return web.JsonErrorCode(403, "文章审核中")
}
} else {
return web.JsonErrorCode(403, "文章审核中")
}
}
services.TopicService.IncrViewCount(topicId) // 增加浏览量
return web.JsonData(render.BuildTopic(topic, user))
}
// 点赞用户
func (c *TopicController) GetRecentlikesBy(topicId int64) *web.JsonResult {
likes := services.UserLikeService.Recent(constants.EntityTopic, topicId, 5)
var users []models.UserInfo
for _, like := range likes {
userInfo := render.BuildUserInfoDefaultIfNull(like.UserId)
if userInfo != nil {
users = append(users, *userInfo)
}
}
return web.JsonData(users)
}
// 最新帖子
func (c *TopicController) GetRecent() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
topics := services.TopicService.Find(sqls.NewCnd().Where("status = ?", constants.StatusOk).Desc("id").Limit(10))
return web.JsonData(render.BuildSimpleTopics(topics, user))
}
// 用户帖子列表
func (c *TopicController) GetUserTopics() *web.JsonResult {
userId, err := params.FormValueInt64(c.Ctx, "userId")
if err != nil {
return web.JsonError(err)
}
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
user := services.UserTokenService.GetCurrent(c.Ctx)
topics, cursor, hasMore := services.TopicService.GetUserTopics(userId, cursor)
return web.JsonCursorData(render.BuildSimpleTopics(topics, user), strconv.FormatInt(cursor, 10), hasMore)
}
// 帖子列表
func (c *TopicController) GetTopics() *web.JsonResult {
var (
cursor = params.FormValueInt64Default(c.Ctx, "cursor", 0)
nodeId = params.FormValueInt64Default(c.Ctx, "nodeId", 0)
user = services.UserTokenService.GetCurrent(c.Ctx)
)
if nodeId == constants.NodeIdFollow && user == nil {
return web.JsonError(errs.NotLogin)
}
var temp []models.Topic
if cursor <= 0 {
stickyTopics := services.TopicService.GetStickyTopics(nodeId, 3)
temp = append(temp, stickyTopics...)
}
topics, cursor, hasMore := services.TopicService.GetTopics(user, nodeId, cursor)
for _, topic := range topics {
topic.Sticky = false // 正常列表不要渲染置顶
temp = append(temp, topic)
}
list := common.Distinct(temp, func(t models.Topic) any {
return t.Id
})
return web.JsonCursorData(render.BuildSimpleTopics(list, user), strconv.FormatInt(cursor, 10), hasMore)
}
// 标签帖子列表
func (c *TopicController) GetTagTopics() *web.JsonResult {
var (
cursor = params.FormValueInt64Default(c.Ctx, "cursor", 0)
tagId, err = params.FormValueInt64(c.Ctx, "tagId")
user = services.UserTokenService.GetCurrent(c.Ctx)
)
if err != nil {
return web.JsonError(err)
}
topics, cursor, hasMore := services.TopicService.GetTagTopics(tagId, cursor)
return web.JsonCursorData(render.BuildSimpleTopics(topics, user), strconv.FormatInt(cursor, 10), hasMore)
}
// 收藏
func (c *TopicController) GetFavoriteBy(topicId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
err := services.FavoriteService.AddTopicFavorite(user.Id, topicId)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 推荐话题列表目前逻辑为取最近50条数据随机展示
func (c *TopicController) GetRecommend() *web.JsonResult {
topics := cache.TopicCache.GetRecommendTopics()
if len(topics) == 0 {
return web.JsonSuccess()
} else {
dest := make([]models.Topic, len(topics))
perm := rand.Perm(len(topics))
for i, v := range perm {
dest[v] = topics[i]
}
end := 10
if end > len(topics) {
end = len(topics)
}
ret := dest[0:end]
return web.JsonData(render.BuildSimpleTopics(ret, nil))
}
}
// 最新话题
func (c *TopicController) GetNewest() *web.JsonResult {
topics := services.TopicService.Find(sqls.NewCnd().Eq("status", constants.StatusOk).Desc("id").Limit(6))
return web.JsonData(render.BuildSimpleTopics(topics, nil))
}
// 设置置顶
func (c *TopicController) PostStickyBy(topicId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if !user.HasAnyRole(constants.RoleOwner, constants.RoleAdmin) {
return web.JsonErrorMsg("无权限")
}
var (
sticky = params.FormValueBoolDefault(c.Ctx, "sticky", false) // 是否指定
)
if err := services.TopicService.SetSticky(topicId, sticky); err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *TopicController) GetHide_content() *web.JsonResult {
topicId := params.FormValueInt64Default(c.Ctx, "topicId", 0)
var (
exists = false // 是否有隐藏内容
show = false // 是否显示隐藏内容
hideContent = "" // 隐藏内容
)
topic := services.TopicService.Get(topicId)
if topic != nil && topic.Status == constants.StatusOk && strs.IsNotBlank(topic.HideContent) {
exists = true
if user := services.UserTokenService.GetCurrent(c.Ctx); user != nil {
if user.Id == topic.UserId || services.CommentService.IsCommented(user.Id, constants.EntityTopic, topic.Id) {
show = true
hideContent = markdown.ToHTML(topic.HideContent)
}
}
}
return web.JsonData(map[string]interface{}{
"exists": exists,
"show": show,
"content": hideContent,
})
}

View File

@@ -0,0 +1,49 @@
package api
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/uploader"
"io"
"log/slog"
"strconv"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/web"
"bbs-go/internal/services"
)
type UploadController struct {
Ctx iris.Context
}
func (c *UploadController) Post() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if err := services.UserService.CheckPostStatus(user); err != nil {
return web.JsonError(err)
}
file, header, err := c.Ctx.FormFile("image")
if err != nil {
return web.JsonError(err)
}
defer file.Close()
if header.Size > constants.UploadMaxBytes {
return web.JsonErrorMsg("图片不能超过" + strconv.Itoa(constants.UploadMaxM) + "M")
}
contentType := header.Header.Get("Content-Type")
fileBytes, err := io.ReadAll(file)
if err != nil {
return web.JsonError(err)
}
slog.Info("上传文件:", slog.Any("filename", header.Filename), slog.Any("size", header.Size))
url, err := uploader.PutImage(fileBytes, contentType)
if err != nil {
return web.JsonError(err)
}
return web.NewEmptyRspBuilder().Put("url", url).JsonResult()
}

View File

@@ -0,0 +1,419 @@
package api
import (
"bbs-go/internal/models/constants"
"bbs-go/internal/pkg/errs"
"bbs-go/internal/pkg/msg"
"bbs-go/internal/pkg/validate"
"strconv"
"strings"
"time"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/common/strs"
"github.com/mlogclub/simple/sqls"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
"github.com/spf13/cast"
"bbs-go/internal/cache"
"bbs-go/internal/controllers/render"
"bbs-go/internal/models"
"bbs-go/internal/services"
)
type UserController struct {
Ctx iris.Context
}
// 获取当前登录用户
func (c *UserController) GetCurrent() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user != nil {
return web.JsonData(render.BuildUserProfile(user))
}
return web.JsonSuccess()
}
// 用户详情
func (c *UserController) GetBy(userId int64) *web.JsonResult {
user := cache.UserCache.Get(userId)
if user != nil && user.Status != constants.StatusDeleted {
return web.JsonData(render.BuildUserDetail(user))
}
return web.JsonErrorMsg("用户不存在")
}
// 修改用户资料
func (c *UserController) PostEditBy(userId int64) *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if user.Id != userId {
return web.JsonErrorMsg("无权限")
}
var (
nickname = strings.TrimSpace(params.FormValue(c.Ctx, "nickname"))
homePage = params.FormValue(c.Ctx, "homePage")
description = params.FormValue(c.Ctx, "description")
gender = strings.TrimSpace(params.FormValue(c.Ctx, "gender"))
birthdayStr = strings.TrimSpace(params.FormValue(c.Ctx, "birthday"))
birthday *time.Time
err error
)
if len(nickname) == 0 {
return web.JsonErrorMsg("昵称不能为空")
}
if strs.IsNotBlank(gender) {
if gender != string(constants.GenderMale) && gender != string(constants.GenderFemale) {
return web.JsonErrorMsg("性别数据错误")
}
}
if strs.IsNotBlank(birthdayStr) {
*birthday, err = dates.Parse(birthdayStr, dates.FmtDate)
if err != nil {
return web.JsonError(err)
}
}
if len(homePage) > 0 && validate.IsURL(homePage) != nil {
return web.JsonErrorMsg("个人主页地址错误")
}
columns := map[string]interface{}{
"nickname": nickname,
"home_page": homePage,
"description": description,
"gender": gender,
}
if birthday != nil {
columns["birthday"] = birthday
}
err = services.UserService.Updates(user.Id, columns)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 修改头像
func (c *UserController) PostUpdateAvatar() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
avatar := strings.TrimSpace(params.FormValue(c.Ctx, "avatar"))
if len(avatar) == 0 {
return web.JsonErrorMsg("头像不能为空")
}
err := services.UserService.UpdateAvatar(user.Id, avatar)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
func (c *UserController) PostUpdateNickname() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
nickname := strings.TrimSpace(params.FormValue(c.Ctx, "nickname"))
if len(nickname) == 0 {
return web.JsonErrorMsg("Nickname cannot be empty")
}
err := services.UserService.UpdateNickname(user.Id, nickname)
if err != nil {
return web.JsonErrorMsg(err.Error())
}
return web.JsonSuccess()
}
func (c *UserController) PostUpdateDescription() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
description := strings.TrimSpace(params.FormValue(c.Ctx, "description"))
err := services.UserService.UpdateDescription(user.Id, description)
if err != nil {
return web.JsonErrorMsg(err.Error())
}
return web.JsonSuccess()
}
func (c *UserController) PostUpdateGender() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
gender := strings.TrimSpace(params.FormValue(c.Ctx, "gender"))
err := services.UserService.UpdateGender(user.Id, gender)
if err != nil {
return web.JsonErrorMsg(err.Error())
}
return web.JsonSuccess()
}
func (c *UserController) PostUpdateBirthday() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
birthday := strings.TrimSpace(params.FormValue(c.Ctx, "birthday"))
err := services.UserService.UpdateBirthday(user.Id, birthday)
if err != nil {
return web.JsonErrorMsg(err.Error())
}
return web.JsonSuccess()
}
// 设置用户名
func (c *UserController) PostSetUsername() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
username := strings.TrimSpace(params.FormValue(c.Ctx, "username"))
err := services.UserService.SetUsername(user.Id, username)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 设置邮箱
func (c *UserController) PostSetEmail() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
email := strings.TrimSpace(params.FormValue(c.Ctx, "email"))
err := services.UserService.SetEmail(user.Id, email)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 设置密码
func (c *UserController) PostSetPassword() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
password := params.FormValue(c.Ctx, "password")
rePassword := params.FormValue(c.Ctx, "rePassword")
err := services.UserService.SetPassword(user.Id, password, rePassword)
if err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 修改密码
func (c *UserController) PostUpdatePassword() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
var (
oldPassword = params.FormValue(c.Ctx, "oldPassword")
password = params.FormValue(c.Ctx, "password")
rePassword = params.FormValue(c.Ctx, "rePassword")
)
if err := services.UserService.UpdatePassword(user.Id, oldPassword, password, rePassword); err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 设置背景图
func (c *UserController) PostSet_background_image() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
backgroundImage := params.FormValue(c.Ctx, "backgroundImage")
if strs.IsBlank(backgroundImage) {
return web.JsonErrorMsg("请上传图片")
}
if err := services.UserService.UpdateBackgroundImage(user.Id, backgroundImage); err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// 用户收藏
func (c *UserController) GetFavorites() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
cursor := params.FormValueInt64Default(c.Ctx, "cursor", 0)
// 用户必须登录
if user == nil {
return web.JsonError(errs.NotLogin)
}
// 查询列表
limit := 20
var favorites []models.Favorite
if cursor > 0 {
favorites = services.FavoriteService.Find(sqls.NewCnd().Where("user_id = ? and id < ?",
user.Id, cursor).Desc("id").Limit(20))
} else {
favorites = services.FavoriteService.Find(sqls.NewCnd().Where("user_id = ?", user.Id).Desc("id").Limit(limit))
}
hasMore := false
if len(favorites) > 0 {
cursor = favorites[len(favorites)-1].Id
hasMore = len(favorites) >= limit
}
return web.JsonCursorData(render.BuildFavorites(favorites), strconv.FormatInt(cursor, 10), hasMore)
}
// 获取最近3条未读消息
func (c *UserController) GetMsgrecent() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
var count int64 = 0
var messages []models.Message
if user != nil {
count = services.MessageService.GetUnReadCount(user.Id)
messages = services.MessageService.Find(sqls.NewCnd().Eq("user_id", user.Id).
Eq("status", msg.StatusUnread).Limit(3).Desc("id"))
}
return web.NewEmptyRspBuilder().Put("count", count).Put("messages", render.BuildMessages(messages)).JsonResult()
}
// 用户消息
func (c *UserController) GetMessages() *web.JsonResult {
user, err := services.UserTokenService.CheckLogin(c.Ctx)
if err != nil {
return web.JsonError(errs.NotLogin)
}
var (
limit = 20
cursor, _ = params.GetInt64(c.Ctx, "cursor")
)
cnd := sqls.NewCnd().Eq("user_id", user.Id).Limit(limit).Desc("id")
if cursor > 0 {
cnd.Lt("id", cursor)
}
list := services.MessageService.Find(cnd)
var (
nextCursor = cursor
hasMore = false
)
if len(list) > 0 {
nextCursor = list[len(list)-1].Id
hasMore = len(list) == limit
}
// 全部标记为已读
services.MessageService.MarkRead(user.Id)
return web.JsonCursorData(render.BuildMessages(list), cast.ToString(nextCursor), hasMore)
}
// 用户积分记录
func (c *UserController) GetScore_logs() *web.JsonResult {
user, err := services.UserTokenService.CheckLogin(c.Ctx)
if err != nil {
return web.JsonError(err)
}
var (
limit = 20
cursor, _ = params.GetInt64(c.Ctx, "cursor")
)
cnd := sqls.NewCnd().Eq("user_id", user.Id).Limit(limit).Desc("id")
if cursor > 0 {
cnd.Lt("id", cursor)
}
list := services.UserScoreLogService.Find(cnd)
var (
nextCursor = cursor
hasMore = false
)
if len(list) > 0 {
nextCursor = list[len(list)-1].Id
hasMore = len(list) == limit
}
return web.JsonCursorData(list, cast.ToString(nextCursor), hasMore)
}
// 积分排行
func (c *UserController) GetScoreRank() *web.JsonResult {
users := cache.UserCache.GetScoreRank()
var results []*models.UserInfo
for _, user := range users {
results = append(results, render.BuildUserInfo(&user))
}
return web.JsonData(results)
}
// 禁言
func (c *UserController) PostForbidden() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if !user.HasAnyRole(constants.RoleOwner, constants.RoleAdmin) {
return web.JsonErrorMsg("无权限")
}
var (
userId = params.FormValueInt64Default(c.Ctx, "userId", 0)
days = params.FormValueIntDefault(c.Ctx, "days", 0)
reason = params.FormValue(c.Ctx, "reason")
)
if userId < 0 {
return web.JsonErrorMsg("请传入userId")
}
if days == -1 && !user.HasRole(constants.RoleOwner) {
return web.JsonErrorMsg("无永久禁言权限")
}
if days == 0 {
services.UserService.RemoveForbidden(user.Id, userId, c.Ctx.Request())
} else {
if err := services.UserService.Forbidden(user.Id, userId, days, reason, c.Ctx.Request()); err != nil {
return web.JsonError(err)
}
}
return web.JsonSuccess()
}
// PostEmailVerify 请求邮箱验证邮件
func (c *UserController) PostSend_verify_email() *web.JsonResult {
user := services.UserTokenService.GetCurrent(c.Ctx)
if user == nil {
return web.JsonError(errs.NotLogin)
}
if err := services.UserService.SendEmailVerifyEmail(user.Id); err != nil {
return web.JsonError(err)
}
return web.JsonSuccess()
}
// PostVerify_email 获取邮箱验证码
func (c *UserController) PostVerify_email() *web.JsonResult {
token := params.FormValue(c.Ctx, "token")
if strs.IsBlank(token) {
return web.JsonErrorMsg("Illegal request")
}
var (
email string
err error
)
if email, err = services.UserService.VerifyEmail(token); err != nil {
return web.JsonError(err)
}
return web.NewEmptyRspBuilder().Put("email", email).JsonResult()
}

View File

@@ -0,0 +1,35 @@
package api
import (
"bbs-go/internal/models"
"bbs-go/internal/services"
"github.com/kataras/iris/v12"
"github.com/mlogclub/simple/common/dates"
"github.com/mlogclub/simple/web"
"github.com/mlogclub/simple/web/params"
)
type UserReportController struct {
Ctx iris.Context
}
func (c *UserReportController) PostSubmit() *web.JsonResult {
var (
dataId, _ = params.FormValueInt64(c.Ctx, "dataId")
dataType = params.FormValue(c.Ctx, "dataId")
reason = params.FormValue(c.Ctx, "reason")
)
report := &models.UserReport{
DataId: dataId,
DataType: dataType,
Reason: reason,
CreateTime: dates.NowTimestamp(),
}
if user := services.UserTokenService.GetCurrent(c.Ctx); user != nil {
report.UserId = user.Id
}
services.UserReportService.Create(report)
return web.JsonSuccess()
}