diff --git a/README.md b/README.md index f1ebb53b..e8f98f80 100644 --- a/README.md +++ b/README.md @@ -383,6 +383,8 @@ release/paopao serve --no-default-features --features sqlite3,localoss,loggerfil |[`Pyroscope`](docs/proposal/23021510-关于使用pyroscope用于性能调试的设计.md)| 性能优化 | 内测 | 开启Pyroscope功能用于性能调试 | |[`Pprof`](docs/proposal/23062905-添加Pprof功能特性用于获取Profile.md)| 性能优化 | 内测 | 开启Pprof功能收集Profile信息 | |`PhoneBind` | 其他 | 稳定 | 手机绑定功能 | +|`UseAuditHook` | 其他 | 内测 | 使用审核hook功能 | +|`UseJobManager` | 其他 | 内测 | 使用JobManager功能 | |`Web:DisallowUserRegister` | 功能特性 | 稳定 | 不允许用户注册 | > 功能项状态详情参考 [features-status](features-status.md). diff --git a/auto/api/v1/loose.go b/auto/api/v1/loose.go index 4c9f9c6f..8c6fcbc8 100644 --- a/auto/api/v1/loose.go +++ b/auto/api/v1/loose.go @@ -89,7 +89,12 @@ func RegisterLooseServant(e *gin.Engine, s Loose) { return } resp, err := s.GetUserTweets(req) - s.Render(c, resp, err) + if err != nil { + s.Render(c, nil, err) + return + } + var rv _render_ = resp + rv.Render(c) }) router.Handle("GET", "/posts", func(c *gin.Context) { select { diff --git a/auto/api/v1/priv.go b/auto/api/v1/priv.go index f4adccfc..c6142180 100644 --- a/auto/api/v1/priv.go +++ b/auto/api/v1/priv.go @@ -44,8 +44,20 @@ type Priv interface { mustEmbedUnimplementedPrivServant() } +type PrivChain interface { + ChainCreateTweet() gin.HandlersChain + + mustEmbedUnimplementedPrivChain() +} + // RegisterPrivServant register Priv servant to gin -func RegisterPrivServant(e *gin.Engine, s Priv) { +func RegisterPrivServant(e *gin.Engine, s Priv, m ...PrivChain) { + var cc PrivChain + if len(m) > 0 { + cc = m[0] + } else { + cc = &UnimplementedPrivChain{} + } router := e.Group("v1") // use chain for router middlewares := s.Chain() @@ -297,7 +309,7 @@ func RegisterPrivServant(e *gin.Engine, s Priv) { } s.Render(c, nil, s.DeleteTweet(req)) }) - router.Handle("POST", "/post", func(c *gin.Context) { + router.Handle("POST", "/post", append(cc.ChainCreateTweet(), func(c *gin.Context) { select { case <-c.Request.Context().Done(): return @@ -310,8 +322,13 @@ func RegisterPrivServant(e *gin.Engine, s Priv) { return } resp, err := s.CreateTweet(req) - s.Render(c, resp, err) - }) + if err != nil { + s.Render(c, nil, err) + return + } + var rv _render_ = resp + rv.Render(c) + })...) router.Handle("GET", "/attachment", func(c *gin.Context) { select { case <-c.Request.Context().Done(): @@ -455,3 +472,12 @@ func (UnimplementedPrivServant) UploadAttachment(req *web.UploadAttachmentReq) ( } func (UnimplementedPrivServant) mustEmbedUnimplementedPrivServant() {} + +// UnimplementedPrivChain can be embedded to have forward compatible implementations. +type UnimplementedPrivChain struct{} + +func (b *UnimplementedPrivChain) ChainCreateTweet() gin.HandlersChain { + return nil +} + +func (b *UnimplementedPrivChain) mustEmbedUnimplementedPrivChain() {} diff --git a/features-status.md b/features-status.md index b8b0dafc..4949fdc8 100644 --- a/features-status.md +++ b/features-status.md @@ -196,7 +196,17 @@ * `PhoneBind` 手机绑定功能; * [ ] 提按文档 * [x] 接口定义 - * [x] 业务逻辑实现 + * [x] 业务逻辑实现 + +* `UseAuditHook` 使用审核hook功能 (目前状态: 内测 待完善后将转为Builtin) + * [ ] 提按文档 + * [x] 接口定义 + * [x] 业务逻辑实现 + +* `UseJobManager` 使用JobManager功能 (目前状态: 内测 待完善后将转为Builtin) + * [ ] 提按文档 + * [x] 接口定义 + * [x] 业务逻辑实现 ### 功能特性: * `Web:DisallowUserRegister` 不允许用户注册; diff --git a/internal/conf/cache.go b/internal/conf/cache.go index ae3875b2..8f69a01c 100644 --- a/internal/conf/cache.go +++ b/internal/conf/cache.go @@ -14,6 +14,11 @@ const ( _defaultKeyPoolSize = 128 ) +// 以下包含一些在cache中会用到的key的前缀 +const ( + PrefixUserTweets = "paopao:usertweets:" +) + // 以下包含一些在cache中会用到的池化后的key var ( KeyUnreadMsg cache.KeyPool[int64] diff --git a/internal/conf/config.yaml b/internal/conf/config.yaml index 5f6c8ceb..fbcdd317 100644 --- a/internal/conf/config.yaml +++ b/internal/conf/config.yaml @@ -9,10 +9,11 @@ Cache: KeyPoolSize: 256 # 键的池大小, 设置范围[128, ++], 默认256 CientSideCacheExpire: 60 # 客户端缓存过期时间 默认60s UnreadMsgExpire: 60 # 未读消息过期时间,单位秒, 默认60s + UserTweetsExpire: 60 # 获取用户推文列表过期时间,单位秒, 默认60s EventManager: # 事件管理器的配置参数 - MinWorker: 10 # 最小后台工作者, 设置范围[5, ++], 默认10 - MaxEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 - MaxTempEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 + MinWorker: 64 # 最小后台工作者, 设置范围[5, ++], 默认64 + MaxEventBuf: 128 # 最大log缓存条数, 设置范围[10, ++], 默认128 + MaxTempEventBuf: 256 # 最大log缓存条数, 设置范围[10, ++], 默认256 MaxTickCount: 60 # 最大的循环周期, 设置范围[60, ++], 默认60 TickWaitTime: 1 # 一个周期的等待时间,单位:秒 默认1s Features: diff --git a/internal/conf/setting.go b/internal/conf/setting.go index 04fcd9c9..0f64f446 100644 --- a/internal/conf/setting.go +++ b/internal/conf/setting.go @@ -100,6 +100,7 @@ type cacheConf struct { KeyPoolSize int CientSideCacheExpire time.Duration UnreadMsgExpire int64 + UserTweetsExpire int64 } type eventManagerConf struct { diff --git a/internal/core/cache.go b/internal/core/cache.go index 2a0db675..335a62ae 100644 --- a/internal/core/cache.go +++ b/internal/core/cache.go @@ -98,8 +98,18 @@ type RedisCache interface { DelRechargeStatus(ctx context.Context, tradeNo string) error } +type AppCache interface { + Get(key string) ([]byte, error) + Set(key string, data []byte, ex int64) error + Delete(key ...string) error + DelAny(pattern string) error + Exist(key string) bool +} + type WebCache interface { + AppCache GetUnreadMsgCountResp(uid int64) ([]byte, error) PutUnreadMsgCountResp(uid int64, data []byte) error DelUnreadMsgCountResp(uid int64) error + ExistUnreadMsgCountResp(uid int64) bool } diff --git a/internal/dao/cache/cache.go b/internal/dao/cache/cache.go index 05f6fce8..2cee6684 100644 --- a/internal/dao/cache/cache.go +++ b/internal/dao/cache/cache.go @@ -58,6 +58,11 @@ func NewWebCache() core.WebCache { return _webCache } +func NewAppCache() core.AppCache { + lazyInitial() + return _appCache +} + func NewSimpleCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) { s := conf.SimpleCacheIndexSetting cacheIndex := &simpleCacheIndexServant{ @@ -101,6 +106,7 @@ func NewNoneCacheIndexService(indexPosts core.IndexPostsService) (core.CacheInde func lazyInitial() { _onceInit.Do(func() { - _webCache = newWebCache() + _appCache = newAppCache() + _webCache = newWebCache(_appCache) }) } diff --git a/internal/dao/cache/web.go b/internal/dao/cache/web.go index d911c5bc..6db706c0 100644 --- a/internal/dao/cache/web.go +++ b/internal/dao/cache/web.go @@ -8,7 +8,6 @@ import ( "context" "time" - "github.com/Masterminds/semver/v3" "github.com/redis/rueidis" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/core" @@ -16,25 +15,21 @@ import ( ) var ( - _webCache core.WebCache = (*redisWebCache)(nil) + _webCache core.WebCache = (*webCache)(nil) + _appCache core.AppCache = (*appCache)(nil) ) -type redisWebCache struct { - cscExpire time.Duration - unreadMsgExpire int64 - c rueidis.Client -} - -func (s *redisWebCache) Name() string { - return "RedisWebCache" +type appCache struct { + cscExpire time.Duration + c rueidis.Client } -func (s *redisWebCache) Version() *semver.Version { - return semver.MustParse("v0.1.0") +type webCache struct { + core.AppCache + unreadMsgExpire int64 } -func (s *redisWebCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) { - key := conf.KeyUnreadMsg.Get(uid) +func (s *appCache) Get(key string) ([]byte, error) { res, err := rueidis.MGetCache(s.c, context.Background(), s.cscExpire, []string{key}) if err != nil { return nil, err @@ -43,24 +38,80 @@ func (s *redisWebCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) { return message.AsBytes() } -func (s *redisWebCache) PutUnreadMsgCountResp(uid int64, data []byte) error { +func (s *appCache) Set(key string, data []byte, ex int64) error { return s.c.Do(context.Background(), s.c.B().Set(). - Key(conf.KeyUnreadMsg.Get(uid)). + Key(key). Value(utils.String(data)). - ExSeconds(s.unreadMsgExpire). + ExSeconds(ex). Build()). Error() } -func (s *redisWebCache) DelUnreadMsgCountResp(uid int64) error { - return s.c.Do(context.Background(), s.c.B().Del().Key(conf.KeyUnreadMsg.Get(uid)).Build()).Error() +func (s *appCache) Delete(keys ...string) (err error) { + if len(keys) != 0 { + err = s.c.Do(context.Background(), s.c.B().Del().Key(keys...).Build()).Error() + } + return +} + +func (s *appCache) DelAny(pattern string) (err error) { + var ( + keys []string + cursor uint64 + entry rueidis.ScanEntry + ) + ctx := context.Background() + for { + cmd := s.c.B().Scan().Cursor(cursor).Match(pattern).Count(50).Build() + if entry, err = s.c.Do(ctx, cmd).AsScanEntry(); err != nil { + return + } + keys = append(keys, entry.Elements...) + if entry.Cursor != 0 { + cursor = entry.Cursor + continue + } + break + } + if len(keys) != 0 { + err = s.c.Do(ctx, s.c.B().Del().Key(keys...).Build()).Error() + } + return +} + +func (s *appCache) Exist(key string) bool { + cmd := s.c.B().Exists().Key(key).Build() + count, _ := s.c.Do(context.Background(), cmd).AsInt64() + return count > 0 +} + +func (s *webCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) { + key := conf.KeyUnreadMsg.Get(uid) + return s.Get(key) +} + +func (s *webCache) PutUnreadMsgCountResp(uid int64, data []byte) error { + return s.Set(conf.KeyUnreadMsg.Get(uid), data, s.unreadMsgExpire) +} + +func (s *webCache) DelUnreadMsgCountResp(uid int64) error { + return s.Delete(conf.KeyUnreadMsg.Get(uid)) +} + +func (s *webCache) ExistUnreadMsgCountResp(uid int64) bool { + return s.Exist(conf.KeyUnreadMsg.Get(uid)) +} + +func newAppCache() *appCache { + return &appCache{ + cscExpire: conf.CacheSetting.CientSideCacheExpire, + c: conf.MustRedisClient(), + } } -func newWebCache() *redisWebCache { - s := conf.CacheSetting - return &redisWebCache{ - cscExpire: s.CientSideCacheExpire, - unreadMsgExpire: s.UnreadMsgExpire, - c: conf.MustRedisClient(), +func newWebCache(ac core.AppCache) *webCache { + return &webCache{ + AppCache: ac, + unreadMsgExpire: conf.CacheSetting.UnreadMsgExpire, } } diff --git a/internal/events/events.go b/internal/events/events.go index 01b9c15c..cccc145d 100644 --- a/internal/events/events.go +++ b/internal/events/events.go @@ -18,7 +18,7 @@ var ( func Initial() { _onceInitial.Do(func() { initEventManager() - if cfg.If("JobManager") { + if cfg.If("UseJobManager") { initJobManager() logrus.Debugln("initial JobManager") } diff --git a/internal/model/web/audit.go b/internal/model/web/audit.go new file mode 100644 index 00000000..5b891177 --- /dev/null +++ b/internal/model/web/audit.go @@ -0,0 +1,39 @@ +// 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 web + +const ( + AuditStyleUnknown AuditStyle = iota + AuditStyleUserTweet + AuditStyleUserTweetComment + AuditStyleUserTweetReply +) + +const ( + AuditHookCtxKey = "audit_ctx_key" +) + +type AuditStyle uint8 + +type AuditMetaInfo struct { + Style AuditStyle + Id int64 +} + +func (s AuditStyle) String() (res string) { + switch s { + case AuditStyleUserTweet: + res = "UserTweet" + case AuditStyleUserTweetComment: + res = "UserTweetComment" + case AuditStyleUserTweetReply: + res = "UserTweetReply" + case AuditStyleUnknown: + fallthrough + default: + res = "Unknown" + } + return +} diff --git a/internal/model/web/loose.go b/internal/model/web/loose.go index 8ab19584..cba3e8ab 100644 --- a/internal/model/web/loose.go +++ b/internal/model/web/loose.go @@ -5,10 +5,14 @@ package web import ( + "encoding/json" + "net/http" + "github.com/alimy/mir/v4" "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core/cs" + "github.com/rocboss/paopao-ce/internal/model/joint" "github.com/rocboss/paopao-ce/internal/servants/base" "github.com/rocboss/paopao-ce/pkg/app" ) @@ -59,7 +63,10 @@ type GetUserTweetsReq struct { PageSize int `form:"-" binding:"-"` } -type GetUserTweetsResp base.PageResp +type GetUserTweetsResp struct { + Data *base.PageResp + JsonResp json.RawMessage +} type GetUserProfileReq struct { BaseInfo `form:"-" binding:"-"` @@ -111,3 +118,15 @@ func (r *TimelineReq) Bind(c *gin.Context) mir.Error { r.Query, r.Type = c.Query("query"), "search" return nil } + +func (r *GetUserTweetsResp) Render(c *gin.Context) { + if len(r.JsonResp) != 0 { + c.JSON(http.StatusOK, r.JsonResp) + } else { + c.JSON(http.StatusOK, &joint.JsonResp{ + Code: 0, + Msg: "success", + Data: r.Data, + }) + } +} diff --git a/internal/model/web/priv.go b/internal/model/web/priv.go index 7ce1aba0..604311b1 100644 --- a/internal/model/web/priv.go +++ b/internal/model/web/priv.go @@ -7,12 +7,14 @@ package web import ( "fmt" "mime/multipart" + "net/http" "strings" "github.com/alimy/mir/v4" "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/core" "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/convert" "github.com/rocboss/paopao-ce/pkg/xerror" @@ -281,3 +283,16 @@ func (r *CreateCommentReq) Bind(c *gin.Context) mir.Error { r.ClientIP = c.ClientIP() return bindAny(c, r) } + +func (r *CreateTweetResp) Render(c *gin.Context) { + c.JSON(http.StatusOK, &joint.JsonResp{ + Code: 0, + Msg: "success", + Data: r, + }) + // 设置审核元信息,用于接下来的审核逻辑 + c.Set(AuditHookCtxKey, &AuditMetaInfo{ + Style: AuditStyleUserTweet, + Id: r.ID, + }) +} diff --git a/internal/servants/base/events.go b/internal/servants/base/events.go index c5e7d08e..545d3399 100644 --- a/internal/servants/base/events.go +++ b/internal/servants/base/events.go @@ -5,10 +5,36 @@ package base import ( + "fmt" + "github.com/alimy/tryst/event" + "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core/ms" + "github.com/rocboss/paopao-ce/internal/events" + "github.com/rocboss/paopao-ce/internal/model/joint" + "github.com/rocboss/paopao-ce/pkg/json" ) +type CacheRespEvent struct { + event.UnimplementedEvent + ac core.AppCache + key string + data any + expire int64 +} + +type ExpireRespEvent struct { + event.UnimplementedEvent + ac core.AppCache + keys []string +} + +type ExpireAnyRespEvent struct { + event.UnimplementedEvent + ac core.AppCache + pattern string +} + type pushPostToSearchEvent struct { event.UnimplementedEvent fn func(*ms.Post) @@ -20,6 +46,71 @@ type pushAllPostToSearchEvent struct { fn func() error } +func OnCacheRespEvent(ac core.AppCache, key string, data any, expire int64) { + events.OnEvent(&CacheRespEvent{ + ac: ac, + key: key, + data: data, + expire: expire, + }) +} + +func OnExpireRespEvent(ac core.AppCache, keys ...string) { + if len(keys) != 0 { + events.OnEvent(&ExpireRespEvent{ + ac: ac, + keys: keys, + }) + } +} + +func OnExpireAnyRespEvent(ac core.AppCache, pattern string) { + events.OnEvent(&ExpireAnyRespEvent{ + ac: ac, + pattern: pattern, + }) +} + +func (p *CacheRespEvent) Name() string { + return "servants.base.CacheRespEvent" +} + +func (p *CacheRespEvent) Action() error { + if p.ac.Exist(p.key) { + // do nothing + return nil + } + resp := &joint.JsonResp{ + Code: 0, + Msg: "success", + Data: p.data, + } + data, err := json.Marshal(resp) + if err != nil { + return fmt.Errorf("CacheRespEvent action marshal resp occurs error: %w", err) + } + if err = p.ac.Set(p.key, data, p.expire); err != nil { + return fmt.Errorf("CacheRespEvent action put resp data to redis cache occurs error: %w", err) + } + return nil +} + +func (p *ExpireRespEvent) Name() string { + return "servants.base.ExpireRespEvent" +} + +func (p *ExpireRespEvent) Action() (err error) { + return p.ac.Delete(p.keys...) +} + +func (p *ExpireAnyRespEvent) Name() string { + return "servants.base.ExpireAnyRespEvent" +} + +func (p *ExpireAnyRespEvent) Action() (err error) { + return p.ac.DelAny(p.pattern) +} + func (p *pushPostToSearchEvent) Name() string { return "servants.base.pushPostToSearchEvent" } diff --git a/internal/servants/chain/audit.go b/internal/servants/chain/audit.go new file mode 100644 index 00000000..01976777 --- /dev/null +++ b/internal/servants/chain/audit.go @@ -0,0 +1,25 @@ +// 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 chain + +import ( + "github.com/gin-gonic/gin" + "github.com/rocboss/paopao-ce/internal/model/web" +) + +func AuditHook() gin.HandlerFunc { + return func(c *gin.Context) { + // 此midleware后面是真正的http handlder,让handler先执行 + c.Next() + // 审核hook 后处理逻辑 + var ami *web.AuditMetaInfo + if val, ok := c.Get(web.AuditHookCtxKey); ok { + if ami, ok = val.(*web.AuditMetaInfo); !ok { + return + } + } + OnAudiotHookEvent(ami) + } +} diff --git a/internal/servants/chain/events.go b/internal/servants/chain/events.go new file mode 100644 index 00000000..d1d73777 --- /dev/null +++ b/internal/servants/chain/events.go @@ -0,0 +1,35 @@ +// 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 chain + +import ( + "github.com/alimy/tryst/event" + "github.com/rocboss/paopao-ce/internal/events" + "github.com/rocboss/paopao-ce/internal/model/web" + "github.com/sirupsen/logrus" +) + +type AuditHookEvent struct { + event.UnimplementedEvent + ami *web.AuditMetaInfo +} + +func (e *AuditHookEvent) Name() string { + return "AuditHookEvent" +} + +func (e *AuditHookEvent) Action() error { + // TODO: just log event now, will add real logic in future. + logrus.Debugf("auditHook event action style[%s] id[%d]", e.ami.Style, e.ami.Id) + return nil +} + +func OnAudiotHookEvent(ami *web.AuditMetaInfo) { + if ami != nil { + events.OnEvent(&AuditHookEvent{ + ami: ami, + }) + } +} diff --git a/internal/servants/web/events.go b/internal/servants/web/events.go index f5b354df..f3879d5e 100644 --- a/internal/servants/web/events.go +++ b/internal/servants/web/events.go @@ -51,6 +51,10 @@ func (e *cacheUnreadMsgEvent) Name() string { } func (e *cacheUnreadMsgEvent) Action() error { + if e.wc.ExistUnreadMsgCountResp(e.uid) { + // do nothing + return nil + } count, err := e.ds.GetUnreadCount(e.uid) if err != nil { return fmt.Errorf("cacheUnreadMsgEvent action occurs error: %w", err) diff --git a/internal/servants/web/loose.go b/internal/servants/web/loose.go index dd01058f..583287ba 100644 --- a/internal/servants/web/loose.go +++ b/internal/servants/web/loose.go @@ -6,9 +6,12 @@ package web import ( "fmt" + "github.com/alimy/mir/v4" + "github.com/alimy/tryst/lets" "github.com/gin-gonic/gin" 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" @@ -26,6 +29,9 @@ var ( type looseSrv struct { api.UnimplementedLooseServant *base.DaoServant + ac core.AppCache + userTweetsExpire int64 + prefixUserTweets string } func (s *looseSrv) Chain() gin.HandlersChain { @@ -67,6 +73,13 @@ func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTwe if xerr != nil { return nil, err } + // 尝试直接从缓存中获取数据 + key, ok := "", false + if res, key, ok = s.userTweetsFromCache(req, user); ok { + // logrus.Debugf("GetUserTweets from cache key:%s", key) + return + } + // 缓存获取未成功,只能查库了 switch req.Style { case web.UserPostsStyleComment, web.UserPostsStyleMedia: res, err = s.listUserTweets(req, user) @@ -79,6 +92,24 @@ func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTwe default: res, err = s.getUserPostTweets(req, user, false) } + // 缓存处理 + if err == nil { + base.OnCacheRespEvent(s.ac, key, res.Data, s.userTweetsExpire) + } + return +} + +func (s *looseSrv) userTweetsFromCache(req *web.GetUserTweetsReq, user *cs.VistUser) (res *web.GetUserTweetsResp, key string, ok bool) { + switch req.Style { + case web.UserPostsStylePost, web.UserPostsStyleHighlight, web.UserPostsStyleMedia: + key = fmt.Sprintf("%s%s:%s:%s:%d:%d", s.prefixUserTweets, req.Username, req.Style, user.RelTyp, req.Page, req.PageSize) + default: + visitUserName := lets.If(user.RelTyp != cs.RelationGuest, user.Username, "_") + key = fmt.Sprintf("%s%s:%s:%s:%d:%d", s.prefixUserTweets, req.Username, req.Style, visitUserName, req.Page, req.PageSize) + } + if data, err := s.ac.Get(key); err == nil { + ok, res = true, &web.GetUserTweetsResp{JsonResp: data} + } return } @@ -100,7 +131,7 @@ func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUse return nil, web.ErrGetStarsFailed } resp := base.PageRespFrom(postsFormated, req.Page, req.PageSize, totalRows) - return (*web.GetUserTweetsResp)(resp), nil + return &web.GetUserTweetsResp{Data: resp}, nil } func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser) (*web.GetUserTweetsResp, mir.Error) { @@ -109,7 +140,6 @@ func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser) total int64 err error ) - fmt.Print(user, user.UserId, req.Style, req.PageSize, (req.Page-1)*req.PageSize) if req.Style == web.UserPostsStyleComment { tweets, total, err = s.Ds.ListUserCommentTweets(user, req.PageSize, (req.Page-1)*req.PageSize) } else if req.Style == web.UserPostsStyleMedia { @@ -128,7 +158,7 @@ func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser) return nil, web.ErrGetPostsFailed } resp := base.PageRespFrom(postFormated, req.Page, req.PageSize, total) - return (*web.GetUserTweetsResp)(resp), nil + return &web.GetUserTweetsResp{Data: resp}, nil } func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *cs.VistUser, isHighlight bool) (*web.GetUserTweetsResp, mir.Error) { @@ -162,7 +192,7 @@ func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *cs.VistUse return nil, web.ErrGetPostsFailed } resp := base.PageRespFrom(posts, req.Page, req.PageSize, totalRows) - return (*web.GetUserTweetsResp)(resp), nil + return &web.GetUserTweetsResp{Data: resp}, nil } func (s *looseSrv) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error) { @@ -324,8 +354,11 @@ func (s *looseSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsR return (*web.TweetCommentsResp)(resp), nil } -func newLooseSrv(s *base.DaoServant) api.Loose { +func newLooseSrv(s *base.DaoServant, ac core.AppCache) api.Loose { return &looseSrv{ - DaoServant: s, + DaoServant: s, + ac: ac, + userTweetsExpire: conf.CacheSetting.UserTweetsExpire, + prefixUserTweets: conf.PrefixUserTweets, } } diff --git a/internal/servants/web/priv.go b/internal/servants/web/priv.go index efb681b4..3fc5900a 100644 --- a/internal/servants/web/priv.go +++ b/internal/servants/web/priv.go @@ -10,6 +10,7 @@ import ( "time" "github.com/alimy/mir/v4" + "github.com/alimy/tryst/cfg" "github.com/disintegration/imaging" "github.com/gin-gonic/gin" "github.com/gofrs/uuid/v5" @@ -27,7 +28,8 @@ import ( ) var ( - _ api.Priv = (*privSrv)(nil) + _ api.Priv = (*privSrv)(nil) + _ api.PrivChain = (*privChain)(nil) _uploadAttachmentTypeMap = map[string]ms.AttachmentType{ "public/image": ms.AttachmentTypeImage, @@ -35,12 +37,6 @@ var ( "public/video": ms.AttachmentTypeVideo, "attachment": ms.AttachmentTypeOther, } - _uploadAttachmentTypes = map[string]cs.AttachmentType{ - "public/image": cs.AttachmentTypeImage, - "public/avatar": cs.AttachmentTypeImage, - "public/video": cs.AttachmentTypeVideo, - "attachment": cs.AttachmentTypeOther, - } ) type privSrv struct { @@ -50,6 +46,17 @@ type privSrv struct { oss core.ObjectStorageService } +type privChain struct { + api.UnimplementedPrivChain +} + +func (s *privChain) ChainCreateTweet() (res gin.HandlersChain) { + if cfg.If("UseAuditHook") { + res = gin.HandlersChain{chain.AuditHook()} + } + return +} + func (s *privSrv) Chain() gin.HandlersChain { return gin.HandlersChain{chain.JWT(), chain.Priv()} } @@ -846,3 +853,7 @@ func newPrivSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Priv { oss: oss, } } + +func newPrivChain() api.PrivChain { + return &privChain{} +} diff --git a/internal/servants/web/web.go b/internal/servants/web/web.go index 87d13d4b..60b99d39 100644 --- a/internal/servants/web/web.go +++ b/internal/servants/web/web.go @@ -21,6 +21,7 @@ var ( _enablePhoneVerify bool _disallowUserRegister bool _ds core.DataService + _ac core.AppCache _wc core.WebCache _oss core.ObjectStorageService _onceInitial sync.Once @@ -34,8 +35,8 @@ func RouteWeb(e *gin.Engine) { api.RegisterAdminServant(e, newAdminSrv(ds)) api.RegisterCoreServant(e, newCoreSrv(ds, _oss, _wc)) api.RegisterRelaxServant(e, newRelaxSrv(ds, _wc)) - api.RegisterLooseServant(e, newLooseSrv(ds)) - api.RegisterPrivServant(e, newPrivSrv(ds, _oss)) + api.RegisterLooseServant(e, newLooseSrv(ds, _ac)) + api.RegisterPrivServant(e, newPrivSrv(ds, _oss), newPrivChain()) api.RegisterPubServant(e, newPubSrv(ds, _oss)) api.RegisterKeyQueryServant(e, NewShareKeyServant(ds)) api.RegisterRankServant(e, NewRankServant(ds)) @@ -56,6 +57,7 @@ func lazyInitial() { _disallowUserRegister = cfg.If("Web:DisallowUserRegister") _oss = dao.ObjectStorageService() _ds = dao.DataService() + _ac = cache.NewAppCache() _wc = cache.NewWebCache() }) } diff --git a/mirc/web/v1/priv.go b/mirc/web/v1/priv.go index 567de00d..758afd31 100644 --- a/mirc/web/v1/priv.go +++ b/mirc/web/v1/priv.go @@ -25,7 +25,7 @@ type Priv struct { DownloadAttachment func(Get, web.DownloadAttachmentReq) web.DownloadAttachmentResp `mir:"/attachment"` // CreateTweet 发布动态 - CreateTweet func(Post, web.CreateTweetReq) web.CreateTweetResp `mir:"/post"` + CreateTweet func(Post, Chain, web.CreateTweetReq) web.CreateTweetResp `mir:"/post"` // DeleteTweet 删除动态 DeleteTweet func(Delete, web.DeleteTweetReq) `mir:"/post"`