签到,黑名单,屏蔽,sse,经验

pull/701/head
miaowuawa 10 months ago
parent f4dcc0e0df
commit 40765e837d

@ -0,0 +1,87 @@
// Code generated by go-mir. DO NOT EDIT.
// versions:
// - mir 5.3
package v1
import (
"net/http"
"github.com/alimy/mir/v5"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
)
type Blockship interface {
_default_
ListBlocks(*web.ListBlocksReq) (*web.ListBlocksResp, error)
UnblockUser(*web.UnblockUserReq) error
BlockUser(*web.BlockUserReq) error
mustEmbedUnimplementedBlockshipServant()
}
// RegisterBlockshipServant register Blockship servant to gin
func RegisterBlockshipServant(e *gin.Engine, s Blockship) {
router := e.Group("v1")
// register routes info to router
router.Handle("GET", "user/blocks", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListBlocksReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListBlocks(req)
s.Render(c, resp, err)
})
router.Handle("POST", "user/unblock", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.UnblockUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.UnblockUser(req))
})
router.Handle("POST", "user/block", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.BlockUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.BlockUser(req))
})
}
// UnimplementedBlockshipServant can be embedded to have forward compatible implementations.
type UnimplementedBlockshipServant struct{}
func (UnimplementedBlockshipServant) ListBlocks(req *web.ListBlocksReq) (*web.ListBlocksResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedBlockshipServant) UnblockUser(req *web.UnblockUserReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedBlockshipServant) BlockUser(req *web.BlockUserReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedBlockshipServant) mustEmbedUnimplementedBlockshipServant() {}

@ -0,0 +1,82 @@
// Code generated by go-mir. DO NOT EDIT.
// versions:
// - mir 5.3
package v1
import (
"net/http"
"github.com/alimy/mir/v5"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
)
type CheckInActivitity interface {
_default_
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
GetCheckInRank(*web.GetCheckInRankReq) (*web.GetCheckInRankResp, error)
UserCheckIn(*web.UserCheckInReq) (*web.UserCheckInResp, error)
mustEmbedUnimplementedCheckInActivitityServant()
}
// RegisterCheckInActivitityServant register CheckInActivitity servant to gin
func RegisterCheckInActivitityServant(e *gin.Engine, s CheckInActivitity) {
router := e.Group("v1")
// use chain for router
middlewares := s.Chain()
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "checkin/rank", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetCheckInRankReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetCheckInRank(req)
s.Render(c, resp, err)
})
router.Handle("POST", "checkin", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.UserCheckInReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.UserCheckIn(req)
s.Render(c, resp, err)
})
}
// UnimplementedCheckInActivitityServant can be embedded to have forward compatible implementations.
type UnimplementedCheckInActivitityServant struct{}
func (UnimplementedCheckInActivitityServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedCheckInActivitityServant) GetCheckInRank(req *web.GetCheckInRankReq) (*web.GetCheckInRankResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCheckInActivitityServant) UserCheckIn(req *web.UserCheckInReq) (*web.UserCheckInResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCheckInActivitityServant) mustEmbedUnimplementedCheckInActivitityServant() {}

@ -18,6 +18,9 @@ type Core interface {
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
StreamMessages(*web.StreamMessagesReq, *gin.Context) error
GetUserArticleStars(*web.GetUserArticleStarsReq) (*web.GetUserArticleStarsResp, error)
GetUserArticleCollections(*web.GetUserArticleCollectionsReq) (*web.GetUserArticleCollectionsResp, error)
TweetCollectionStatus(*web.TweetCollectionStatusReq) (*web.TweetCollectionStatusResp, error)
TweetStarStatus(*web.TweetStarStatusReq) (*web.TweetStarStatusResp, error)
SuggestTags(*web.SuggestTagsReq) (*web.SuggestTagsResp, error)
@ -46,6 +49,52 @@ func RegisterCoreServant(e *gin.Engine, s Core) {
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "user/messages/stream", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.StreamMessagesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
if err := s.StreamMessages(req, c); err != nil {
s.Render(c, nil, err)
return
}
})
router.Handle("GET", "user/articles/stars", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetUserArticleStarsReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetUserArticleStars(req)
s.Render(c, resp, err)
})
router.Handle("GET", "user/articles/collections", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetUserArticleCollectionsReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetUserArticleCollections(req)
s.Render(c, resp, err)
})
router.Handle("GET", "post/collection", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -283,6 +332,18 @@ func (UnimplementedCoreServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedCoreServant) StreamMessages(req *web.StreamMessagesReq, c *gin.Context) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) GetUserArticleStars(req *web.GetUserArticleStarsReq) (*web.GetUserArticleStarsResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) GetUserArticleCollections(req *web.GetUserArticleCollectionsReq) (*web.GetUserArticleCollectionsResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) TweetCollectionStatus(req *web.TweetCollectionStatusReq) (*web.TweetCollectionStatusResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -18,6 +18,14 @@ type Loose interface {
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
ArticleCollectionStatus(*web.ArticleCollectionStatusReq) (*web.ArticleCollectionStatusResp, error)
ArticleStarStatus(*web.ArticleStarStatusReq) (*web.ArticleStarStatusResp, error)
GetUserArticles(*web.GetUserArticlesReq) (*web.GetUserArticlesResp, error)
ListArticleSeries(*web.ListArticleSeriesReq) (*web.ListArticleSeriesResp, error)
GetArticleSeries(*web.GetArticleSeriesReq) (*web.GetArticleSeriesResp, error)
ArticleComments(*web.ArticleCommentsReq) (*web.ArticleCommentsResp, error)
ListArticles(*web.ListArticlesReq) (*web.ListArticlesResp, error)
GetArticle(*web.GetArticleReq) (*web.GetArticleResp, error)
TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, error)
TweetComments(*web.TweetCommentsReq) (*web.TweetCommentsResp, error)
TopicList(*web.TopicListReq) (*web.TopicListResp, error)
@ -36,6 +44,154 @@ func RegisterLooseServant(e *gin.Engine, s Loose) {
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "articles/collection", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ArticleCollectionStatusReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ArticleCollectionStatus(req)
s.Render(c, resp, err)
})
router.Handle("GET", "articles/star", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ArticleStarStatusReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ArticleStarStatus(req)
s.Render(c, resp, err)
})
router.Handle("GET", "user/articles", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetUserArticlesReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetUserArticles(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "articles/series", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListArticleSeriesReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListArticleSeries(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "articles/series/detail", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetArticleSeriesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetArticleSeries(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "articles/comments", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ArticleCommentsReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ArticleComments(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "articles", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListArticlesReq)
var bv _binding_ = req
if err := bv.Bind(c); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListArticles(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "articles/detail", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.GetArticleReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.GetArticle(req)
if err != nil {
s.Render(c, nil, err)
return
}
var rv _render_ = resp
rv.Render(c)
})
router.Handle("GET", "post", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -145,6 +301,38 @@ func (UnimplementedLooseServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedLooseServant) ArticleCollectionStatus(req *web.ArticleCollectionStatusReq) (*web.ArticleCollectionStatusResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) ArticleStarStatus(req *web.ArticleStarStatusReq) (*web.ArticleStarStatusResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) GetUserArticles(req *web.GetUserArticlesReq) (*web.GetUserArticlesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) ListArticleSeries(req *web.ListArticleSeriesReq) (*web.ListArticleSeriesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) GetArticleSeries(req *web.GetArticleSeriesReq) (*web.GetArticleSeriesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) ArticleComments(req *web.ArticleCommentsReq) (*web.ArticleCommentsResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) ListArticles(req *web.ListArticlesReq) (*web.ListArticlesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) GetArticle(req *web.GetArticleReq) (*web.GetArticleResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -0,0 +1,87 @@
// Code generated by go-mir. DO NOT EDIT.
// versions:
// - mir 5.3
package v1
import (
"net/http"
"github.com/alimy/mir/v5"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
)
type Muteship interface {
_default_
ListMutes(*web.ListMutesReq) (*web.ListMutesResp, error)
UnmuteUser(*web.UnmuteUserReq) error
MuteUser(*web.MuteUserReq) error
mustEmbedUnimplementedMuteshipServant()
}
// RegisterMuteshipServant register Muteship servant to gin
func RegisterMuteshipServant(e *gin.Engine, s Muteship) {
router := e.Group("v1")
// register routes info to router
router.Handle("GET", "user/mutes", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListMutesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListMutes(req)
s.Render(c, resp, err)
})
router.Handle("POST", "user/unmute", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.UnmuteUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.UnmuteUser(req))
})
router.Handle("POST", "user/mute", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.MuteUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.MuteUser(req))
})
}
// UnimplementedMuteshipServant can be embedded to have forward compatible implementations.
type UnimplementedMuteshipServant struct{}
func (UnimplementedMuteshipServant) ListMutes(req *web.ListMutesReq) (*web.ListMutesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedMuteshipServant) UnmuteUser(req *web.UnmuteUserReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedMuteshipServant) MuteUser(req *web.MuteUserReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedMuteshipServant) mustEmbedUnimplementedMuteshipServant() {}

@ -18,6 +18,19 @@ type Priv interface {
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
AdminDeleteArticle(*web.AdminDeleteArticleReq) error
DeleteArticleSeries(*web.DeleteArticleSeriesReq) error
UpdateArticleSeries(*web.UpdateArticleSeriesReq) error
CreateArticleSeries(*web.CreateArticleSeriesReq) (*web.CreateArticleSeriesResp, error)
DeleteArticleCommentReply(*web.DeleteArticleCommentReplyReq) error
CreateArticleCommentReply(*web.CreateArticleCommentReplyReq) (*web.CreateArticleCommentReplyResp, error)
DeleteArticleComment(*web.DeleteArticleCommentReq) error
CreateArticleComment(*web.CreateArticleCommentReq) (*web.CreateArticleCommentResp, error)
CollectionArticle(*web.ArticleCollectionReq) (*web.ArticleCollectionResp, error)
StarArticle(*web.ArticleStarReq) (*web.ArticleStarResp, error)
DeleteArticle(*web.DeleteArticleReq) error
UpdateArticle(*web.UpdateArticleReq) error
CreateArticle(*web.CreateArticleReq) (*web.CreateArticleResp, error)
UnfollowTopic(*web.UnfollowTopicReq) error
FollowTopic(*web.FollowTopicReq) error
PinTopic(*web.PinTopicReq) (*web.PinTopicResp, error)
@ -47,6 +60,8 @@ type Priv interface {
}
type PrivChain interface {
ChainUpdateArticle() gin.HandlersChain
ChainCreateArticle() gin.HandlersChain
ChainCreateTweet() gin.HandlersChain
mustEmbedUnimplementedPrivChain()
@ -66,6 +81,181 @@ func RegisterPrivServant(e *gin.Engine, s Priv, m ...PrivChain) {
router.Use(middlewares...)
// register routes info to router
router.Handle("DELETE", "admin/articles", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.AdminDeleteArticleReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.AdminDeleteArticle(req))
})
router.Handle("DELETE", "articles/series", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteArticleSeriesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.DeleteArticleSeries(req))
})
router.Handle("PUT", "articles/series", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.UpdateArticleSeriesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.UpdateArticleSeries(req))
})
router.Handle("POST", "articles/series", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.CreateArticleSeriesReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.CreateArticleSeries(req)
s.Render(c, resp, err)
})
router.Handle("DELETE", "articles/comment/reply", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteArticleCommentReplyReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.DeleteArticleCommentReply(req))
})
router.Handle("POST", "articles/comment/reply", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.CreateArticleCommentReplyReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.CreateArticleCommentReply(req)
s.Render(c, resp, err)
})
router.Handle("DELETE", "articles/comment", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteArticleCommentReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.DeleteArticleComment(req))
})
router.Handle("POST", "articles/comment", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.CreateArticleCommentReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.CreateArticleComment(req)
s.Render(c, resp, err)
})
router.Handle("POST", "articles/collection", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ArticleCollectionReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.CollectionArticle(req)
s.Render(c, resp, err)
})
router.Handle("POST", "articles/star", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ArticleStarReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.StarArticle(req)
s.Render(c, resp, err)
})
router.Handle("DELETE", "articles", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteArticleReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.DeleteArticle(req))
})
router.Handle("PUT", "articles", append(cc.ChainUpdateArticle(), func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.UpdateArticleReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.UpdateArticle(req))
})...)
router.Handle("POST", "articles", append(cc.ChainCreateArticle(), func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.CreateArticleReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.CreateArticle(req)
s.Render(c, resp, err)
})...)
router.Handle("POST", "topic/unfollow", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -413,6 +603,58 @@ func (UnimplementedPrivServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedPrivServant) AdminDeleteArticle(req *web.AdminDeleteArticleReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteArticleSeries(req *web.DeleteArticleSeriesReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) UpdateArticleSeries(req *web.UpdateArticleSeriesReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) CreateArticleSeries(req *web.CreateArticleSeriesReq) (*web.CreateArticleSeriesResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteArticleCommentReply(req *web.DeleteArticleCommentReplyReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) CreateArticleCommentReply(req *web.CreateArticleCommentReplyReq) (*web.CreateArticleCommentReplyResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteArticleComment(req *web.DeleteArticleCommentReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) CreateArticleComment(req *web.CreateArticleCommentReq) (*web.CreateArticleCommentResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) CollectionArticle(req *web.ArticleCollectionReq) (*web.ArticleCollectionResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) StarArticle(req *web.ArticleStarReq) (*web.ArticleStarResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteArticle(req *web.DeleteArticleReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) UpdateArticle(req *web.UpdateArticleReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) CreateArticle(req *web.CreateArticleReq) (*web.CreateArticleResp, error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) UnfollowTopic(req *web.UnfollowTopicReq) error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
@ -514,6 +756,14 @@ func (UnimplementedPrivServant) mustEmbedUnimplementedPrivServant() {}
// UnimplementedPrivChain can be embedded to have forward compatible implementations.
type UnimplementedPrivChain struct{}
func (b *UnimplementedPrivChain) ChainUpdateArticle() gin.HandlersChain {
return nil
}
func (b *UnimplementedPrivChain) ChainCreateArticle() gin.HandlersChain {
return nil
}
func (b *UnimplementedPrivChain) ChainCreateTweet() gin.HandlersChain {
return nil
}

@ -19,7 +19,6 @@ type Relax interface {
Chain() gin.HandlersChain
GetUnreadMsgCount(*web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, error)
StreamUnreadMsgCount(*gin.Context)
mustEmbedUnimplementedRelaxServant()
}
@ -63,16 +62,6 @@ func RegisterRelaxServant(e *gin.Engine, s Relax, m ...RelaxChain) {
var rv _render_ = resp
rv.Render(c)
})...)
// Register SSE route for streaming unread message count
router.Handle("GET", "user/msgcount/stream", append(middlewares, func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
s.StreamUnreadMsgCount(c)
})...)
}
// UnimplementedRelaxServant can be embedded to have forward compatible implementations.

File diff suppressed because it is too large Load Diff

@ -42,6 +42,8 @@ const (
TableUser = "user"
TableUserRelation = "user_relation"
TableUserMetric = "user_metric"
TableOperationSettings = "operation_settings"
TableUserCheckin = "user_checkin"
TableWalletRecharge = "wallet_recharge"
TableWalletStatement = "wallet_statement"
)

@ -410,6 +410,8 @@ func (s *databaseConf) TableNames() (res TableNameMap) {
TableUser,
TableUserRelation,
TableUserMetric,
TableOperationSettings,
TableUserCheckin,
TableWalletRecharge,
TableWalletStatement,
}

@ -6,3 +6,36 @@
// model define
package cs
// RelationTyp 用户关系类型
type RelationTyp uint8
const (
RelationUnknown RelationTyp = iota
RelationSelf
RelationFriend
RelationFollower
RelationFollowing
RelationAdmin
RelationGuest
)
// String 返回关系类型的字符串表示
func (t RelationTyp) String() string {
switch t {
case RelationSelf:
return "self"
case RelationFriend:
return "friend"
case RelationFollower:
return "follower"
case RelationFollowing:
return "following"
case RelationAdmin:
return "admin"
case RelationUnknown:
fallthrough
default:
return "unknown"
}
}

@ -4,8 +4,29 @@
package cs
import (
"github.com/rocboss/paopao-ce/internal/core/ms"
)
// TweetBox 推文列表盒子,包含其他一些关于推文列表的信息
type TweetBox struct {
Tweets TweetList
Total int64
}
// ContentBox 综合内容列表盒子,包含动态和长文章
type ContentBox struct {
Contents ContentList `json:"contents"`
Total int64 `json:"total"`
}
// ContentItem 内容项
type ContentItem struct {
Type ms.ContentType `json:"type"`
Tweet *TweetItem `json:"tweet,omitempty"`
Article interface{} `json:"article,omitempty"`
CreatedOn int64 `json:"created_on"`
}
// ContentList 内容列表
type ContentList []*ContentItem

@ -4,30 +4,6 @@
package cs
const (
RelationUnknown RelationTyp = iota
RelationSelf
RelationFriend
RelationFollower
RelationFollowing
RelationAdmin
RelationGuest
)
type (
// UserInfoList 用户信息列表
UserInfoList []*UserInfo
//
RelationTyp uint8
VistUser struct {
Username string
UserId int64
RelTyp RelationTyp
}
)
// UserInfo 用户基本信息
type UserInfo struct {
ID int64 `json:"id"`
@ -54,21 +30,3 @@ type UserProfile struct {
Level int `json:"level"`
}
func (t RelationTyp) String() string {
switch t {
case RelationSelf:
return "self"
case RelationFriend:
return "friend"
case RelationFollower:
return "follower"
case RelationFollowing:
return "following"
case RelationAdmin:
return "admin"
case RelationUnknown:
fallthrough
default:
return "unknown"
}
}

@ -1,27 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package core
import (
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
)
// UserLevelService 用户等级服务接口
type UserLevelService interface {
// GetLevelByExperience 根据经验值获取用户等级
GetLevelByExperience(experience int) (*dbr.UserLevel, error)
// GetAllLevels 获取所有等级配置
GetAllLevels() ([]*dbr.UserLevel, error)
// CreateLevel 创建等级配置
CreateLevel(level *dbr.UserLevel) (*dbr.UserLevel, error)
// UpdateLevel 更新等级配置
UpdateLevel(level *dbr.UserLevel) error
// DeleteLevel 删除等级配置
DeleteLevel(level int) error
}

@ -0,0 +1,58 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package ms
// ArticleContentT 长文章内容类型
type ArticleContentT int
const (
ArticleContentTypeTitle ArticleContentT = 1 + iota
ArticleContentTypeText
ArticleContentTypeImage
ArticleContentTypeVideo
ArticleContentTypeAudio
ArticleContentTypeLink
ArticleContentTypeAttachment
ArticleContentTypePaidAttachment
)
// ArticleVisibleT 长文章可见性类型
type ArticleVisibleT int
const (
ArticleVisiblePrivate ArticleVisibleT = 0 + iota // 私密
ArticleVisiblePaid // 付费可见
ArticleVisibleReserved1 // 保留
ArticleVisibleReserved2 // 保留
ArticleVisibleFriend // 好友可见
ArticleVisibleFollowing // 关注可见
ArticleVisibleReserved3 // 保留
ArticleVisibleReserved4 // 保留
ArticleVisiblePublic // 公开
)
// Article 文章别名
type Article = interface{}
// ArticleContent 文章内容别名
type ArticleContent = interface{}
// ArticleSeries 文章系列别名
type ArticleSeries = interface{}
// ArticleSeriesItem 系列文章关联别名
type ArticleSeriesItem = interface{}
// ArticleCollection 文章收藏别名
type ArticleCollection = interface{}
// ArticleStar 文章点赞别名
type ArticleStar = interface{}
// ArticleComment 文章评论别名
type ArticleComment = interface{}
// ArticleCommentReply 文章评论回复别名
type ArticleCommentReply = interface{}

@ -8,3 +8,25 @@ type IndexTweetList struct {
Tweets []*PostFormated
Total int64
}
// ContentType 内容类型
type ContentType int
const (
ContentTypeTweet ContentType = iota + 1
ContentTypeArticle
)
// IndexContentItem 首页内容项
type IndexContentItem struct {
Type ContentType `json:"type"`
Tweet *PostFormated `json:"tweet,omitempty"`
Article interface{} `json:"article,omitempty"`
CreatedOn int64 `json:"created_on"`
}
// IndexContentList 首页综合内容列表
type IndexContentList struct {
Contents []*IndexContentItem `json:"contents"`
Total int64 `json:"total"`
}

@ -18,3 +18,13 @@ type IndexPostsService interface {
type IndexPostsServantA interface {
IndexPosts(user *ms.User, limit int, offset int) (*cs.TweetBox, error)
}
// IndexContentService 广场首页综合内容服务(包含动态和长文章)
type IndexContentService interface {
IndexContent(user *ms.User, offset int, limit int) (*ms.IndexContentList, error)
}
// IndexContentServantA 广场首页综合内容服务(版本A)
type IndexContentServantA interface {
IndexContent(user *ms.User, limit int, offset int) (*cs.ContentBox, error)
}

@ -7,6 +7,7 @@ package core
import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
)
// TweetService 推文检索服务
@ -23,9 +24,9 @@ type TweetService interface {
GetPostAttatchmentBill(postID, userID int64) (*ms.PostAttachmentBill, error)
GetPostContentsByIDs(ids []int64) ([]*ms.PostContent, error)
GetPostContentByID(id int64) (*ms.PostContent, error)
ListUserStarTweets(user *cs.VistUser, limit int, offset int) ([]*ms.PostStar, int64, error)
ListUserMediaTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
ListUserCommentTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
ListUserStarTweets(user *dbr.VistUser, limit int, offset int) ([]*ms.PostStar, int64, error)
ListUserMediaTweets(user *dbr.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
ListUserCommentTweets(user *dbr.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
ListUserTweets(userId int64, style uint8, justEssence bool, limit, offset int) ([]*ms.Post, int64, error)
ListFollowingTweets(userId int64, limit, offset int) ([]*ms.Post, int64, error)
ListIndexNewestTweets(limit, offset int) ([]*ms.Post, int64, error)

@ -5,8 +5,8 @@
package core
import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
)
// UserManageService 用户管理服务
@ -16,7 +16,7 @@ type UserManageService interface {
GetUserByPhone(phone string) ([]*ms.User, error)
GetUsersByIDs(ids []int64) ([]*ms.User, error)
GetUsersByKeyword(keyword string) ([]*ms.User, error)
UserProfileByName(username string) (*cs.UserProfile, error)
UserProfileByName(username string) (*dbr.UserProfile, error)
CreateUser(user *ms.User) (*ms.User, error)
UpdateUser(user *ms.User) error
GetRegisterUserCount() (int64, error)

@ -11,8 +11,8 @@ import (
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
)
type cacheDataService struct {
@ -62,12 +62,12 @@ func (s *cacheDataService) GetUserByUsername(username string) (res *ms.User, err
return
}
func (s *cacheDataService) UserProfileByName(username string) (res *cs.UserProfile, err error) {
func (s *cacheDataService) UserProfileByName(username string) (res *dbr.UserProfile, err error) {
// 先从缓存获取, 不处理错误
key := conf.KeyUserProfileByName.Get(username)
if data, xerr := s.ac.Get(key); xerr == nil {
buf := bytes.NewBuffer(data)
res = &cs.UserProfile{}
res = &dbr.UserProfile{}
err = gob.NewDecoder(buf).Decode(res)
return
}

@ -0,0 +1,77 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var (
_ core.UserBlockManageService = (*userBlockManageSrv)(nil)
)
type userBlockManageSrv struct {
db *gorm.DB
b *dbr.UserBlock
u *dbr.User
}
func newUserBlockManageService(db *gorm.DB) core.UserBlockManageService {
return &userBlockManageSrv{
db: db,
b: &dbr.UserBlock{},
u: &dbr.User{},
}
}
func (s *userBlockManageSrv) BlockUser(userId int64, targetUserId int64, reason string) error {
if _, err := s.b.GetUserBlock(s.db, userId, targetUserId); err != nil {
block := &dbr.UserBlock{
UserId: userId,
TargetUserId: targetUserId,
Reason: reason,
}
if _, err = block.Create(s.db); err != nil {
logrus.Errorf("userBlockManageSrv.blockUser create new block err:%s", err)
return err
}
}
return nil
}
func (s *userBlockManageSrv) UnblockUser(userId int64, targetUserId int64) error {
return s.b.DelUserBlock(s.db, userId, targetUserId)
}
func (s *userBlockManageSrv) ListBlocks(userId int64, limit, offset int) (*ms.UserBlockList, error) {
blocks, total, err := s.b.ListUserBlocks(s.db, userId, limit, offset)
if err != nil {
return nil, err
}
res := &ms.UserBlockList{
Total: total,
}
for _, block := range blocks {
res.Blocks = append(res.Blocks, ms.UserBlockItem{
UserId: block.UserId,
TargetUserId: block.TargetUserId,
Reason: block.Reason,
CreatedOn: block.CreatedOn,
})
}
return res, nil
}
func (s *userBlockManageSrv) IsBlocked(userId int64, targetUserId int64) bool {
return s.b.IsBlocked(s.db, userId, targetUserId)
}
func (s *userBlockManageSrv) IsBlocking(userId int64, targetUserId int64) bool {
return s.b.IsBlocked(s.db, targetUserId, userId)
}

@ -0,0 +1,65 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package dbr
import (
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
type UserBlock struct {
*Model
UserId int64 `json:"user_id"`
TargetUserId int64 `json:"target_user_id"`
Reason string `json:"reason"`
}
func (UserBlock) TableName() string {
return "p_user_block"
}
func (b *UserBlock) GetUserBlock(db *gorm.DB, userId, targetUserId int64) (*UserBlock, error) {
var block UserBlock
err := db.Unscoped().Where("user_id = ? AND target_user_id = ?", userId, targetUserId).First(&block).Error
if err != nil {
logrus.Debugf("UserBlock.GetUserBlock get block error:%s", err)
return nil, err
}
return &block, nil
}
func (b *UserBlock) DelUserBlock(db *gorm.DB, userId, targetUserId int64) error {
return db.Unscoped().Where("user_id = ? AND target_user_id = ?", userId, targetUserId).Delete(b).Error
}
func (b *UserBlock) ListUserBlocks(db *gorm.DB, userId int64, limit int, offset int) (res []*UserBlock, total int64, err error) {
db = db.Model(b).Where("user_id=?", userId)
if err = db.Count(&total).Error; err != nil {
return
}
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
if err = db.Find(&res).Error; err != nil {
return
}
return
}
func (b *UserBlock) IsBlocked(db *gorm.DB, userId, targetUserId int64) bool {
if _, err := b.GetUserBlock(db, userId, targetUserId); err == nil {
return true
}
return false
}
func (b *UserBlock) Create(db *gorm.DB) (*UserBlock, error) {
err := db.Create(b).Error
return b, err
}
func (b *UserBlock) UpdateInUnscoped(db *gorm.DB) error {
return db.Unscoped().Save(b).Error
}

@ -0,0 +1,75 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package dbr
// RelationTyp 用户关系类型
type RelationTyp uint8
const (
RelationUnknown RelationTyp = iota
RelationSelf
RelationFriend
RelationFollower
RelationFollowing
RelationAdmin
RelationGuest
)
// String 返回关系类型的字符串表示
func (t RelationTyp) String() string {
switch t {
case RelationSelf:
return "self"
case RelationFriend:
return "friend"
case RelationFollower:
return "follower"
case RelationFollowing:
return "following"
case RelationAdmin:
return "admin"
case RelationUnknown:
fallthrough
default:
return "unknown"
}
}
// UserInfo 用户基本信息
type UserInfo struct {
ID int64 `json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
IsAdmin bool `json:"is_admin"`
CreatedOn int64 `json:"created_on"`
}
// UserInfoList 用户信息列表
type UserInfoList []*UserInfo
// VistUser 访问用户信息
type VistUser struct {
Username string
UserId int64
RelTyp RelationTyp
}
// UserProfile 用户资料
type UserProfile struct {
ID int64 `json:"id" db:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Phone string `json:"phone"`
Status int `json:"status"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
IsAdmin bool `json:"is_admin"`
CreatedOn int64 `json:"created_on"`
TweetsCount int `json:"tweets_count"`
Experience int `json:"experience"`
Level int `json:"level"`
}

@ -1,53 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package dbr
import (
"gorm.io/gorm"
)
// UserLevel 用户等级配置模型
type UserLevel struct {
Level int `gorm:"column:level;primaryKey" json:"level"` // 等级
MinExperience int `gorm:"column:min_experience" json:"min_experience"` // 最小经验值
MaxExperience int `gorm:"column:max_experience" json:"max_experience"` // 最大经验值
Name string `gorm:"column:name" json:"name"` // 等级名称
Description string `gorm:"column:description" json:"description"` // 等级描述
}
// TableName 指定表名映射
func (u *UserLevel) TableName() string {
return "p_user_level"
}
// GetByExperience 根据经验值获取等级
func (u *UserLevel) GetByExperience(db *gorm.DB, experience int) (*UserLevel, error) {
var level UserLevel
err := db.Where("min_experience <= ? AND max_experience >= ?", experience, experience).First(&level).Error
return &level, err
}
// GetAll 获取所有等级配置
func (u *UserLevel) GetAll(db *gorm.DB) ([]*UserLevel, error) {
var levels []*UserLevel
err := db.Order("level ASC").Find(&levels).Error
return levels, err
}
// Create 创建等级配置
func (u *UserLevel) Create(db *gorm.DB) (*UserLevel, error) {
err := db.Create(&u).Error
return u, err
}
// Update 更新等级配置
func (u *UserLevel) Update(db *gorm.DB) error {
return db.Model(&UserLevel{}).Where("level = ?", u.Level).Save(u).Error
}
// Delete 删除等级配置
func (u *UserLevel) Delete(db *gorm.DB) error {
return db.Where("level = ?", u.Level).Delete(&UserLevel{}).Error
}

@ -0,0 +1,65 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package dbr
import (
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
type UserMute struct {
*Model
UserId int64 `json:"user_id"`
TargetUserId int64 `json:"target_user_id"`
Reason string `json:"reason"`
}
func (UserMute) TableName() string {
return "p_user_mute"
}
func (m *UserMute) GetUserMute(db *gorm.DB, userId, targetUserId int64) (*UserMute, error) {
var mute UserMute
err := db.Unscoped().Where("user_id = ? AND target_user_id = ?", userId, targetUserId).First(&mute).Error
if err != nil {
logrus.Debugf("UserMute.GetUserMute get mute error:%s", err)
return nil, err
}
return &mute, nil
}
func (m *UserMute) DelUserMute(db *gorm.DB, userId, targetUserId int64) error {
return db.Unscoped().Where("user_id = ? AND target_user_id = ?", userId, targetUserId).Delete(m).Error
}
func (m *UserMute) ListUserMutes(db *gorm.DB, userId int64, limit int, offset int) (res []*UserMute, total int64, err error) {
db = db.Model(m).Where("user_id=?", userId)
if err = db.Count(&total).Error; err != nil {
return
}
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
if err = db.Find(&res).Error; err != nil {
return
}
return
}
func (m *UserMute) IsMuted(db *gorm.DB, userId, targetUserId int64) bool {
if _, err := m.GetUserMute(db, userId, targetUserId); err == nil {
return true
}
return false
}
func (m *UserMute) Create(db *gorm.DB) (*UserMute, error) {
err := db.Create(m).Error
return m, err
}
func (m *UserMute) UpdateInUnscoped(db *gorm.DB) error {
return db.Unscoped().Save(m).Error
}

@ -7,7 +7,6 @@ package dbr
import (
"time"
"github.com/rocboss/paopao-ce/internal/core/cs"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
@ -53,7 +52,7 @@ func (p *PostStar) Delete(db *gorm.DB) error {
}).Error
}
func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, typ cs.RelationTyp, limit int, offset int) (res []*PostStar, err error) {
func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, typ RelationTyp, limit int, offset int) (res []*PostStar, err error) {
tn := db.NamingStrategy.TableName("PostStar") + "."
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
@ -70,11 +69,11 @@ func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, typ cs.RelationTyp
}
db = db.Joins("Post")
switch typ {
case cs.RelationAdmin:
case RelationAdmin:
// admin have all permition to visit all type tweets
case cs.RelationFriend:
case RelationFriend:
db = db.Where("visibility = ? OR visibility = ?", PostVisitPublic, PostVisitFriend)
case cs.RelationSelf:
case RelationSelf:
db = db.Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID)
default:
db = db.Where("visibility=?", PostVisitPublic)
@ -84,7 +83,7 @@ func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, typ cs.RelationTyp
return
}
func (p *PostStar) Count(db *gorm.DB, typ cs.RelationTyp, conditions *ConditionsT) (res int64, err error) {
func (p *PostStar) Count(db *gorm.DB, typ RelationTyp, conditions *ConditionsT) (res int64, err error) {
tn := db.NamingStrategy.TableName("PostStar") + "."
if p.PostID > 0 {
db = db.Where(tn+"post_id = ?", p.PostID)
@ -99,11 +98,11 @@ func (p *PostStar) Count(db *gorm.DB, typ cs.RelationTyp, conditions *Conditions
}
db = db.Joins("Post")
switch typ {
case cs.RelationAdmin:
case RelationAdmin:
// admin have all permition to visit all type tweets
case cs.RelationFriend:
case RelationFriend:
db = db.Where("visibility = ? OR visibility = ?", PostVisitPublic, PostVisitFriend)
case cs.RelationSelf:
case RelationSelf:
db = db.Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID)
default:
db = db.Where("visibility=?", PostVisitPublic)

@ -5,7 +5,6 @@
package dbr
import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"gorm.io/gorm"
)
@ -25,7 +24,7 @@ type User struct {
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
IsAdmin bool `json:"is_admin"`
Experience int `gorm:"-" json:"experience"`
Experience int `json:"experience"`
Level int `gorm:"-" json:"level"`
}
@ -98,7 +97,7 @@ func (u *User) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]
return users, nil
}
func (u *User) ListUserInfoById(db *gorm.DB, ids []int64) (res cs.UserInfoList, err error) {
func (u *User) ListUserInfoById(db *gorm.DB, ids []int64) (res UserInfoList, err error) {
err = db.Model(u).Where("id IN ?", ids).Find(&res).Error
return
}

@ -42,7 +42,6 @@ type dataSrv struct {
core.TrendsManageServantA
core.UserManageService
core.UserMetricServantA
core.UserLevelService
core.ContactManageService
core.FollowingManageService
core.UserRelationService
@ -63,14 +62,12 @@ func NewDataService() (core.DataService, core.VersionInfo) {
pvs := security.NewPhoneVerifyService()
tms := newTweetMetricServentA(db)
ums := newUserMetricServentA(db)
uls := newUserLevelService(db)
cms := newCommentMetricServentA(db)
cis := cache.NewEventCacheIndexSrv(tms)
ds := &dataSrv{
TweetMetricServantA: tms,
CommentMetricServantA: cms,
UserMetricServantA: ums,
UserLevelService: uls,
WalletService: newWalletService(db),
MessageService: newMessageService(db),
TopicService: newTopicService(db),
@ -80,7 +77,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
CommentService: newCommentService(db),
CommentManageService: newCommentManageService(db),
TrendsManageServantA: newTrendsManageServentA(db),
UserManageService: newUserManageService(db, ums, uls),
UserManageService: newUserManageService(db, ums),
ContactManageService: newContactManageService(db),
FollowingManageService: newFollowingManageService(db),
UserRelationService: newUserRelationService(db),

@ -1,54 +0,0 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"gorm.io/gorm"
)
var (
_ core.UserLevelService = (*userLevelSrv)(nil)
)
// userLevelSrv 实现用户等级服务接口
type userLevelSrv struct {
db *gorm.DB
}
// newUserLevelService 创建用户等级服务实例
func newUserLevelService(db *gorm.DB) core.UserLevelService {
return &userLevelSrv{
db: db,
}
}
// GetLevelByExperience 根据经验值获取用户等级
func (s *userLevelSrv) GetLevelByExperience(experience int) (*dbr.UserLevel, error) {
level := &dbr.UserLevel{}
return level.GetByExperience(s.db, experience)
}
// GetAllLevels 获取所有等级配置
func (s *userLevelSrv) GetAllLevels() ([]*dbr.UserLevel, error) {
level := &dbr.UserLevel{}
return level.GetAll(s.db)
}
// CreateLevel 创建等级配置
func (s *userLevelSrv) CreateLevel(level *dbr.UserLevel) (*dbr.UserLevel, error) {
return level.Create(s.db)
}
// UpdateLevel 更新等级配置
func (s *userLevelSrv) UpdateLevel(level *dbr.UserLevel) error {
return level.Update(s.db)
}
// DeleteLevel 删除等级配置
func (s *userLevelSrv) DeleteLevel(level int) error {
return (&dbr.UserLevel{Level: level}).Delete(s.db)
}

@ -0,0 +1,142 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"encoding/json"
"fmt"
"sync"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"gorm.io/gorm"
)
const (
// 等级配置缓存键
levelConfigCacheKey = "user_level_config"
// 缓存过期时间30分钟
levelConfigCacheExpire = 30 * 60
)
var (
levelConfigCacheOnce sync.Once
levelConfigAppCache core.AppCache
)
// GetUserLevelByExperience 根据经验值获取用户等级
func GetUserLevelByExperience(db *gorm.DB, experience int) (int, error) {
// 尝试从缓存获取等级配置
levelMap, err := getLevelConfigFromCache()
if err != nil {
// 缓存获取失败,从数据库读取
levelMap, err = getLevelConfigFromDB(db)
if err != nil {
// 如果获取失败,使用默认等级配置
return getDefaultLevel(experience), nil
}
}
// 遍历等级配置找到对应的等级
for levelKey, expRange := range levelMap {
if experience >= expRange[0] && experience <= expRange[1] {
var level int
fmt.Sscanf(levelKey[2:], "%d", &level) // 从"lv1"提取数字
return level, nil
}
}
// 如果没有找到对应等级,返回默认等级
return getDefaultLevel(experience), nil
}
// getLevelConfigFromCache 从缓存获取等级配置
func getLevelConfigFromCache() (map[string][2]int, error) {
levelConfigCacheOnce.Do(func() {
levelConfigAppCache = cache.NewAppCache()
})
cacheData, err := levelConfigAppCache.Get(levelConfigCacheKey)
if err != nil {
return nil, err
}
var levelMap map[string][2]int
if err := json.Unmarshal(cacheData, &levelMap); err != nil {
return nil, err
}
return levelMap, nil
}
// getLevelConfigFromDB 从数据库获取等级配置并缓存
func getLevelConfigFromDB(db *gorm.DB) (map[string][2]int, error) {
setting := &dbr.OperationSetting{}
levelConfig, err := setting.GetByKey(db, "level_to_exp")
if err != nil {
// 如果获取失败,返回错误
return nil, err
}
var levelMap map[string][2]int
if err := json.Unmarshal([]byte(levelConfig.Value), &levelMap); err != nil {
return nil, err
}
// 将配置缓存起来
levelConfigCacheOnce.Do(func() {
levelConfigAppCache = cache.NewAppCache()
})
cacheData, _ := json.Marshal(levelMap)
levelConfigAppCache.Set(levelConfigCacheKey, cacheData, levelConfigCacheExpire)
return levelMap, nil
}
// UpdateLevelConfigCache 更新等级配置缓存(当运营设置更新时调用)
func UpdateLevelConfigCache() error {
levelConfigCacheOnce.Do(func() {
levelConfigAppCache = cache.NewAppCache()
})
return levelConfigAppCache.Delete(levelConfigCacheKey)
}
// UpdateOperationSetting 更新运营设置(带缓存清理)
func UpdateOperationSetting(db *gorm.DB, key, value, description string) error {
setting := &dbr.OperationSetting{
Key: key,
Value: value,
Description: description,
}
// 更新数据库
err := setting.Update(db)
if err != nil {
return err
}
// 如果是等级配置更新,清理缓存
if key == "level_to_exp" {
UpdateLevelConfigCache()
}
return nil
}
// getDefaultLevel 获取默认等级
func getDefaultLevel(experience int) int {
if experience >= 0 && experience <= 100 {
return 1
} else if experience >= 101 && experience <= 300 {
return 2
} else if experience >= 301 && experience <= 600 {
return 3
} else if experience >= 601 && experience <= 1000 {
return 4
} else {
return 5
}
}

@ -0,0 +1,73 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var (
_ core.UserMuteManageService = (*userMuteManageSrv)(nil)
)
type userMuteManageSrv struct {
db *gorm.DB
m *dbr.UserMute
u *dbr.User
}
func newUserMuteManageService(db *gorm.DB) core.UserMuteManageService {
return &userMuteManageSrv{
db: db,
m: &dbr.UserMute{},
u: &dbr.User{},
}
}
func (s *userMuteManageSrv) MuteUser(userId int64, targetUserId int64, reason string) error {
if _, err := s.m.GetUserMute(s.db, userId, targetUserId); err != nil {
mute := &dbr.UserMute{
UserId: userId,
TargetUserId: targetUserId,
Reason: reason,
}
if _, err = mute.Create(s.db); err != nil {
logrus.Errorf("userMuteManageSrv.muteUser create new mute err:%s", err)
return err
}
}
return nil
}
func (s *userMuteManageSrv) UnmuteUser(userId int64, targetUserId int64) error {
return s.m.DelUserMute(s.db, userId, targetUserId)
}
func (s *userMuteManageSrv) ListMutes(userId int64, limit, offset int) (*ms.UserMuteList, error) {
mutes, total, err := s.m.ListUserMutes(s.db, userId, limit, offset)
if err != nil {
return nil, err
}
res := &ms.UserMuteList{
Total: total,
}
for _, mute := range mutes {
res.Mutes = append(res.Mutes, ms.UserMuteItem{
UserId: mute.UserId,
TargetUserId: mute.TargetUserId,
Reason: mute.Reason,
CreatedOn: mute.CreatedOn,
})
}
return res, nil
}
func (s *userMuteManageSrv) IsMuted(userId int64, targetUserId int64) bool {
return s.m.IsMuted(s.db, userId, targetUserId)
}

@ -183,7 +183,15 @@ func (s *topicSrv) listTags(conditions *ms.ConditionsT, limit int, offset int) (
}
for _, userInfo := range userInfos {
for _, item = range tagMap[userInfo.ID] {
item.User = userInfo
item.User = &cs.UserInfo{
ID: userInfo.ID,
Nickname: userInfo.Nickname,
Username: userInfo.Username,
Status: userInfo.Status,
Avatar: userInfo.Avatar,
IsAdmin: userInfo.IsAdmin,
CreatedOn: userInfo.CreatedOn,
}
}
}
}
@ -231,7 +239,15 @@ func (s *topicSrv) tagsFormatB(userTopicsMap map[int64]*topicInfo, tags cs.TagIn
tagFormated := tag.Format()
for _, user := range users {
if user.ID == tagFormated.UserID {
tagFormated.User = user
tagFormated.User = &cs.UserInfo{
ID: user.ID,
Nickname: user.Nickname,
Username: user.Username,
Status: user.Status,
Avatar: user.Avatar,
IsAdmin: user.IsAdmin,
CreatedOn: user.CreatedOn,
}
}
}
tagList = append(tagList, tagFormated)
@ -342,7 +358,15 @@ func (s *topicSrvA) ListTags(typ cs.TagType, offset, limit int) (res cs.TagList,
}
for _, userInfo := range userInfos {
for _, item = range tagMap[userInfo.ID] {
item.User = userInfo
item.User = &cs.UserInfo{
ID: userInfo.ID,
Nickname: userInfo.Nickname,
Username: userInfo.Username,
Status: userInfo.Status,
Avatar: userInfo.Avatar,
IsAdmin: userInfo.IsAdmin,
CreatedOn: userInfo.CreatedOn,
}
}
}
}

@ -535,10 +535,10 @@ func (s *tweetSrv) GetUserPostStars(userID int64, limit int, offset int) ([]*ms.
}
return star.List(s.db, &dbr.ConditionsT{
"ORDER": s.db.NamingStrategy.TableName("PostStar") + ".id DESC",
}, cs.RelationSelf, limit, offset)
}, dbr.RelationTyp(cs.RelationSelf), limit, offset)
}
func (s *tweetSrv) ListUserStarTweets(user *cs.VistUser, limit int, offset int) (res []*ms.PostStar, total int64, err error) {
func (s *tweetSrv) ListUserStarTweets(user *dbr.VistUser, limit int, offset int) (res []*ms.PostStar, total int64, err error) {
star := &dbr.PostStar{
UserID: user.UserId,
}
@ -551,14 +551,14 @@ func (s *tweetSrv) ListUserStarTweets(user *cs.VistUser, limit int, offset int)
return
}
func (s *tweetSrv) getUserTweets(db *gorm.DB, user *cs.VistUser, limit int, offset int) (res []*ms.Post, total int64, err error) {
func (s *tweetSrv) getUserTweets(db *gorm.DB, user *dbr.VistUser, limit int, offset int) (res []*ms.Post, total int64, err error) {
visibilities := []core.PostVisibleT{core.PostVisitPublic}
switch user.RelTyp {
case cs.RelationAdmin, cs.RelationSelf:
case dbr.RelationAdmin, dbr.RelationSelf:
visibilities = append(visibilities, core.PostVisitPrivate, core.PostVisitFriend)
case cs.RelationFriend:
case dbr.RelationFriend:
visibilities = append(visibilities, core.PostVisitFriend)
case cs.RelationGuest:
case dbr.RelationGuest:
fallthrough
default:
// nothing
@ -575,12 +575,12 @@ func (s *tweetSrv) getUserTweets(db *gorm.DB, user *cs.VistUser, limit int, offs
return
}
func (s *tweetSrv) ListUserMediaTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
func (s *tweetSrv) ListUserMediaTweets(user *dbr.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
db := s.db.Table(_post_by_media_).Where("user_id=?", user.UserId)
return s.getUserTweets(db, user, limit, offset)
}
func (s *tweetSrv) ListUserCommentTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
func (s *tweetSrv) ListUserCommentTweets(user *dbr.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
db := s.db.Table(_post_by_comment_).Where("comment_user_id=?", user.UserId)
return s.getUserTweets(db, user, limit, offset)
}
@ -589,7 +589,7 @@ func (s *tweetSrv) GetUserPostStarCount(userID int64) (int64, error) {
star := &dbr.PostStar{
UserID: userID,
}
return star.Count(s.db, cs.RelationSelf, &dbr.ConditionsT{})
return star.Count(s.db, dbr.RelationTyp(cs.RelationSelf), &dbr.ConditionsT{})
}
func (s *tweetSrv) GetUserPostCollection(postID, userID int64) (*ms.PostCollection, error) {

@ -9,7 +9,6 @@ import (
"strings"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"gorm.io/gorm"
@ -22,7 +21,6 @@ var (
type userManageSrv struct {
db *gorm.DB
ums core.UserMetricServantA
uls core.UserLevelService
_userProfileJoins string
_userProfileWhere string
@ -33,11 +31,10 @@ type userRelationSrv struct {
db *gorm.DB
}
func newUserManageService(db *gorm.DB, ums core.UserMetricServantA, uls core.UserLevelService) core.UserManageService {
func newUserManageService(db *gorm.DB, ums core.UserMetricServantA) core.UserManageService {
return &userManageSrv{
db: db,
ums: ums,
uls: uls,
_userProfileJoins: fmt.Sprintf("LEFT JOIN %s m ON %s.id=m.user_id", _userMetric_, _user_),
_userProfileWhere: fmt.Sprintf("%s.username=? AND %s.is_del=0", _user_, _user_),
_userProfileColumns: []string{
@ -50,8 +47,8 @@ func newUserManageService(db *gorm.DB, ums core.UserMetricServantA, uls core.Use
fmt.Sprintf("%s.balance", _user_),
fmt.Sprintf("%s.is_admin", _user_),
fmt.Sprintf("%s.created_on", _user_),
fmt.Sprintf("%s.experience", _user_),
"m.tweets_count",
"m.experience",
},
}
}
@ -69,20 +66,12 @@ func (s *userManageSrv) GetUserByID(id int64) (*ms.User, error) {
},
}
user, err := user.Get(s.db)
metric, err := s.ums.GetUserMetric(id)
if err != nil {
return nil, err
}
user.Experience = metric.Experience
// 获取用户等级
level, err := s.uls.GetLevelByExperience(user.Experience)
if err == nil {
user.Level = level.Level
} else {
// 默认等级为1
user.Level = 1
}
// 默认等级为1
user.Level = 1
return user, nil
}
@ -95,25 +84,14 @@ func (s *userManageSrv) GetUserByUsername(username string) (*ms.User, error) {
if err != nil {
return nil, err
}
metric, err := s.ums.GetUserMetric(user.ID)
if err != nil {
return user, nil
}
user.Experience = metric.Experience
// 获取用户等级
level, err := s.uls.GetLevelByExperience(user.Experience)
if err == nil {
user.Level = level.Level
} else {
// 默认等级为1
user.Level = 1
}
// 默认等级为1
user.Level = 1
return user, nil
}
func (s *userManageSrv) UserProfileByName(username string) (res *cs.UserProfile, err error) {
func (s *userManageSrv) UserProfileByName(username string) (res *dbr.UserProfile, err error) {
err = s.db.Table(_user_).Joins(s.
_userProfileJoins).
Where(s._userProfileWhere, username).
@ -121,14 +99,8 @@ func (s *userManageSrv) UserProfileByName(username string) (res *cs.UserProfile,
First(&res).Error
if err == nil && res != nil {
// 获取用户等级
level, err := s.uls.GetLevelByExperience(res.Experience)
if err == nil {
res.Level = level.Level
} else {
// 默认等级为1
res.Level = 1
}
// 默认等级为1
res.Level = 1
}
return
@ -145,21 +117,10 @@ func (s *userManageSrv) GetUserByPhone(phone string) ([]*ms.User, error) {
return nil, err
}
// 为每个用户设置经验值
// 为每个用户设置等级
for _, u := range users {
metric, err := s.ums.GetUserMetric(u.ID)
if err == nil {
u.Experience = metric.Experience
// 获取用户等级
level, err := s.uls.GetLevelByExperience(u.Experience)
if err == nil {
u.Level = level.Level
} else {
// 默认等级为1
u.Level = 1
}
}
// 默认等级为1
u.Level = 1
}
return users, nil
@ -174,21 +135,10 @@ func (s *userManageSrv) GetUsersByIDs(ids []int64) ([]*ms.User, error) {
return nil, err
}
// 为每个用户设置经验值
// 为每个用户设置等级
for _, u := range users {
metric, err := s.ums.GetUserMetric(u.ID)
if err == nil {
u.Experience = metric.Experience
// 获取用户等级
level, err := s.uls.GetLevelByExperience(u.Experience)
if err == nil {
u.Level = level.Level
} else {
// 默认等级为1
u.Level = 1
}
}
// 默认等级为1
u.Level = 1
}
return users, nil
@ -213,21 +163,10 @@ func (s *userManageSrv) GetUsersByKeyword(keyword string) ([]*ms.User, error) {
return nil, err
}
// 为每个用户设置经验值
// 为每个用户设置等级
for _, u := range users {
metric, err := s.ums.GetUserMetric(u.ID)
if err == nil {
u.Experience = metric.Experience
// 获取用户等级
level, err := s.uls.GetLevelByExperience(u.Experience)
if err == nil {
u.Level = level.Level
} else {
// 默认等级为1
u.Level = 1
}
}
// 默认等级为1
u.Level = 1
}
return users, nil

@ -97,3 +97,4 @@ func NewBridgeTweetSearchService(ts core.TweetSearchService) core.TweetSearchSer
return bts
}

@ -12,6 +12,23 @@ import (
"github.com/rocboss/paopao-ce/pkg/json"
)
type CacheResp struct {
Data any
JsonResp stdJson.RawMessage
}
func (r *CacheResp) Render(c *gin.Context) {
if len(r.JsonResp) != 0 {
c.JSON(http.StatusOK, r.JsonResp)
} else {
c.JSON(http.StatusOK, &JsonResp{
Code: 0,
Msg: "success",
Data: r.Data,
})
}
}
type CachePageResp struct {
Data *PageResp
JsonResp stdJson.RawMessage

@ -1,8 +1,60 @@
package web
import (
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/xerror"
)
// UserCheckInReq 用户签到请求
type UserCheckInReq struct {
UserId int64 `json:"-"` // 用户ID从JWT中获取
}
// UserCheckInResp 用户签到响应
type UserCheckInResp struct {
// CheckInCount 签到次数
CheckInExp int `json:"check_in_exp"`
// CheckInDays 签到天数
CheckInDays int `json:"check_in_days"`
CheckInExp int `json:"check_in_exp"` // 签到获得的经验值
CheckInDays int `json:"check_in_days"` // 用户总签到天数
UserLevel int `json:"user_level"` // 用户当前等级
UserExp int `json:"user_exp"` // 用户当前经验值
}
// GetCheckInRankReq 获取签到排行榜请求
type GetCheckInRankReq struct {
UserId int64 `json:"-"` // 用户ID从JWT中获取
}
// GetCheckInRankResp 获取签到排行榜响应
type GetCheckInRankResp struct {
Ranks []CheckInRankItem `json:"ranks"`
}
// CheckInRankItem 签到排行榜项
type CheckInRankItem struct {
UserID int64 `json:"user_id"`
Username string `json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
CheckInExp int `json:"check_in_exp"` // 今日签到获得的经验值
CheckInTime int64 `json:"check_in_time"` // 签到时间戳
}
// Bind 绑定请求参数
func (r *UserCheckInReq) Bind(c *gin.Context) error {
userId, exist := base.UserIdFrom(c)
if !exist {
return xerror.UnauthorizedAuthNotExist
}
r.UserId = userId
return nil
}
// Bind 绑定请求参数
func (r *GetCheckInRankReq) Bind(c *gin.Context) error {
userId, exist := base.UserIdFrom(c)
if !exist {
return xerror.UnauthorizedAuthNotExist
}
r.UserId = userId
return nil
}

@ -0,0 +1,310 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/xerror"
)
// 长文章相关请求和响应结构
type ArticleContentItem struct {
Type ms.ArticleContentT `json:"type" binding:"required"`
Content string `json:"content" binding:"required"`
Sort int64 `json:"sort"`
}
type CreateArticleReq struct {
BaseInfo `json:"-" binding:"-"`
Title string `json:"title" binding:"required,max=200"`
Summary string `json:"summary" binding:"max=500"`
SeriesID *int64 `json:"series_id"`
Contents []ArticleContentItem `json:"contents" binding:"required,dive"`
Visibility ms.ArticleVisibleT `json:"visibility"`
Price int64 `json:"price"`
Tags []string `json:"tags"`
}
type CreateArticleResp struct {
ID int64 `json:"id"`
}
type UpdateArticleReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
Title string `json:"title" binding:"required,max=200"`
Summary string `json:"summary" binding:"max=500"`
SeriesID *int64 `json:"series_id"`
Contents []ArticleContentItem `json:"contents" binding:"required,dive"`
Visibility ms.ArticleVisibleT `json:"visibility"`
Price int64 `json:"price"`
Tags []string `json:"tags"`
}
type DeleteArticleReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type GetArticleReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `form:"id" binding:"required"`
}
type GetArticleResp struct {
joint.CacheResp
}
type ListArticlesReq struct {
BaseInfo `json:"-" binding:"-"`
Query string `form:"query"`
Visibility []ms.ArticleVisibleT `form:"visibility"`
Type string `form:"type"`
Style string `form:"style"`
Page int `form:"-" binding:"-"`
PageSize int `form:"-" binding:"-"`
}
type ListArticlesResp struct {
joint.CachePageResp
}
type ArticleStarReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type ArticleStarResp struct {
Status bool `json:"status"`
}
type ArticleCollectionReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type ArticleCollectionResp struct {
Status bool `json:"status"`
}
type CreateArticleCommentReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
Content string `json:"content" binding:"required,max=1000"`
}
type CreateArticleCommentResp struct {
ID int64 `json:"id"`
}
type DeleteArticleCommentReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type ArticleCommentsReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `form:"id" binding:"required"`
Page int `form:"-" binding:"-"`
PageSize int `form:"-" binding:"-"`
}
type ArticleCommentsResp struct {
joint.CachePageResp
}
type CreateArticleCommentReplyReq struct {
BaseInfo `json:"-" binding:"-"`
CommentID int64 `json:"comment_id" binding:"required"`
ReplyToID *int64 `json:"reply_to_id"`
Content string `json:"content" binding:"required,max=1000"`
}
type CreateArticleCommentReplyResp struct {
ID int64 `json:"id"`
}
type DeleteArticleCommentReplyReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type CreateArticleSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
Title string `json:"title" binding:"required,max=200"`
Description string `json:"description" binding:"max=1000"`
Cover string `json:"cover"`
Visibility ms.ArticleVisibleT `json:"visibility"`
Tags []string `json:"tags"`
}
type CreateArticleSeriesResp struct {
ID int64 `json:"id"`
}
type UpdateArticleSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
Title string `json:"title" binding:"required,max=200"`
Description string `json:"description" binding:"max=1000"`
Cover string `json:"cover"`
Visibility ms.ArticleVisibleT `json:"visibility"`
Tags []string `json:"tags"`
}
type DeleteArticleSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type GetArticleSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `form:"id" binding:"required"`
}
type GetArticleSeriesResp struct {
joint.CacheResp
}
type ListArticleSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
Query string `form:"query"`
Visibility []ms.ArticleVisibleT `form:"visibility"`
Page int `form:"-" binding:"-"`
PageSize int `form:"-" binding:"-"`
}
type ListArticleSeriesResp struct {
joint.CachePageResp
}
type AddToSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
SeriesID int64 `json:"series_id" binding:"required"`
ArticleID int64 `json:"article_id" binding:"required"`
Sort int `json:"sort"`
}
type RemoveFromSeriesReq struct {
BaseInfo `json:"-" binding:"-"`
SeriesID int64 `json:"series_id" binding:"required"`
ArticleID int64 `json:"article_id" binding:"required"`
}
type GetUserArticlesReq struct {
BaseInfo `json:"-" binding:"-"`
Username string `form:"username" binding:"required"`
Style string `form:"style"`
Page int `form:"-" binding:"-"`
PageSize int `form:"-" binding:"-"`
}
type GetUserArticlesResp struct {
joint.CachePageResp
}
type GetUserArticleCollectionsReq BasePageReq
type GetUserArticleCollectionsResp base.PageResp
type GetUserArticleStarsReq BasePageReq
type GetUserArticleStarsResp base.PageResp
type ArticleStarStatusReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `form:"id" binding:"required"`
}
type ArticleStarStatusResp struct {
Status bool `json:"status"`
}
type ArticleCollectionStatusReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `form:"id" binding:"required"`
}
type ArticleCollectionStatusResp struct {
Status bool `json:"status"`
}
type AdminDeleteArticleReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
Reason string `json:"reason" binding:"required"`
}
// Bind methods for request validation and binding
func (r *ListArticlesReq) Bind(c *gin.Context) error {
r.Page, r.PageSize = app.GetPageInfo(c)
return nil
}
func (r *ArticleCommentsReq) Bind(c *gin.Context) error {
r.Page, r.PageSize = app.GetPageInfo(c)
return nil
}
func (r *ListArticleSeriesReq) Bind(c *gin.Context) error {
r.Page, r.PageSize = app.GetPageInfo(c)
return nil
}
func (r *GetUserArticlesReq) Bind(c *gin.Context) error {
r.Page, r.PageSize = app.GetPageInfo(c)
return nil
}
func (r *GetUserArticleCollectionsReq) Bind(c *gin.Context) error {
return (*BasePageReq)(r).Bind(c)
}
func (r *GetUserArticleStarsReq) Bind(c *gin.Context) error {
return (*BasePageReq)(r).Bind(c)
}
func (r *ArticleStarStatusReq) Bind(c *gin.Context) error {
userId, exist := base.UserIdFrom(c)
if !exist {
return xerror.UnauthorizedAuthNotExist
}
r.BaseInfo = BaseInfo{
User: &ms.User{Model: &ms.Model{ID: userId}},
}
r.ID = convert.StrTo(c.Query("id")).MustInt64()
return nil
}
func (r *ArticleCollectionStatusReq) Bind(c *gin.Context) error {
userId, exist := base.UserIdFrom(c)
if !exist {
return xerror.UnauthorizedAuthNotExist
}
r.BaseInfo = BaseInfo{
User: &ms.User{Model: &ms.Model{ID: userId}},
}
r.ID = convert.StrTo(c.Query("id")).MustInt64()
return nil
}
func (r *CreateArticleReq) ValidateContents() error {
totalWords := 0
for _, content := range r.Contents {
if content.Type == ms.ArticleContentTypeText {
// 计算字数,这里简化为字符数,实际应该用中文分词库
totalWords += len([]rune(content.Content))
}
}
if totalWords > 50000 {
return xerror.NewError(400, "文章字数超过50000字限制")
}
return nil
}

@ -0,0 +1,28 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type BlockUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
Reason string `json:"reason"`
}
type UnblockUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
}
type ListBlocksReq struct {
BaseInfo `json:"-" binding:"-"`
joint.BasePageInfo
}
type ListBlocksResp base.PageResp

@ -178,3 +178,7 @@ func (r *TweetStarStatusReq) Bind(c *gin.Context) error {
r.TweetId = convert.StrTo(c.Query("id")).MustInt64()
return nil
}
type StreamMessagesReq struct {
SimpleInfo `json:"-" binding:"-"`
}

@ -95,6 +95,8 @@ type GetUserProfileResp struct {
Follows int64 `json:"follows"`
Followings int64 `json:"followings"`
TweetsCount int `json:"tweets_count"`
Experience int `json:"experience"`
Level int `json:"level"`
}
type TopicListReq struct {

@ -0,0 +1,28 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type MuteUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
Reason string `json:"reason"`
}
type UnmuteUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
}
type ListMutesReq struct {
BaseInfo `json:"-" binding:"-"`
joint.BasePageInfo
}
type ListMutesResp base.PageResp

@ -89,6 +89,16 @@ var (
ErrGetFollowCountFailed = xerror.NewError(80104, "获取关注计数信息失败")
ErrNotAllowFollowSelf = xerror.NewError(80105, "不能关注自己")
ErrNotAllowUnfollowSelf = xerror.NewError(80106, "不能取消关注自己")
ErrBlockUserFailed = xerror.NewError(80107, "拉黑用户失败")
ErrUnblockUserFailed = xerror.NewError(80108, "取消拉黑失败")
ErrListBlocksFailed = xerror.NewError(80109, "获取拉黑列表失败")
ErrNotAllowBlockSelf = xerror.NewError(80110, "不能拉黑自己")
ErrAlreadyBlocked = xerror.NewError(80111, "该用户已被拉黑")
ErrMuteUserFailed = xerror.NewError(80112, "屏蔽用户失败")
ErrUnmuteUserFailed = xerror.NewError(80113, "取消屏蔽失败")
ErrListMutesFailed = xerror.NewError(80114, "获取屏蔽列表失败")
ErrNotAllowMuteSelf = xerror.NewError(80115, "不能屏蔽自己")
ErrAlreadyMuted = xerror.NewError(80116, "该用户已被屏蔽")
ErrGetIndexTrendsFailed = xerror.NewError(802001, "获取动态条栏信息失败")
@ -105,5 +115,27 @@ var (
ErrFileInvalidExt = xerror.NewError(10201, "文件类型不合法")
ErrFileInvalidSize = xerror.NewError(10202, "文件大小超限")
// 长文章相关错误
ErrCreateArticle = xerror.NewError(11001, "长文章创建失败")
ErrUpdateArticle = xerror.NewError(11002, "长文章更新失败")
ErrDeleteArticle = xerror.NewError(11003, "长文章删除失败")
ErrArticleNotExist = xerror.NewError(11004, "长文章不存在")
ErrArticleWordLimit = xerror.NewError(11005, "长文章字数超过限制")
ErrStarArticle = xerror.NewError(11006, "长文章点赞操作失败")
ErrCollectionArticle = xerror.NewError(11007, "长文章收藏操作失败")
ErrCreateComment = xerror.NewError(11008, "评论发布失败")
ErrDeleteComment = xerror.NewError(11009, "评论删除失败")
ErrCommentNotExist = xerror.NewError(11010, "评论不存在")
ErrCommentContentEmpty = xerror.NewError(11011, "评论内容不能为空")
ErrCreateCommentReply = xerror.NewError(11012, "评论回复失败")
ErrDeleteCommentReply = xerror.NewError(11013, "评论回复删除失败")
ErrCommentReplyNotExist = xerror.NewError(11014, "评论回复不存在")
ErrCreateArticleSeries = xerror.NewError(11015, "长文章系列创建失败")
ErrUpdateArticleSeries = xerror.NewError(11016, "长文章系列更新失败")
ErrDeleteArticleSeries = xerror.NewError(11017, "长文章系列删除失败")
ErrArticleSeriesNotExist = xerror.NewError(11018, "长文章系列不存在")
ErrAddToSeries = xerror.NewError(11019, "添加到系列失败")
ErrRemoveFromSeries = xerror.NewError(11020, "从系列移除失败")
ErrNotImplemented = xerror.NewError(10501, "功能未实现")
)

@ -20,6 +20,7 @@ import (
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/infra/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
@ -388,9 +389,9 @@ func (s *DaoServant) DeleteSearchPost(post *ms.Post) error {
return s.Ts.DeleteDocuments([]string{fmt.Sprintf("%d", post.ID)})
}
func (s *DaoServant) RelationTypFrom(me *ms.User, username string) (res *cs.VistUser, err error) {
res = &cs.VistUser{
RelTyp: cs.RelationSelf,
func (s *DaoServant) RelationTypFrom(me *ms.User, username string) (res *dbr.VistUser, err error) {
res = &dbr.VistUser{
RelTyp: dbr.RelationTyp(cs.RelationSelf),
Username: username,
}
// visit by self
@ -405,16 +406,16 @@ func (s *DaoServant) RelationTypFrom(me *ms.User, username string) (res *cs.Vist
res.UserId = he.ID
// visit by guest
if me == nil {
res.RelTyp = cs.RelationGuest
res.RelTyp = dbr.RelationGuest
return
}
// visit by admin/friend/other
if me.IsAdmin {
res.RelTyp = cs.RelationAdmin
res.RelTyp = dbr.RelationAdmin
} else if s.Ds.IsFriend(me.ID, he.ID) {
res.RelTyp = cs.RelationFriend
res.RelTyp = dbr.RelationFriend
} else {
res.RelTyp = cs.RelationGuest
res.RelTyp = dbr.RelationGuest
}
return
}

@ -0,0 +1,109 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/xerror"
"github.com/sirupsen/logrus"
)
var (
_ api.Blockship = (*blockshipSrv)(nil)
)
type blockshipSrv struct {
api.UnimplementedBlockshipServant
*base.DaoServant
}
func (s *blockshipSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.Jwt()}
}
func (s *blockshipSrv) BlockUser(r *web.BlockUserReq) error {
if r.User == nil {
return xerror.UnauthorizedTokenError
} else if r.User.ID == r.UserId {
return web.ErrNotAllowBlockSelf
}
// 验证reason长度防止过长输入
if len(r.Reason) > 500 {
r.Reason = r.Reason[:500]
}
// 防止重复拉黑
if s.Ds.IsBlocked(r.User.ID, r.UserId) {
return web.ErrAlreadyBlocked
}
// 拉黑前先取消关注关系
if s.Ds.IsFollow(r.User.ID, r.UserId) {
if err := s.Ds.UnfollowUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.UnfollowUser err when blocking: %s", err)
}
}
if s.Ds.IsFollow(r.UserId, r.User.ID) {
if err := s.Ds.UnfollowUser(r.UserId, r.User.ID); err != nil {
logrus.Errorf("Ds.UnfollowUser err when blocking: %s", err)
}
}
if err := s.Ds.BlockUser(r.User.ID, r.UserId, r.Reason); err != nil {
logrus.Errorf("Ds.BlockUser err: %s userId: %d targetId: %d", err, r.User.ID, r.UserId)
return web.ErrBlockUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionBlock, r.User.ID)
return nil
}
func (s *blockshipSrv) UnblockUser(r *web.UnblockUserReq) error {
if r.User == nil {
return xerror.UnauthorizedTokenError
} else if r.User.ID == r.UserId {
return web.ErrNotAllowUnblockSelf
}
if err := s.Ds.UnblockUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.UnblockUser err: %s userId: %d targetId: %d", err, r.User.ID, r.UserId)
return web.ErrUnblockUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionUnblock, r.User.ID)
return nil
}
func (s *blockshipSrv) ListBlocks(r *web.ListBlocksReq) (*web.ListBlocksResp, error) {
if r.User == nil {
return nil, xerror.UnauthorizedTokenError
}
res, err := s.Ds.ListBlocks(r.User.ID, r.PageSize, (r.Page-1)*r.PageSize)
if err != nil {
logrus.Errorf("Ds.ListBlocks err: %s", err)
return nil, web.ErrListBlocksFailed
}
resp := base.PageRespFrom(res.Blocks, r.Page, r.PageSize, res.Total)
return (*web.ListBlocksResp)(resp), nil
}
func newBlockshipSrv(s *base.DaoServant) api.Blockship {
return &blockshipSrv{
DaoServant: s,
}
}

@ -6,8 +6,9 @@ package web
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"unicode/utf8"
@ -15,6 +16,7 @@ import (
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
@ -418,6 +420,113 @@ func (s *coreSrv) TweetStarStatus(req *web.TweetStarStatusReq) (*web.TweetStarSt
return resp, nil
}
func (s *coreSrv) StreamMessages(req *web.StreamMessagesReq, c *gin.Context) error {
// 设置SSE响应头
c.Header("Content-Type", "text/event-stream")
c.Header("Cache-Control", "no-cache")
c.Header("Connection", "keep-alive")
c.Header("Access-Control-Allow-Origin", "*")
// 获取用户ID
userId := req.Uid
if userId <= 0 {
s.sendSSEEvent(c, "error", "Unauthorized")
return nil
}
// 创建上下文用于控制连接
ctx, cancel := context.WithCancel(c.Request.Context())
defer cancel()
// 记录已发送的消息ID避免重复发送
sentMessageIDs := make(map[int64]bool)
// 发送初始连接确认
s.sendSSEEvent(c, "connected", fmt.Sprintf(`{"user_id": %d, "timestamp": %d}`, userId, time.Now().Unix()))
// 定期检查新消息
ticker := time.NewTicker(5 * time.Second) // 每5秒检查一次
defer ticker.Stop()
for {
select {
case <-ctx.Done():
// 连接断开
return nil
case <-ticker.C:
// 获取未读消息
messages, _, err := s.Ds.GetMessages(userId, cs.StyleMsgUnread, 50, 0)
if err != nil {
logrus.Errorf("StreamMessages: failed to get messages for user %d: %v", userId, err)
continue
}
// 过滤并发送新消息
for _, msg := range messages {
if sentMessageIDs[msg.ID] {
continue // 已发送过,跳过
}
// 格式化消息数据
messageData := map[string]interface{}{
"id": msg.ID,
"type": msg.Type,
"brief": msg.Brief,
"content": msg.Content,
"sender_user_id": msg.SenderUserID,
"receiver_user_id": msg.ReceiverUserID,
"post_id": msg.PostID,
"comment_id": msg.CommentID,
"reply_id": msg.ReplyID,
"is_read": msg.IsRead,
"created_on": msg.CreatedOn,
}
// 发送消息事件
if err := s.sendSSEEvent(c, "message", messageData); err != nil {
logrus.Errorf("StreamMessages: failed to send SSE event: %v", err)
return nil
}
// 标记为已发送
sentMessageIDs[msg.ID] = true
}
// 手动刷新响应,确保消息立即发送
s.flushSSE(c)
}
}
}
// sendSSEEvent 发送SSE事件
func (s *coreSrv) sendSSEEvent(c *gin.Context, event string, data interface{}) error {
var dataStr string
switch v := data.(type) {
case string:
dataStr = v
default:
jsonBytes, err := json.Marshal(data)
if err != nil {
return err
}
dataStr = string(jsonBytes)
}
// 按照SSE格式写入数据
if event != "" {
fmt.Fprintf(c.Writer, "event: %s\n", event)
}
fmt.Fprintf(c.Writer, "data: %s\n\n", dataStr)
return nil
}
// flushSSE 刷新SSE响应
func (s *coreSrv) flushSSE(c *gin.Context) {
if flusher, ok := c.Writer.(http.Flusher); ok {
flusher.Flush()
}
}
func (s *coreSrv) messagesFromCache(req *web.GetMessagesReq, limit int, offset int) (res *web.GetMessagesResp, key string, ok bool) {
key = fmt.Sprintf("%s%d:%s:%d:%d", s.prefixMessages, req.Uid, req.Style, limit, offset)
if data, err := s.wc.Get(key); err == nil {

@ -99,9 +99,12 @@ func (s *followshipSrv) FollowUser(r *web.FollowUserReq) error {
} else if r.User.ID == r.UserId {
return web.ErrNotAllowFollowSelf
}
// TODO: 检查是否已被对方拉黑 (暂时跳过)
if err := s.Ds.FollowUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.FollowUser err: %s userId: %d followId: %d", err, r.User.ID, r.UserId)
return web.ErrUnfollowUserFailed
return web.ErrFolloUserFailed
}
// 触发缓存更新事件
// TODO: 合并成一个事件

@ -201,13 +201,13 @@ func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTwe
return
}
func (s *looseSrv) userTweetsFromCache(req *web.GetUserTweetsReq, user *cs.VistUser) (res *web.GetUserTweetsResp, key string, ok bool) {
func (s *looseSrv) userTweetsFromCache(req *web.GetUserTweetsReq, user *dbr.VistUser) (res *web.GetUserTweetsResp, key string, ok bool) {
switch req.Style {
case web.UserPostsStylePost, web.UserPostsStyleHighlight, web.UserPostsStyleMedia:
key = fmt.Sprintf("%s%d:%s:%s:%d:%d", s.prefixUserTweets, user.UserId, req.Style, user.RelTyp, req.Page, req.PageSize)
default:
meName := "_"
if user.RelTyp != cs.RelationGuest {
if user.RelTyp != dbr.RelationTyp(cs.RelationGuest) {
meName = req.User.Username
}
key = fmt.Sprintf("%s%d:%s:%s:%d:%d", s.prefixUserTweets, user.UserId, req.Style, meName, req.Page, req.PageSize)
@ -222,7 +222,7 @@ func (s *looseSrv) userTweetsFromCache(req *web.GetUserTweetsReq, user *cs.VistU
return
}
func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUser) (*web.GetUserTweetsResp, error) {
func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *dbr.VistUser) (*web.GetUserTweetsResp, error) {
stars, totalRows, err := s.Ds.ListUserStarTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
if err != nil {
logrus.Errorf("getUserStarTweets err[1]: %s", err)
@ -255,7 +255,7 @@ func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUse
}, nil
}
func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser) (*web.GetUserTweetsResp, error) {
func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *dbr.VistUser) (*web.GetUserTweetsResp, error) {
var (
tweets []*ms.Post
total int64
@ -294,18 +294,18 @@ func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser)
}, nil
}
func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *cs.VistUser, isHighlight bool) (*web.GetUserTweetsResp, error) {
func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *dbr.VistUser, isHighlight bool) (*web.GetUserTweetsResp, error) {
style := cs.StyleUserTweetsGuest
switch user.RelTyp {
case cs.RelationAdmin:
case dbr.RelationAdmin:
style = cs.StyleUserTweetsAdmin
case cs.RelationSelf:
case dbr.RelationSelf:
style = cs.StyleUserTweetsSelf
case cs.RelationFriend:
case dbr.RelationFriend:
style = cs.StyleUserTweetsFriend
case cs.RelationFollowing:
case dbr.RelationFollowing:
style = cs.StyleUserTweetsFollowing
case cs.RelationGuest:
case dbr.RelationGuest:
fallthrough
default:
// nothing
@ -368,6 +368,8 @@ func (s *looseSrv) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfi
Follows: follows,
Followings: followings,
TweetsCount: he.TweetsCount,
Experience: he.Experience,
Level: he.Level,
}, nil
}

@ -0,0 +1,97 @@
// Copyright 2024 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package web
import (
"github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/xerror"
"github.com/sirupsen/logrus"
)
var (
_ api.Muteship = (*muteshipSrv)(nil)
)
type muteshipSrv struct {
api.UnimplementedMuteshipServant
*base.DaoServant
}
func (s *muteshipSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.Jwt()}
}
func (s *muteshipSrv) MuteUser(r *web.MuteUserReq) error {
if r.User == nil {
return xerror.UnauthorizedTokenError
} else if r.User.ID == r.UserId {
return web.ErrNotAllowMuteSelf
}
// 验证reason长度防止过长输入
if len(r.Reason) > 500 {
r.Reason = r.Reason[:500]
}
// 防止重复屏蔽
if s.Ds.IsMuted(r.User.ID, r.UserId) {
return web.ErrAlreadyMuted
}
if err := s.Ds.MuteUser(r.User.ID, r.UserId, r.Reason); err != nil {
logrus.Errorf("Ds.MuteUser err: %s userId: %d targetId: %d", err, r.User.ID, r.UserId)
return web.ErrMuteUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionMute, r.User.ID)
return nil
}
func (s *muteshipSrv) UnmuteUser(r *web.UnmuteUserReq) error {
if r.User == nil {
return xerror.UnauthorizedTokenError
} else if r.User.ID == r.UserId {
return web.ErrNotAllowUnmuteSelf
}
if err := s.Ds.UnmuteUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.UnmuteUser err: %s userId: %d targetId: %d", err, r.User.ID, r.UserId)
return web.ErrUnmuteUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
onMessageActionEvent(_messageActionUnmute, r.User.ID)
return nil
}
func (s *muteshipSrv) ListMutes(r *web.ListMutesReq) (*web.ListMutesResp, error) {
if r.User == nil {
return nil, xerror.UnauthorizedTokenError
}
res, err := s.Ds.ListMutes(r.User.ID, r.PageSize, (r.Page-1)*r.PageSize)
if err != nil {
logrus.Errorf("Ds.ListMutes err: %s", err)
return nil, web.ErrListMutesFailed
}
resp := base.PageRespFrom(res.Mutes, r.Page, r.PageSize, res.Total)
return (*web.ListMutesResp)(resp), nil
}
func newMuteshipSrv(s *base.DaoServant) api.Muteship {
return &muteshipSrv{
DaoServant: s,
}
}

@ -1,4 +1,18 @@
package v1
import (
. "github.com/alimy/mir/v5"
"github.com/rocboss/paopao-ce/internal/model/web"
)
// CheckInActivitity 签到活动
type CheckInActivitity struct {
Schema `mir:"v1,chain"`
// UserCheckIn 用户签到
UserCheckIn func(Post, web.UserCheckInReq) web.UserCheckInResp `mir:"checkin"`
// GetCheckInRank 获取签到排行榜
GetCheckInRank func(Get, web.GetCheckInRankReq) web.GetCheckInRankResp `mir:"checkin/rank"`
}

@ -0,0 +1,21 @@
package v1
import (
. "github.com/alimy/mir/v5"
"github.com/rocboss/paopao-ce/internal/model/web"
)
// Blockship 拉黑模式 服务
type Blockship struct {
Schema `mir:"v1"`
// BlockUser 拉黑用户
BlockUser func(Post, web.BlockUserReq) `mir:"user/block"`
// UnblockUser 取消拉黑用户
UnblockUser func(Post, web.UnblockUserReq) `mir:"user/unblock"`
// ListBlocks 获取用户的拉黑列表
ListBlocks func(Get, web.ListBlocksReq) web.ListBlocksResp `mir:"user/blocks"`
}

@ -57,4 +57,13 @@ type Core struct {
// TweetCollectionStatus 获取动态收藏状态
TweetCollectionStatus func(Get, web.TweetCollectionStatusReq) web.TweetCollectionStatusResp `mir:"post/collection"`
// GetUserArticleCollections 获取用户长文章收藏列表
GetUserArticleCollections func(Get, web.GetUserArticleCollectionsReq) web.GetUserArticleCollectionsResp `mir:"user/articles/collections"`
// GetUserArticleStars 获取用户长文章点赞列表
GetUserArticleStars func(Get, web.GetUserArticleStarsReq) web.GetUserArticleStarsResp `mir:"user/articles/stars"`
// StreamMessages SSE获取消息列表
StreamMessages func(Get, web.StreamMessagesReq) `mir:"user/messages/stream"`
}

@ -27,4 +27,28 @@ type Loose struct {
// TweetDetail 获取动态详情
TweetDetail func(Get, web.TweetDetailReq) web.TweetDetailResp `mir:"post"`
// GetArticle 获取长文章详情
GetArticle func(Get, web.GetArticleReq) web.GetArticleResp `mir:"articles/detail"`
// ListArticles 获取长文章列表
ListArticles func(Get, web.ListArticlesReq) web.ListArticlesResp `mir:"articles"`
// ArticleComments 获取长文章评论
ArticleComments func(Get, web.ArticleCommentsReq) web.ArticleCommentsResp `mir:"articles/comments"`
// GetArticleSeries 获取长文章系列详情
GetArticleSeries func(Get, web.GetArticleSeriesReq) web.GetArticleSeriesResp `mir:"articles/series/detail"`
// ListArticleSeries 获取长文章系列列表
ListArticleSeries func(Get, web.ListArticleSeriesReq) web.ListArticleSeriesResp `mir:"articles/series"`
// GetUserArticles 获取用户长文章列表
GetUserArticles func(Get, web.GetUserArticlesReq) web.GetUserArticlesResp `mir:"user/articles"`
// ArticleStarStatus 获取长文章点赞状态
ArticleStarStatus func(Get, web.ArticleStarStatusReq) web.ArticleStarStatusResp `mir:"articles/star"`
// ArticleCollectionStatus 获取长文章收藏状态
ArticleCollectionStatus func(Get, web.ArticleCollectionStatusReq) web.ArticleCollectionStatusResp `mir:"articles/collection"`
}

@ -0,0 +1,21 @@
package v1
import (
. "github.com/alimy/mir/v5"
"github.com/rocboss/paopao-ce/internal/model/web"
)
// Muteship 屏蔽模式 服务
type Muteship struct {
Schema `mir:"v1"`
// MuteUser 屏蔽用户
MuteUser func(Post, web.MuteUserReq) `mir:"user/mute"`
// UnmuteUser 取消屏蔽用户
UnmuteUser func(Post, web.UnmuteUserReq) `mir:"user/unmute"`
// ListMutes 获取用户的屏蔽列表
ListMutes func(Get, web.ListMutesReq) web.ListMutesResp `mir:"user/mutes"`
}

@ -81,4 +81,44 @@ type Priv struct {
// UnfollowTopic 取消关注话题
UnfollowTopic func(Post, web.UnfollowTopicReq) `mir:"topic/unfollow"`
// 长文章相关API
// CreateArticle 创建长文章
CreateArticle func(Post, Chain, web.CreateArticleReq) web.CreateArticleResp `mir:"articles"`
// UpdateArticle 更新长文章
UpdateArticle func(Put, Chain, web.UpdateArticleReq) `mir:"articles"`
// DeleteArticle 删除长文章
DeleteArticle func(Delete, web.DeleteArticleReq) `mir:"articles"`
// StarArticle 长文章点赞操作
StarArticle func(Post, web.ArticleStarReq) web.ArticleStarResp `mir:"articles/star"`
// CollectionArticle 长文章收藏操作
CollectionArticle func(Post, web.ArticleCollectionReq) web.ArticleCollectionResp `mir:"articles/collection"`
// CreateArticleComment 发布长文章评论
CreateArticleComment func(Post, web.CreateArticleCommentReq) web.CreateArticleCommentResp `mir:"articles/comment"`
// DeleteArticleComment 删除长文章评论
DeleteArticleComment func(Delete, web.DeleteArticleCommentReq) `mir:"articles/comment"`
// CreateArticleCommentReply 发布评论回复
CreateArticleCommentReply func(Post, web.CreateArticleCommentReplyReq) web.CreateArticleCommentReplyResp `mir:"articles/comment/reply"`
// DeleteArticleCommentReply 删除评论回复
DeleteArticleCommentReply func(Delete, web.DeleteArticleCommentReplyReq) `mir:"articles/comment/reply"`
// CreateArticleSeries 创建长文章系列
CreateArticleSeries func(Post, web.CreateArticleSeriesReq) web.CreateArticleSeriesResp `mir:"articles/series"`
// UpdateArticleSeries 更新长文章系列
UpdateArticleSeries func(Put, web.UpdateArticleSeriesReq) `mir:"articles/series"`
// DeleteArticleSeries 删除长文章系列
DeleteArticleSeries func(Delete, web.DeleteArticleSeriesReq) `mir:"articles/series"`
// AdminDeleteArticle 管理员删除长文章
AdminDeleteArticle func(Delete, web.AdminDeleteArticleReq) `mir:"admin/articles"`
}

@ -0,0 +1,12 @@
-- 删除用户签到记录表
DROP TABLE IF EXISTS `p_user_checkin`;
-- 删除运营设置表
DROP TABLE IF EXISTS `p_operation_settings`;
-- 删除用户表的经验值字段
ALTER TABLE `p_user` DROP COLUMN `experience`;

@ -0,0 +1,40 @@
-- 为用户表添加经验值字段
ALTER TABLE `p_user` ADD COLUMN `experience` int(11) NOT NULL DEFAULT 0 COMMENT '用户经验值' AFTER `balance`;
-- 创建运营设置表
CREATE TABLE IF NOT EXISTS `p_operation_settings` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`key` varchar(255) NOT NULL COMMENT '设置键',
`value` text NOT NULL COMMENT '设置值',
`description` varchar(500) DEFAULT NULL COMMENT '设置描述',
`created_on` int(11) NOT NULL DEFAULT 0,
`updated_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_key` (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运营设置表';
-- 创建用户签到记录表
CREATE TABLE IF NOT EXISTS `p_user_checkin` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`checkin_date` date NOT NULL COMMENT '签到日期',
`experience_gained` int(11) NOT NULL DEFAULT 0 COMMENT '获得的经验值',
`created_on` int(11) NOT NULL DEFAULT 0,
`updated_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_date` (`user_id`, `checkin_date`),
KEY `idx_user_id` (`user_id`),
KEY `idx_checkin_date` (`checkin_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户签到记录表';
-- 插入默认运营设置
INSERT INTO `p_operation_settings` (`key`, `value`, `description`, `created_on`, `updated_on`) VALUES
('checkin_enabled', 'true', '签到功能是否启用', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('checkin_exp_range_min', '5', '签到最小经验值', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('checkin_exp_range_max', '15', '签到最大经验值', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('checkin_rank_length', '10', '签到排行榜显示人数', UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
('level_to_exp', '{"lv1":[0,100],"lv2":[101,300],"lv3":[301,600],"lv4":[601,1000],"lv5":[1001,1500]}', '经验-等级对应关系', UNIX_TIMESTAMP(), UNIX_TIMESTAMP());

@ -0,0 +1,23 @@
-- 重新创建用户等级表
CREATE TABLE IF NOT EXISTS `p_user_level` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`level` int(11) NOT NULL COMMENT '等级',
`min_experience` int(11) NOT NULL COMMENT '最小经验值',
`max_experience` int(11) NOT NULL COMMENT '最大经验值',
`created_on` int(11) NOT NULL DEFAULT 0,
`updated_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_level` (`level`),
UNIQUE KEY `idx_min_experience` (`min_experience`),
UNIQUE KEY `idx_max_experience` (`max_experience`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户等级配置表';
-- 插入初始等级数据
INSERT INTO `p_user_level` (`level`, `min_experience`, `max_experience`, `created_on`, `updated_on`) VALUES
(1, 0, 100, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(2, 101, 300, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(3, 301, 600, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(4, 601, 1000, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()),
(5, 1001, 1500, UNIX_TIMESTAMP(), UNIX_TIMESTAMP());

@ -0,0 +1,4 @@
-- 删除用户等级表
DROP TABLE IF EXISTS `p_user_level`;

@ -0,0 +1,10 @@
-- 删除长文章相关表
DROP TABLE IF EXISTS `p_article_comment_replies`;
DROP TABLE IF EXISTS `p_article_comments`;
DROP TABLE IF EXISTS `p_article_stars`;
DROP TABLE IF EXISTS `p_article_collections`;
DROP TABLE IF EXISTS `p_article_series_items`;
DROP TABLE IF EXISTS `p_article_series`;
DROP TABLE IF EXISTS `p_article_contents`;
DROP TABLE IF EXISTS `p_articles`;

@ -0,0 +1,153 @@
-- 创建长文章相关表
-- 长文章主表
CREATE TABLE IF NOT EXISTS `p_articles` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`series_id` bigint(20) DEFAULT NULL COMMENT '系列ID如果属于某个系列',
`title` varchar(200) NOT NULL COMMENT '文章标题',
`summary` text COMMENT '文章摘要',
`word_count` int(11) NOT NULL DEFAULT 0 COMMENT '字数统计',
`visibility` tinyint(4) NOT NULL DEFAULT 0 COMMENT '可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开',
`price` bigint(20) DEFAULT 0 COMMENT '付费价格(分)',
`is_top` int(11) NOT NULL DEFAULT 0 COMMENT '是否置顶',
`is_essence` int(11) NOT NULL DEFAULT 0 COMMENT '是否精华',
`is_lock` int(11) NOT NULL DEFAULT 0 COMMENT '是否锁定',
`comment_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '评论数',
`collection_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '收藏数',
`share_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '分享数',
`upvote_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '点赞数',
`view_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '阅读数',
`latest_replied_on` int(11) NOT NULL DEFAULT 0 COMMENT '最后回复时间',
`tags` varchar(500) DEFAULT '' COMMENT '标签,逗号分隔',
`ip` varchar(64) DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) DEFAULT '' COMMENT 'IP地理位置',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_series_id` (`series_id`),
KEY `idx_visibility` (`visibility`),
KEY `idx_is_top` (`is_top`),
KEY `idx_is_essence` (`is_essence`),
KEY `idx_created_on` (`created_on`),
KEY `idx_modified_on` (`modified_on`),
KEY `idx_tags` (`tags`(191))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章表';
-- 长文章内容表
CREATE TABLE IF NOT EXISTS `p_article_contents` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`content` longtext NOT NULL COMMENT '内容',
`content_type` int(11) NOT NULL DEFAULT 1 COMMENT '内容类型: 1标题 2文字段落 3图片地址 4视频地址 5语音地址 6链接地址 7附件资源 8收费附件',
`sort` bigint(20) NOT NULL DEFAULT 0 COMMENT '排序',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_article_id` (`article_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_content_type` (`content_type`),
KEY `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章内容表';
-- 长文章系列表
CREATE TABLE IF NOT EXISTS `p_article_series` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`title` varchar(200) NOT NULL COMMENT '系列标题',
`description` text COMMENT '系列描述',
`cover` varchar(500) DEFAULT '' COMMENT '封面图片',
`article_count` int(11) NOT NULL DEFAULT 0 COMMENT '文章数量',
`total_word_count` int(11) NOT NULL DEFAULT 0 COMMENT '总字数',
`is_completed` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否完结',
`visibility` tinyint(4) NOT NULL DEFAULT 0 COMMENT '可见性',
`tags` varchar(500) DEFAULT '' COMMENT '标签,逗号分隔',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_visibility` (`visibility`),
KEY `idx_is_completed` (`is_completed`),
KEY `idx_created_on` (`created_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章系列表';
-- 系列文章关联表
CREATE TABLE IF NOT EXISTS `p_article_series_items` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`series_id` bigint(20) NOT NULL COMMENT '系列ID',
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序',
`created_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_series_article` (`series_id`, `article_id`),
KEY `idx_series_id` (`series_id`),
KEY `idx_article_id` (`article_id`),
KEY `idx_sort` (`sort`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系列文章关联表';
-- 长文章收藏表
CREATE TABLE IF NOT EXISTS `p_article_collections` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`created_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_article` (`user_id`, `article_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_article_id` (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章收藏表';
-- 长文章点赞表
CREATE TABLE IF NOT EXISTS `p_article_stars` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`created_on` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_article` (`user_id`, `article_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_article_id` (`article_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章点赞表';
-- 长文章评论表
CREATE TABLE IF NOT EXISTS `p_article_comments` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`content` text NOT NULL COMMENT '评论内容',
`ip` varchar(64) DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) DEFAULT '' COMMENT 'IP地理位置',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_article_id` (`article_id`),
KEY `idx_created_on` (`created_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章评论表';
-- 长文章评论回复表
CREATE TABLE IF NOT EXISTS `p_article_comment_replies` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`article_id` bigint(20) NOT NULL COMMENT '文章ID',
`comment_id` bigint(20) NOT NULL COMMENT '评论ID',
`reply_to_id` bigint(20) DEFAULT NULL COMMENT '回复目标ID',
`content` text NOT NULL COMMENT '回复内容',
`ip` varchar(64) DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) DEFAULT '' COMMENT 'IP地理位置',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_article_id` (`article_id`),
KEY `idx_comment_id` (`comment_id`),
KEY `idx_reply_to_id` (`reply_to_id`),
KEY `idx_created_on` (`created_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长文章评论回复表';

@ -0,0 +1,4 @@
-- 撤销用户拉黑和屏蔽功能相关表
DROP TABLE IF EXISTS `p_user_mutes`;
DROP TABLE IF EXISTS `p_user_blocks`;

@ -0,0 +1,33 @@
-- 添加用户拉黑和屏蔽功能相关表
-- 用户拉黑表
CREATE TABLE IF NOT EXISTS `p_user_blocks` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '发起拉黑的用户ID',
`target_user_id` bigint(20) NOT NULL COMMENT '被拉黑的用户ID',
`reason` varchar(500) DEFAULT '' COMMENT '拉黑原因',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_target` (`user_id`, `target_user_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_target_user_id` (`target_user_id`),
KEY `idx_created_on` (`created_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户拉黑表';
-- 用户屏蔽表
CREATE TABLE IF NOT EXISTS `p_user_mutes` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '发起屏蔽的用户ID',
`target_user_id` bigint(20) NOT NULL COMMENT '被屏蔽的用户ID',
`reason` varchar(500) DEFAULT '' COMMENT '屏蔽原因',
`created_on` int(11) NOT NULL DEFAULT 0,
`modified_on` int(11) NOT NULL DEFAULT 0,
`is_del` int(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_target` (`user_id`, `target_user_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_target_user_id` (`target_user_id`),
KEY `idx_created_on` (`created_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户屏蔽表';

@ -0,0 +1,12 @@
-- 删除用户签到记录表
DROP TABLE IF EXISTS "p_user_checkin";
-- 删除运营设置表
DROP TABLE IF EXISTS "p_operation_settings";
-- 删除用户表的经验值字段
ALTER TABLE "p_user" DROP COLUMN "experience";

@ -0,0 +1,39 @@
-- 为用户表添加经验值字段
ALTER TABLE "p_user" ADD COLUMN "experience" integer NOT NULL DEFAULT 0;
-- 创建运营设置表
CREATE TABLE IF NOT EXISTS "p_operation_settings" (
"id" bigserial PRIMARY KEY,
"key" varchar(255) NOT NULL UNIQUE,
"value" text NOT NULL,
"description" varchar(500),
"created_on" integer NOT NULL DEFAULT 0,
"updated_on" integer NOT NULL DEFAULT 0
);
-- 创建用户签到记录表
CREATE TABLE IF NOT EXISTS "p_user_checkin" (
"id" bigserial PRIMARY KEY,
"user_id" bigint NOT NULL,
"checkin_date" date NOT NULL,
"experience_gained" integer NOT NULL DEFAULT 0,
"created_on" integer NOT NULL DEFAULT 0,
"updated_on" integer NOT NULL DEFAULT 0,
UNIQUE ("user_id", "checkin_date")
);
-- 创建索引
CREATE INDEX IF NOT EXISTS "idx_user_checkin_user_id" ON "p_user_checkin" ("user_id");
CREATE INDEX IF NOT EXISTS "idx_user_checkin_checkin_date" ON "p_user_checkin" ("checkin_date");
-- 插入默认运营设置
INSERT INTO "p_operation_settings" ("key", "value", "description", "created_on", "updated_on") VALUES
('checkin_enabled', 'true', '签到功能是否启用', extract(epoch from now())::integer, extract(epoch from now())::integer),
('checkin_exp_range_min', '5', '签到最小经验值', extract(epoch from now())::integer, extract(epoch from now())::integer),
('checkin_exp_range_max', '15', '签到最大经验值', extract(epoch from now())::integer, extract(epoch from now())::integer),
('checkin_rank_length', '10', '签到排行榜显示人数', extract(epoch from now())::integer, extract(epoch from now())::integer),
('level_to_exp', '{"lv1":[0,100],"lv2":[101,300],"lv3":[301,600],"lv4":[601,1000],"lv5":[1001,1500]}', '经验-等级对应关系', extract(epoch from now())::integer, extract(epoch from now())::integer);

@ -0,0 +1,24 @@
-- 重新创建用户等级表
CREATE TABLE IF NOT EXISTS "p_user_level" (
"id" bigserial PRIMARY KEY,
"level" integer NOT NULL,
"min_experience" integer NOT NULL,
"max_experience" integer NOT NULL,
"created_on" integer NOT NULL DEFAULT 0,
"updated_on" integer NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_level_level" ON "p_user_level" ("level");
CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_level_min_experience" ON "p_user_level" ("min_experience");
CREATE UNIQUE INDEX IF NOT EXISTS "idx_user_level_max_experience" ON "p_user_level" ("max_experience");
-- 插入初始等级数据
INSERT INTO "p_user_level" ("level", "min_experience", "max_experience", "created_on", "updated_on") VALUES
(1, 0, 100, extract(epoch from now())::integer, extract(epoch from now())::integer),
(2, 101, 300, extract(epoch from now())::integer, extract(epoch from now())::integer),
(3, 301, 600, extract(epoch from now())::integer, extract(epoch from now())::integer),
(4, 601, 1000, extract(epoch from now())::integer, extract(epoch from now())::integer),
(5, 1001, 1500, extract(epoch from now())::integer, extract(epoch from now())::integer);

@ -0,0 +1,4 @@
-- 删除用户等级表
DROP TABLE IF EXISTS "p_user_level";

@ -0,0 +1,4 @@
-- 撤销用户拉黑和屏蔽功能相关表
DROP TABLE IF EXISTS "p_user_mutes";
DROP TABLE IF EXISTS "p_user_blocks";

@ -0,0 +1,47 @@
-- 添加用户拉黑和屏蔽功能相关表
-- 用户拉黑表
CREATE TABLE IF NOT EXISTS "p_user_blocks" (
"id" bigserial NOT NULL PRIMARY KEY,
"user_id" bigint NOT NULL,
"target_user_id" bigint NOT NULL,
"reason" varchar(500) DEFAULT '',
"created_on" integer NOT NULL DEFAULT 0,
"modified_on" integer NOT NULL DEFAULT 0,
"is_del" integer NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS "uk_user_blocks_user_target" ON "p_user_blocks" ("user_id", "target_user_id");
CREATE INDEX IF NOT EXISTS "idx_user_blocks_user_id" ON "p_user_blocks" ("user_id");
CREATE INDEX IF NOT EXISTS "idx_user_blocks_target_user_id" ON "p_user_blocks" ("target_user_id");
CREATE INDEX IF NOT EXISTS "idx_user_blocks_created_on" ON "p_user_blocks" ("created_on");
-- 添加注释
COMMENT ON TABLE "p_user_blocks" IS '用户拉黑表';
COMMENT ON COLUMN "p_user_blocks"."user_id" IS '发起拉黑的用户ID';
COMMENT ON COLUMN "p_user_blocks"."target_user_id" IS '被拉黑的用户ID';
COMMENT ON COLUMN "p_user_blocks"."reason" IS '拉黑原因';
-- 用户屏蔽表
CREATE TABLE IF NOT EXISTS "p_user_mutes" (
"id" bigserial NOT NULL PRIMARY KEY,
"user_id" bigint NOT NULL,
"target_user_id" bigint NOT NULL,
"reason" varchar(500) DEFAULT '',
"created_on" integer NOT NULL DEFAULT 0,
"modified_on" integer NOT NULL DEFAULT 0,
"is_del" integer NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS "uk_user_mutes_user_target" ON "p_user_mutes" ("user_id", "target_user_id");
CREATE INDEX IF NOT EXISTS "idx_user_mutes_user_id" ON "p_user_mutes" ("user_id");
CREATE INDEX IF NOT EXISTS "idx_user_mutes_target_user_id" ON "p_user_mutes" ("target_user_id");
CREATE INDEX IF NOT EXISTS "idx_user_mutes_created_on" ON "p_user_mutes" ("created_on");
-- 添加注释
COMMENT ON TABLE "p_user_mutes" IS '用户屏蔽表';
COMMENT ON COLUMN "p_user_mutes"."user_id" IS '发起屏蔽的用户ID';
COMMENT ON COLUMN "p_user_mutes"."target_user_id" IS '被屏蔽的用户ID';
COMMENT ON COLUMN "p_user_mutes"."reason" IS '屏蔽原因';

@ -0,0 +1,11 @@
-- 删除用户签到记录表
DROP TABLE IF EXISTS p_user_checkin;
-- 删除运营设置表
DROP TABLE IF EXISTS p_operation_settings;
-- 删除用户表的经验值字段 (SQLite不支持直接删除列这里只是为了保持一致性)

@ -0,0 +1,39 @@
-- 为用户表添加经验值字段
ALTER TABLE p_user ADD COLUMN experience INTEGER NOT NULL DEFAULT 0;
-- 创建运营设置表
CREATE TABLE IF NOT EXISTS p_operation_settings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL UNIQUE,
value TEXT NOT NULL,
description TEXT,
created_on INTEGER NOT NULL DEFAULT 0,
updated_on INTEGER NOT NULL DEFAULT 0
);
-- 创建用户签到记录表
CREATE TABLE IF NOT EXISTS p_user_checkin (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
checkin_date TEXT NOT NULL,
experience_gained INTEGER NOT NULL DEFAULT 0,
created_on INTEGER NOT NULL DEFAULT 0,
updated_on INTEGER NOT NULL DEFAULT 0,
UNIQUE(user_id, checkin_date)
);
-- 创建索引
CREATE INDEX IF NOT EXISTS idx_user_checkin_user_id ON p_user_checkin(user_id);
CREATE INDEX IF NOT EXISTS idx_user_checkin_checkin_date ON p_user_checkin(checkin_date);
-- 插入默认运营设置
INSERT INTO p_operation_settings (key, value, description, created_on, updated_on) VALUES
('checkin_enabled', 'true', '签到功能是否启用', strftime('%s', 'now'), strftime('%s', 'now')),
('checkin_exp_range_min', '5', '签到最小经验值', strftime('%s', 'now'), strftime('%s', 'now')),
('checkin_exp_range_max', '15', '签到最大经验值', strftime('%s', 'now'), strftime('%s', 'now')),
('checkin_rank_length', '10', '签到排行榜显示人数', strftime('%s', 'now'), strftime('%s', 'now')),
('level_to_exp', '{"lv1":[0,100],"lv2":[101,300],"lv3":[301,600],"lv4":[601,1000],"lv5":[1001,1500]}', '经验-等级对应关系', strftime('%s', 'now'), strftime('%s', 'now'));

@ -0,0 +1,24 @@
-- 重新创建用户等级表
CREATE TABLE IF NOT EXISTS p_user_level (
id INTEGER PRIMARY KEY AUTOINCREMENT,
level INTEGER NOT NULL,
min_experience INTEGER NOT NULL,
max_experience INTEGER NOT NULL,
created_on INTEGER NOT NULL DEFAULT 0,
updated_on INTEGER NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_level_level ON p_user_level(level);
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_level_min_experience ON p_user_level(min_experience);
CREATE UNIQUE INDEX IF NOT EXISTS idx_user_level_max_experience ON p_user_level(max_experience);
-- 插入初始等级数据
INSERT INTO p_user_level (level, min_experience, max_experience, created_on, updated_on) VALUES
(1, 0, 100, strftime('%s', 'now'), strftime('%s', 'now')),
(2, 101, 300, strftime('%s', 'now'), strftime('%s', 'now')),
(3, 301, 600, strftime('%s', 'now'), strftime('%s', 'now')),
(4, 601, 1000, strftime('%s', 'now'), strftime('%s', 'now')),
(5, 1001, 1500, strftime('%s', 'now'), strftime('%s', 'now'));

@ -0,0 +1,4 @@
-- 删除用户等级表
DROP TABLE IF EXISTS p_user_level;

@ -0,0 +1,4 @@
-- 撤销用户拉黑和屏蔽功能相关表
DROP TABLE IF EXISTS `p_user_mutes`;
DROP TABLE IF EXISTS `p_user_blocks`;

@ -0,0 +1,35 @@
-- 添加用户拉黑和屏蔽功能相关表
-- 用户拉黑表
CREATE TABLE IF NOT EXISTS `p_user_blocks` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`user_id` INTEGER NOT NULL,
`target_user_id` INTEGER NOT NULL,
`reason` TEXT DEFAULT '',
`created_on` INTEGER NOT NULL DEFAULT 0,
`modified_on` INTEGER NOT NULL DEFAULT 0,
`is_del` INTEGER NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS `uk_user_blocks_user_target` ON `p_user_blocks` (`user_id`, `target_user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_blocks_user_id` ON `p_user_blocks` (`user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_blocks_target_user_id` ON `p_user_blocks` (`target_user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_blocks_created_on` ON `p_user_blocks` (`created_on`);
-- 用户屏蔽表
CREATE TABLE IF NOT EXISTS `p_user_mutes` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`user_id` INTEGER NOT NULL,
`target_user_id` INTEGER NOT NULL,
`reason` TEXT DEFAULT '',
`created_on` INTEGER NOT NULL DEFAULT 0,
`modified_on` INTEGER NOT NULL DEFAULT 0,
`is_del` INTEGER NOT NULL DEFAULT 0
);
-- 创建索引
CREATE UNIQUE INDEX IF NOT EXISTS `uk_user_mutes_user_target` ON `p_user_mutes` (`user_id`, `target_user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_mutes_user_id` ON `p_user_mutes` (`user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_mutes_target_user_id` ON `p_user_mutes` (`target_user_id`);
CREATE INDEX IF NOT EXISTS `idx_user_mutes_created_on` ON `p_user_mutes` (`created_on`);
Loading…
Cancel
Save