mir: partial api implement for new web service

pull/196/head
Michael Li 2 years ago
parent dee07b6184
commit 2d2c245ca1
No known key found for this signature in database

@ -8,6 +8,7 @@ import (
"github.com/alimy/mir/v3"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type Core interface {
@ -25,18 +26,22 @@ type Core interface {
GetStars() mir.Error
GetCollections() mir.Error
SendUserWhisper() mir.Error
ReadMessage() mir.Error
GetMessages() mir.Error
GetUnreadMsgCount() mir.Error
ReadMessage(*web.ReadMessageReq) mir.Error
GetMessages(*web.GetMessagesReq) (*base.PageResp, mir.Error)
GetUnreadMsgCount(*web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error)
GetUserInfo(*web.UserInfoReq) (*web.UserInfoResp, mir.Error)
SyncSearchIndex() mir.Error
SyncSearchIndex(*web.SyncSearchIndexReq) mir.Error
mustEmbedUnimplementedCoreServant()
}
type CoreBinding interface {
BindChangeAvatar(*gin.Context) (*web.ChangeAvatarReq, mir.Error)
BindReadMessage(*gin.Context) (*web.ReadMessageReq, mir.Error)
BindGetMessages(*gin.Context) (*web.GetMessagesReq, mir.Error)
BindGetUnreadMsgCount(*gin.Context) (*web.GetUnreadMsgCountReq, mir.Error)
BindGetUserInfo(*gin.Context) (*web.UserInfoReq, mir.Error)
BindSyncSearchIndex(*gin.Context) (*web.SyncSearchIndexReq, mir.Error)
mustEmbedUnimplementedCoreBinding()
}
@ -54,8 +59,8 @@ type CoreRender interface {
RenderGetCollections(*gin.Context, mir.Error)
RenderSendUserWhisper(*gin.Context, mir.Error)
RenderReadMessage(*gin.Context, mir.Error)
RenderGetMessages(*gin.Context, mir.Error)
RenderGetUnreadMsgCount(*gin.Context, mir.Error)
RenderGetMessages(*gin.Context, *base.PageResp, mir.Error)
RenderGetUnreadMsgCount(*gin.Context, *web.GetUnreadMsgCountResp, mir.Error)
RenderGetUserInfo(*gin.Context, *web.UserInfoResp, mir.Error)
RenderSyncSearchIndex(*gin.Context, mir.Error)
@ -192,7 +197,12 @@ func RegisterCoreServant(e *gin.Engine, s Core, b CoreBinding, r CoreRender) {
default:
}
r.RenderReadMessage(c, s.ReadMessage())
req, err := b.BindReadMessage(c)
if err != nil {
r.RenderReadMessage(c, err)
return
}
r.RenderReadMessage(c, s.ReadMessage(req))
})
router.Handle("GET", "/user/messages", func(c *gin.Context) {
@ -202,7 +212,13 @@ func RegisterCoreServant(e *gin.Engine, s Core, b CoreBinding, r CoreRender) {
default:
}
r.RenderGetMessages(c, s.GetMessages())
req, err := b.BindGetMessages(c)
if err != nil {
r.RenderGetMessages(c, nil, err)
return
}
resp, err := s.GetMessages(req)
r.RenderGetMessages(c, resp, err)
})
router.Handle("GET", "/user/msgcount/unread", func(c *gin.Context) {
@ -212,7 +228,13 @@ func RegisterCoreServant(e *gin.Engine, s Core, b CoreBinding, r CoreRender) {
default:
}
r.RenderGetUnreadMsgCount(c, s.GetUnreadMsgCount())
req, err := b.BindGetUnreadMsgCount(c)
if err != nil {
r.RenderGetUnreadMsgCount(c, nil, err)
return
}
resp, err := s.GetUnreadMsgCount(req)
r.RenderGetUnreadMsgCount(c, resp, err)
})
router.Handle("GET", "/user/info", func(c *gin.Context) {
@ -238,7 +260,12 @@ func RegisterCoreServant(e *gin.Engine, s Core, b CoreBinding, r CoreRender) {
default:
}
r.RenderSyncSearchIndex(c, s.SyncSearchIndex())
req, err := b.BindSyncSearchIndex(c)
if err != nil {
r.RenderSyncSearchIndex(c, err)
return
}
r.RenderSyncSearchIndex(c, s.SyncSearchIndex(req))
})
}
@ -295,23 +322,23 @@ func (UnimplementedCoreServant) SendUserWhisper() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) ReadMessage() mir.Error {
func (UnimplementedCoreServant) ReadMessage(req *web.ReadMessageReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) GetMessages() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedCoreServant) GetMessages(req *web.GetMessagesReq) (*base.PageResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) GetUnreadMsgCount() mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
func (UnimplementedCoreServant) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedCoreServant) SyncSearchIndex() mir.Error {
func (UnimplementedCoreServant) SyncSearchIndex(req *web.SyncSearchIndexReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
@ -370,12 +397,12 @@ func (r *UnimplementedCoreRender) RenderReadMessage(c *gin.Context, err mir.Erro
r.RenderAny(c, nil, err)
}
func (r *UnimplementedCoreRender) RenderGetMessages(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedCoreRender) RenderGetMessages(c *gin.Context, data *base.PageResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedCoreRender) RenderGetUnreadMsgCount(c *gin.Context, err mir.Error) {
r.RenderAny(c, nil, err)
func (r *UnimplementedCoreRender) RenderGetUnreadMsgCount(c *gin.Context, data *web.GetUnreadMsgCountResp, err mir.Error) {
r.RenderAny(c, data, err)
}
func (r *UnimplementedCoreRender) RenderGetUserInfo(c *gin.Context, data *web.UserInfoResp, err mir.Error) {
@ -399,10 +426,34 @@ func (b *UnimplementedCoreBinding) BindChangeAvatar(c *gin.Context) (*web.Change
return obj, err
}
func (b *UnimplementedCoreBinding) BindReadMessage(c *gin.Context) (*web.ReadMessageReq, mir.Error) {
obj := new(web.ReadMessageReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedCoreBinding) BindGetMessages(c *gin.Context) (*web.GetMessagesReq, mir.Error) {
obj := new(web.GetMessagesReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedCoreBinding) BindGetUnreadMsgCount(c *gin.Context) (*web.GetUnreadMsgCountReq, mir.Error) {
obj := new(web.GetUnreadMsgCountReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedCoreBinding) BindGetUserInfo(c *gin.Context) (*web.UserInfoReq, mir.Error) {
obj := new(web.UserInfoReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedCoreBinding) BindSyncSearchIndex(c *gin.Context) (*web.SyncSearchIndexReq, mir.Error) {
obj := new(web.SyncSearchIndexReq)
err := b.BindAny(c, obj)
return obj, err
}
func (b *UnimplementedCoreBinding) mustEmbedUnimplementedCoreBinding() {}

@ -6,7 +6,7 @@ require (
github.com/Masterminds/semver/v3 v3.1.1
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868
github.com/alimy/cfg v0.3.0
github.com/alimy/mir/v3 v3.0.0-beta.3
github.com/alimy/mir/v3 v3.0.0-beta.4
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible
github.com/allegro/bigcache/v3 v3.0.2
github.com/bytedance/sonic v1.5.0

@ -147,8 +147,8 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:C
github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk=
github.com/alimy/cfg v0.3.0 h1:9xgA0QWVCPSq9fFNRcYahVCAX22IL9ts2wrTQPfAStY=
github.com/alimy/cfg v0.3.0/go.mod h1:rOxbasTH2srl6StAjNF5Vyi8bfrdkl3fLGmOYtSw81c=
github.com/alimy/mir/v3 v3.0.0-beta.3 h1:b+2C9rvZg2ssZ0+gMUf9aYwhuJFKExO611ybXRP+/30=
github.com/alimy/mir/v3 v3.0.0-beta.3/go.mod h1:ybhT2ijOiDn0lLwWzIY6vXdv+uzZrctS7VFfczcXBWU=
github.com/alimy/mir/v3 v3.0.0-beta.4 h1:4++lea7OWmykHRVbF1nCnKOJXCvBifmnhe47e91aV6c=
github.com/alimy/mir/v3 v3.0.0-beta.4/go.mod h1:ybhT2ijOiDn0lLwWzIY6vXdv+uzZrctS7VFfczcXBWU=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=

@ -4,14 +4,56 @@
package web
import (
"context"
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type ChangeAvatarReq struct {
*BaseInfo `json:"-"`
*BaseInfo `json:"-" binding:"-"`
}
type SyncSearchIndexReq struct {
*BaseInfo `json:"-" binding:"-"`
Ctx context.Context `json:"-" binding:"-"`
}
type UserInfoReq struct {
*BaseInfo `json:"-"`
*BaseInfo `json:"-" binding:"-"`
Username string `json:"username" form:"username" binding:"required"`
}
type UserInfoResp struct {
// TODO
Id int64 `json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Phone string `json:"phone"`
IsAdmin bool `json:"is_admin"`
}
type GetUnreadMsgCountReq struct {
*SimpleInfo `json:"-" binding:"-"`
}
type GetUnreadMsgCountResp struct {
Count int64 `json:"count"`
}
type GetMessagesReq struct {
UserId int64
Page int
PageSize int
}
type GetMessagesResp = base.PageResp
type ReadMessageReq struct {
*SimpleInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}

@ -12,6 +12,14 @@ type BaseInfo struct {
User *core.User
}
type SimpleInfo struct {
Uid int64
}
func (b *BaseInfo) SetUser(user *core.User) {
b.User = user
}
func (s *SimpleInfo) SetUserId(id int64) {
s.Uid = id
}

@ -41,6 +41,10 @@ type UserSetter interface {
SetUser(*core.User)
}
type UserIdSetter interface {
SetUserId(int64)
}
func UserFrom(c *gin.Context) (*core.User, bool) {
if u, exists := c.Get("USER"); exists {
user, ok := u.(*core.User)
@ -50,13 +54,21 @@ func UserFrom(c *gin.Context) (*core.User, bool) {
}
func UserIdFrom(c *gin.Context) (int64, bool) {
if u, exists := c.Get("UID"); exists {
uid, ok := u.(int64)
return uid, ok
if uid, exists := c.Get("UID"); exists {
v, ok := uid.(int64)
return v, ok
}
return -1, false
}
func UserNameFrom(c *gin.Context) (string, bool) {
if username, exists := c.Get("USERNAME"); exists {
v, ok := username.(string)
return v, ok
}
return "", false
}
func BindAny(c *gin.Context, obj any) mir.Error {
var errs xerror.ValidErrors
err := c.ShouldBind(obj)
@ -65,12 +77,14 @@ func BindAny(c *gin.Context, obj any) mir.Error {
}
// setup *core.User if needed
if setter, ok := obj.(UserSetter); ok {
user, exist := UserFrom(c)
if !exist {
return xerror.UnauthorizedTokenError
}
user, _ := UserFrom(c)
setter.SetUser(user)
}
// setup UserId if needed
if setter, ok := obj.(UserIdSetter); ok {
uid, _ := UserIdFrom(c)
setter.SetUserId(uid)
}
return nil
}
@ -88,3 +102,29 @@ func RenderAny(c *gin.Context, data any, err mir.Error) {
})
}
}
func (s *DaoServant) GetTweetBy(id int64) (*core.PostFormated, error) {
post, err := s.Ds.GetPostByID(id)
if err != nil {
return nil, err
}
postContents, err := s.Ds.GetPostContentsByIDs([]int64{post.ID})
if err != nil {
return nil, err
}
users, err := s.Ds.GetUsersByIDs([]int64{post.UserID})
if err != nil {
return nil, err
}
// 数据整合
postFormated := post.Format()
for _, user := range users {
postFormated.User = user.Format()
}
for _, content := range postContents {
if content.PostID == post.ID {
postFormated.Contents = append(postFormated.Contents, content.Format())
}
}
return postFormated, nil
}

@ -136,7 +136,7 @@ func (s *alipayPrivSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
func (s *alipayPrivSrv) UserWalletBills(req *web.UserWalletBillsReq) (*web.UserWalletBillsResp, mir.Error) {
func (s *alipayPrivSrv) UserWalletBills(req *web.UserWalletBillsReq) (*base.PageResp, mir.Error) {
bills, err := s.Ds.GetUserWalletBills(req.UserId, (req.Page-1)*req.PageSize, req.PageSize)
if err != nil {
logrus.Errorf("GetUserWalletBills err: %s", err)

@ -5,11 +5,21 @@
package web
import (
"context"
"fmt"
"math"
"time"
"github.com/alimy/mir/v3"
"github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/xerror"
"github.com/sirupsen/logrus"
)
var (
@ -34,10 +44,187 @@ type coreRender struct {
*api.UnimplementedCoreRender
}
func (b *coreBinding) BindSyncSearchIndex(c *gin.Context) (*web.SyncSearchIndexReq, mir.Error) {
user, _ := base.UserFrom(c)
return &web.SyncSearchIndexReq{
BaseInfo: &web.BaseInfo{
User: user,
},
Ctx: c.Request.Context(),
}, nil
}
func (b *coreBinding) BindGetUserInfo(c *gin.Context) (*web.UserInfoReq, mir.Error) {
username, exists := base.UserNameFrom(c)
if !exists {
return nil, xerror.UnauthorizedAuthNotExist
}
return &web.UserInfoReq{
Username: username,
}, nil
}
func (b *coreBinding) BindGetMessages(c *gin.Context) (*web.GetMessagesReq, mir.Error) {
uid, ok := base.UserIdFrom(c)
if !ok {
return nil, xerror.UnauthorizedTokenError
}
page, pageSize := app.GetPageOffset(c)
return &web.GetMessagesReq{
UserId: uid,
Page: page,
PageSize: pageSize,
}, nil
}
func (s *coreSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
func (s *coreSrv) SyncSearchIndex(req *web.SyncSearchIndexReq) mir.Error {
if req.User != nil && req.User.IsAdmin {
go s.pushPostsToSearch(req.Ctx)
}
return nil
}
func (s *coreSrv) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Error) {
user, err := s.Ds.GetUserByUsername(req.Username)
if err != nil {
return nil, xerror.UnauthorizedAuthNotExist
}
if user.Model == nil || user.ID < 0 {
return nil, xerror.UnauthorizedAuthNotExist
}
resp := &web.UserInfoResp{
Id: user.ID,
Nickname: user.Nickname,
Username: user.Username,
Status: user.Status,
Avatar: user.Avatar,
Balance: user.Balance,
IsAdmin: user.IsAdmin,
}
if user.Phone != "" && len(user.Phone) == 11 {
resp.Phone = user.Phone[0:3] + "****" + user.Phone[7:]
}
return resp, nil
}
func (s *coreSrv) GetUnreadMsgCount(req *web.GetUnreadMsgCountReq) (*web.GetUnreadMsgCountResp, mir.Error) {
count, err := s.Ds.GetUnreadCount(req.Uid)
if err != nil {
return nil, xerror.ServerError
}
return &web.GetUnreadMsgCountResp{
Count: count,
}, nil
}
func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*base.PageResp, mir.Error) {
conditions := &core.ConditionsT{
"receiver_user_id": req.UserId,
"ORDER": "id DESC",
}
messages, err := s.Ds.GetMessages(conditions, (req.Page-1)*req.PageSize, req.PageSize)
for _, mf := range messages {
if mf.SenderUserID > 0 {
user, err := s.Ds.GetUserByID(mf.SenderUserID)
if err == nil {
mf.SenderUser = user.Format()
}
}
// 好友申请消息不需要获取其他信息
if mf.Type == core.MsgTypeRequestingFriend {
continue
}
if mf.PostID > 0 {
post, err := s.GetTweetBy(mf.PostID)
if err == nil {
mf.Post = post
if mf.CommentID > 0 {
comment, err := s.Ds.GetCommentByID(mf.CommentID)
if err == nil {
mf.Comment = comment
if mf.ReplyID > 0 {
reply, err := s.Ds.GetCommentReplyByID(mf.ReplyID)
if err == nil {
mf.Reply = reply
}
}
}
}
}
}
}
if err != nil {
logrus.Errorf("Ds.GetMessages err: %v\n", err)
return nil, _errGetMessagesFailed
}
totalRows, _ := s.Ds.GetMessageCount(conditions)
return base.PageRespFrom(messages, req.Page, req.PageSize, totalRows), nil
}
func (s *coreSrv) ReadMessage(req *web.ReadMessageReq) mir.Error {
message, err := s.Ds.GetMessageByID(req.ID)
if err != nil {
return _errReadMessageFailed
}
if message.ReceiverUserID != req.Uid {
return _errNoPermission
}
if err = s.Ds.ReadMessage(message); err != nil {
logrus.Errorf("Ds.ReadMessage err: %s", err)
return _errReadMessageFailed
}
return nil
}
func (s *coreSrv) pushPostsToSearch(c context.Context) {
if ok, _ := s.Redis.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok {
defer s.Redis.Del(c, "JOB_PUSH_TO_SEARCH")
splitNum := 1000
totalRows, _ := s.Ds.GetPostCount(&core.ConditionsT{
"visibility IN ?": []core.PostVisibleT{core.PostVisitPublic, core.PostVisitFriend},
})
pages := math.Ceil(float64(totalRows) / float64(splitNum))
nums := int(pages)
for i := 0; i < nums; i++ {
posts, postsFormated, err := s.getTweetList(&core.ConditionsT{}, i*splitNum, splitNum)
if err != nil || len(posts) != len(postsFormated) {
continue
}
for i, pf := range postsFormated {
contentFormated := ""
for _, content := range pf.Contents {
if content.Type == core.ContentTypeText || content.Type == core.ContentTypeTitle {
contentFormated = contentFormated + content.Content + "\n"
}
}
docs := []core.TsDocItem{{
Post: posts[i],
Content: contentFormated,
}}
s.ts.AddDocuments(docs, fmt.Sprintf("%d", posts[i].ID))
}
}
}
}
func (s *coreSrv) deleteSearchPost(post *core.Post) error {
return s.ts.DeleteDocuments([]string{fmt.Sprintf("%d", post.ID)})
}
func (s *coreSrv) getTweetList(conditions *core.ConditionsT, offset, limit int) ([]*core.Post, []*core.PostFormated, error) {
posts, err := s.Ds.GetPosts(conditions, offset, limit)
if err != nil {
return nil, nil, err
}
postFormated, err := s.Ds.MergePosts(posts)
return posts, postFormated, err
}
func newCoreSrv(s *base.DaoServant, ts core.TweetSearchService, oss core.ObjectStorageService) api.Core {
return &coreSrv{
DaoServant: s,

@ -16,19 +16,19 @@ type Core struct {
Group Group `mir:"v1"`
// SyncSearchIndex 同步索引
SyncSearchIndex func(Get) `mir:"/sync/index"`
SyncSearchIndex func(Get, web.SyncSearchIndexReq) `mir:"/sync/index"`
// GetUserInfo 获取当前用户信息
GetUserInfo func(Get, web.UserInfoReq) web.UserInfoResp `mir:"/user/info"`
// GetUnreadMsgCount 获取当前用户未读消息数量
GetUnreadMsgCount func(Get) `mir:"/user/msgcount/unread"`
GetUnreadMsgCount func(Get, web.GetUnreadMsgCountReq) web.GetUnreadMsgCountResp `mir:"/user/msgcount/unread"`
// GetMessages 获取消息列表
GetMessages func(Get) `mir:"/user/messages"`
GetMessages func(Get, web.GetMessagesReq) web.GetMessagesResp `mir:"/user/messages"`
// ReadMessage 标记消息已读
ReadMessage func(Post) `mir:"/user/message/read"`
ReadMessage func(Post, web.ReadMessageReq) `mir:"/user/message/read"`
// SendUserWhisper 发送用户私信
SendUserWhisper func(Post) `mir:"/user/whisper"`

Loading…
Cancel
Save