增加用户帖子的缓存机制

pull/406/head
HXY 2 years ago
commit bd485c2626

@ -383,6 +383,8 @@ release/paopao serve --no-default-features --features sqlite3,localoss,loggerfil
|[`Pyroscope`](docs/proposal/23021510-关于使用pyroscope用于性能调试的设计.md)| 性能优化 | 内测 | 开启Pyroscope功能用于性能调试 | |[`Pyroscope`](docs/proposal/23021510-关于使用pyroscope用于性能调试的设计.md)| 性能优化 | 内测 | 开启Pyroscope功能用于性能调试 |
|[`Pprof`](docs/proposal/23062905-添加Pprof功能特性用于获取Profile.md)| 性能优化 | 内测 | 开启Pprof功能收集Profile信息 | |[`Pprof`](docs/proposal/23062905-添加Pprof功能特性用于获取Profile.md)| 性能优化 | 内测 | 开启Pprof功能收集Profile信息 |
|`PhoneBind` | 其他 | 稳定 | 手机绑定功能 | |`PhoneBind` | 其他 | 稳定 | 手机绑定功能 |
|`UseAuditHook` | 其他 | 内测 | 使用审核hook功能 |
|`UseJobManager` | 其他 | 内测 | 使用JobManager功能 |
|`Web:DisallowUserRegister` | 功能特性 | 稳定 | 不允许用户注册 | |`Web:DisallowUserRegister` | 功能特性 | 稳定 | 不允许用户注册 |
> 功能项状态详情参考 [features-status](features-status.md). > 功能项状态详情参考 [features-status](features-status.md).

@ -89,7 +89,12 @@ func RegisterLooseServant(e *gin.Engine, s Loose) {
return return
} }
resp, err := s.GetUserTweets(req) 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) { router.Handle("GET", "/posts", func(c *gin.Context) {
select { select {

@ -44,8 +44,20 @@ type Priv interface {
mustEmbedUnimplementedPrivServant() mustEmbedUnimplementedPrivServant()
} }
type PrivChain interface {
ChainCreateTweet() gin.HandlersChain
mustEmbedUnimplementedPrivChain()
}
// RegisterPrivServant register Priv servant to gin // 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") router := e.Group("v1")
// use chain for router // use chain for router
middlewares := s.Chain() middlewares := s.Chain()
@ -297,7 +309,7 @@ func RegisterPrivServant(e *gin.Engine, s Priv) {
} }
s.Render(c, nil, s.DeleteTweet(req)) 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 { select {
case <-c.Request.Context().Done(): case <-c.Request.Context().Done():
return return
@ -310,8 +322,13 @@ func RegisterPrivServant(e *gin.Engine, s Priv) {
return return
} }
resp, err := s.CreateTweet(req) 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) { router.Handle("GET", "/attachment", func(c *gin.Context) {
select { select {
case <-c.Request.Context().Done(): case <-c.Request.Context().Done():
@ -455,3 +472,12 @@ func (UnimplementedPrivServant) UploadAttachment(req *web.UploadAttachmentReq) (
} }
func (UnimplementedPrivServant) mustEmbedUnimplementedPrivServant() {} 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() {}

@ -198,6 +198,16 @@
* [x] 接口定义 * [x] 接口定义
* [x] 业务逻辑实现 * [x] 业务逻辑实现
* `UseAuditHook` 使用审核hook功能 (目前状态: 内测 待完善后将转为Builtin)
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
* `UseJobManager` 使用JobManager功能 (目前状态: 内测 待完善后将转为Builtin)
* [ ] 提按文档
* [x] 接口定义
* [x] 业务逻辑实现
### 功能特性: ### 功能特性:
* `Web:DisallowUserRegister` 不允许用户注册; * `Web:DisallowUserRegister` 不允许用户注册;
* [ ] 提按文档 * [ ] 提按文档

@ -14,6 +14,11 @@ const (
_defaultKeyPoolSize = 128 _defaultKeyPoolSize = 128
) )
// 以下包含一些在cache中会用到的key的前缀
const (
PrefixUserTweets = "paopao:usertweets:"
)
// 以下包含一些在cache中会用到的池化后的key // 以下包含一些在cache中会用到的池化后的key
var ( var (
KeyUnreadMsg cache.KeyPool[int64] KeyUnreadMsg cache.KeyPool[int64]

@ -9,10 +9,11 @@ Cache:
KeyPoolSize: 256 # 键的池大小, 设置范围[128, ++], 默认256 KeyPoolSize: 256 # 键的池大小, 设置范围[128, ++], 默认256
CientSideCacheExpire: 60 # 客户端缓存过期时间 默认60s CientSideCacheExpire: 60 # 客户端缓存过期时间 默认60s
UnreadMsgExpire: 60 # 未读消息过期时间,单位秒, 默认60s UnreadMsgExpire: 60 # 未读消息过期时间,单位秒, 默认60s
UserTweetsExpire: 60 # 获取用户推文列表过期时间,单位秒, 默认60s
EventManager: # 事件管理器的配置参数 EventManager: # 事件管理器的配置参数
MinWorker: 10 # 最小后台工作者, 设置范围[5, ++], 默认10 MinWorker: 64 # 最小后台工作者, 设置范围[5, ++], 默认64
MaxEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 MaxEventBuf: 128 # 最大log缓存条数, 设置范围[10, ++], 默认128
MaxTempEventBuf: 100 # 最大log缓存条数, 设置范围[10, ++], 默认100 MaxTempEventBuf: 256 # 最大log缓存条数, 设置范围[10, ++], 默认256
MaxTickCount: 60 # 最大的循环周期, 设置范围[60, ++], 默认60 MaxTickCount: 60 # 最大的循环周期, 设置范围[60, ++], 默认60
TickWaitTime: 1 # 一个周期的等待时间,单位:秒 默认1s TickWaitTime: 1 # 一个周期的等待时间,单位:秒 默认1s
Features: Features:

@ -100,6 +100,7 @@ type cacheConf struct {
KeyPoolSize int KeyPoolSize int
CientSideCacheExpire time.Duration CientSideCacheExpire time.Duration
UnreadMsgExpire int64 UnreadMsgExpire int64
UserTweetsExpire int64
} }
type eventManagerConf struct { type eventManagerConf struct {

@ -98,8 +98,18 @@ type RedisCache interface {
DelRechargeStatus(ctx context.Context, tradeNo string) error 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 { type WebCache interface {
AppCache
GetUnreadMsgCountResp(uid int64) ([]byte, error) GetUnreadMsgCountResp(uid int64) ([]byte, error)
PutUnreadMsgCountResp(uid int64, data []byte) error PutUnreadMsgCountResp(uid int64, data []byte) error
DelUnreadMsgCountResp(uid int64) error DelUnreadMsgCountResp(uid int64) error
ExistUnreadMsgCountResp(uid int64) bool
} }

@ -58,6 +58,11 @@ func NewWebCache() core.WebCache {
return _webCache return _webCache
} }
func NewAppCache() core.AppCache {
lazyInitial()
return _appCache
}
func NewSimpleCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) { func NewSimpleCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) {
s := conf.SimpleCacheIndexSetting s := conf.SimpleCacheIndexSetting
cacheIndex := &simpleCacheIndexServant{ cacheIndex := &simpleCacheIndexServant{
@ -101,6 +106,7 @@ func NewNoneCacheIndexService(indexPosts core.IndexPostsService) (core.CacheInde
func lazyInitial() { func lazyInitial() {
_onceInit.Do(func() { _onceInit.Do(func() {
_webCache = newWebCache() _appCache = newAppCache()
_webCache = newWebCache(_appCache)
}) })
} }

@ -8,7 +8,6 @@ import (
"context" "context"
"time" "time"
"github.com/Masterminds/semver/v3"
"github.com/redis/rueidis" "github.com/redis/rueidis"
"github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
@ -16,25 +15,21 @@ import (
) )
var ( var (
_webCache core.WebCache = (*redisWebCache)(nil) _webCache core.WebCache = (*webCache)(nil)
_appCache core.AppCache = (*appCache)(nil)
) )
type redisWebCache struct { type appCache struct {
cscExpire time.Duration cscExpire time.Duration
unreadMsgExpire int64
c rueidis.Client c rueidis.Client
} }
func (s *redisWebCache) Name() string { type webCache struct {
return "RedisWebCache" core.AppCache
} unreadMsgExpire int64
func (s *redisWebCache) Version() *semver.Version {
return semver.MustParse("v0.1.0")
} }
func (s *redisWebCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) { func (s *appCache) Get(key string) ([]byte, error) {
key := conf.KeyUnreadMsg.Get(uid)
res, err := rueidis.MGetCache(s.c, context.Background(), s.cscExpire, []string{key}) res, err := rueidis.MGetCache(s.c, context.Background(), s.cscExpire, []string{key})
if err != nil { if err != nil {
return nil, err return nil, err
@ -43,24 +38,80 @@ func (s *redisWebCache) GetUnreadMsgCountResp(uid int64) ([]byte, error) {
return message.AsBytes() 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(). return s.c.Do(context.Background(), s.c.B().Set().
Key(conf.KeyUnreadMsg.Get(uid)). Key(key).
Value(utils.String(data)). Value(utils.String(data)).
ExSeconds(s.unreadMsgExpire). ExSeconds(ex).
Build()). Build()).
Error() Error()
} }
func (s *redisWebCache) DelUnreadMsgCountResp(uid int64) error { func (s *appCache) Delete(keys ...string) (err error) {
return s.c.Do(context.Background(), s.c.B().Del().Key(conf.KeyUnreadMsg.Get(uid)).Build()).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 newWebCache() *redisWebCache { func (s *webCache) PutUnreadMsgCountResp(uid int64, data []byte) error {
s := conf.CacheSetting return s.Set(conf.KeyUnreadMsg.Get(uid), data, s.unreadMsgExpire)
return &redisWebCache{ }
cscExpire: s.CientSideCacheExpire,
unreadMsgExpire: 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(), c: conf.MustRedisClient(),
} }
} }
func newWebCache(ac core.AppCache) *webCache {
return &webCache{
AppCache: ac,
unreadMsgExpire: conf.CacheSetting.UnreadMsgExpire,
}
}

@ -18,7 +18,7 @@ var (
func Initial() { func Initial() {
_onceInitial.Do(func() { _onceInitial.Do(func() {
initEventManager() initEventManager()
if cfg.If("JobManager") { if cfg.If("UseJobManager") {
initJobManager() initJobManager()
logrus.Debugln("initial JobManager") logrus.Debugln("initial JobManager")
} }

@ -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
}

@ -5,10 +5,14 @@
package web package web
import ( import (
"encoding/json"
"net/http"
"github.com/alimy/mir/v4" "github.com/alimy/mir/v4"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/cs" "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/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/app"
) )
@ -59,7 +63,10 @@ type GetUserTweetsReq struct {
PageSize int `form:"-" binding:"-"` PageSize int `form:"-" binding:"-"`
} }
type GetUserTweetsResp base.PageResp type GetUserTweetsResp struct {
Data *base.PageResp
JsonResp json.RawMessage
}
type GetUserProfileReq struct { type GetUserProfileReq struct {
BaseInfo `form:"-" binding:"-"` BaseInfo `form:"-" binding:"-"`
@ -111,3 +118,15 @@ func (r *TimelineReq) Bind(c *gin.Context) mir.Error {
r.Query, r.Type = c.Query("query"), "search" r.Query, r.Type = c.Query("query"), "search"
return nil 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,
})
}
}

@ -7,12 +7,14 @@ package web
import ( import (
"fmt" "fmt"
"mime/multipart" "mime/multipart"
"net/http"
"strings" "strings"
"github.com/alimy/mir/v4" "github.com/alimy/mir/v4"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms" "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/internal/servants/base"
"github.com/rocboss/paopao-ce/pkg/convert" "github.com/rocboss/paopao-ce/pkg/convert"
"github.com/rocboss/paopao-ce/pkg/xerror" "github.com/rocboss/paopao-ce/pkg/xerror"
@ -281,3 +283,16 @@ func (r *CreateCommentReq) Bind(c *gin.Context) mir.Error {
r.ClientIP = c.ClientIP() r.ClientIP = c.ClientIP()
return bindAny(c, r) 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,
})
}

@ -5,10 +5,36 @@
package base package base
import ( import (
"fmt"
"github.com/alimy/tryst/event" "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/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 { type pushPostToSearchEvent struct {
event.UnimplementedEvent event.UnimplementedEvent
fn func(*ms.Post) fn func(*ms.Post)
@ -20,6 +46,71 @@ type pushAllPostToSearchEvent struct {
fn func() error 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 { func (p *pushPostToSearchEvent) Name() string {
return "servants.base.pushPostToSearchEvent" return "servants.base.pushPostToSearchEvent"
} }

@ -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)
}
}

@ -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,
})
}
}

@ -51,6 +51,10 @@ func (e *cacheUnreadMsgEvent) Name() string {
} }
func (e *cacheUnreadMsgEvent) Action() error { func (e *cacheUnreadMsgEvent) Action() error {
if e.wc.ExistUnreadMsgCountResp(e.uid) {
// do nothing
return nil
}
count, err := e.ds.GetUnreadCount(e.uid) count, err := e.ds.GetUnreadCount(e.uid)
if err != nil { if err != nil {
return fmt.Errorf("cacheUnreadMsgEvent action occurs error: %w", err) return fmt.Errorf("cacheUnreadMsgEvent action occurs error: %w", err)

@ -6,9 +6,12 @@ package web
import ( import (
"fmt" "fmt"
"github.com/alimy/mir/v4" "github.com/alimy/mir/v4"
"github.com/alimy/tryst/lets"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1" 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"
"github.com/rocboss/paopao-ce/internal/core/cs" "github.com/rocboss/paopao-ce/internal/core/cs"
"github.com/rocboss/paopao-ce/internal/core/ms" "github.com/rocboss/paopao-ce/internal/core/ms"
@ -26,6 +29,9 @@ var (
type looseSrv struct { type looseSrv struct {
api.UnimplementedLooseServant api.UnimplementedLooseServant
*base.DaoServant *base.DaoServant
ac core.AppCache
userTweetsExpire int64
prefixUserTweets string
} }
func (s *looseSrv) Chain() gin.HandlersChain { func (s *looseSrv) Chain() gin.HandlersChain {
@ -67,6 +73,13 @@ func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTwe
if xerr != nil { if xerr != nil {
return nil, err 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 { switch req.Style {
case web.UserPostsStyleComment, web.UserPostsStyleMedia: case web.UserPostsStyleComment, web.UserPostsStyleMedia:
res, err = s.listUserTweets(req, user) res, err = s.listUserTweets(req, user)
@ -79,6 +92,24 @@ func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTwe
default: default:
res, err = s.getUserPostTweets(req, user, false) 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 return
} }
@ -100,7 +131,7 @@ func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUse
return nil, web.ErrGetStarsFailed return nil, web.ErrGetStarsFailed
} }
resp := base.PageRespFrom(postsFormated, req.Page, req.PageSize, totalRows) 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) { 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 total int64
err error err error
) )
fmt.Print(user, user.UserId, req.Style, req.PageSize, (req.Page-1)*req.PageSize)
if req.Style == web.UserPostsStyleComment { if req.Style == web.UserPostsStyleComment {
tweets, total, err = s.Ds.ListUserCommentTweets(user, req.PageSize, (req.Page-1)*req.PageSize) tweets, total, err = s.Ds.ListUserCommentTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
} else if req.Style == web.UserPostsStyleMedia { } else if req.Style == web.UserPostsStyleMedia {
@ -128,7 +158,7 @@ func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser)
return nil, web.ErrGetPostsFailed return nil, web.ErrGetPostsFailed
} }
resp := base.PageRespFrom(postFormated, req.Page, req.PageSize, total) 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) { 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 return nil, web.ErrGetPostsFailed
} }
resp := base.PageRespFrom(posts, req.Page, req.PageSize, totalRows) 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) { 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 return (*web.TweetCommentsResp)(resp), nil
} }
func newLooseSrv(s *base.DaoServant) api.Loose { func newLooseSrv(s *base.DaoServant, ac core.AppCache) api.Loose {
return &looseSrv{ return &looseSrv{
DaoServant: s, DaoServant: s,
ac: ac,
userTweetsExpire: conf.CacheSetting.UserTweetsExpire,
prefixUserTweets: conf.PrefixUserTweets,
} }
} }

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/alimy/mir/v4" "github.com/alimy/mir/v4"
"github.com/alimy/tryst/cfg"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gofrs/uuid/v5" "github.com/gofrs/uuid/v5"
@ -28,6 +29,7 @@ import (
var ( var (
_ api.Priv = (*privSrv)(nil) _ api.Priv = (*privSrv)(nil)
_ api.PrivChain = (*privChain)(nil)
_uploadAttachmentTypeMap = map[string]ms.AttachmentType{ _uploadAttachmentTypeMap = map[string]ms.AttachmentType{
"public/image": ms.AttachmentTypeImage, "public/image": ms.AttachmentTypeImage,
@ -35,12 +37,6 @@ var (
"public/video": ms.AttachmentTypeVideo, "public/video": ms.AttachmentTypeVideo,
"attachment": ms.AttachmentTypeOther, "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 { type privSrv struct {
@ -50,6 +46,17 @@ type privSrv struct {
oss core.ObjectStorageService 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 { func (s *privSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT(), chain.Priv()} return gin.HandlersChain{chain.JWT(), chain.Priv()}
} }
@ -846,3 +853,7 @@ func newPrivSrv(s *base.DaoServant, oss core.ObjectStorageService) api.Priv {
oss: oss, oss: oss,
} }
} }
func newPrivChain() api.PrivChain {
return &privChain{}
}

@ -21,6 +21,7 @@ var (
_enablePhoneVerify bool _enablePhoneVerify bool
_disallowUserRegister bool _disallowUserRegister bool
_ds core.DataService _ds core.DataService
_ac core.AppCache
_wc core.WebCache _wc core.WebCache
_oss core.ObjectStorageService _oss core.ObjectStorageService
_onceInitial sync.Once _onceInitial sync.Once
@ -34,8 +35,8 @@ func RouteWeb(e *gin.Engine) {
api.RegisterAdminServant(e, newAdminSrv(ds)) api.RegisterAdminServant(e, newAdminSrv(ds))
api.RegisterCoreServant(e, newCoreSrv(ds, _oss, _wc)) api.RegisterCoreServant(e, newCoreSrv(ds, _oss, _wc))
api.RegisterRelaxServant(e, newRelaxSrv(ds, _wc)) api.RegisterRelaxServant(e, newRelaxSrv(ds, _wc))
api.RegisterLooseServant(e, newLooseSrv(ds)) api.RegisterLooseServant(e, newLooseSrv(ds, _ac))
api.RegisterPrivServant(e, newPrivSrv(ds, _oss)) api.RegisterPrivServant(e, newPrivSrv(ds, _oss), newPrivChain())
api.RegisterPubServant(e, newPubSrv(ds, _oss)) api.RegisterPubServant(e, newPubSrv(ds, _oss))
api.RegisterKeyQueryServant(e, NewShareKeyServant(ds)) api.RegisterKeyQueryServant(e, NewShareKeyServant(ds))
api.RegisterRankServant(e, NewRankServant(ds)) api.RegisterRankServant(e, NewRankServant(ds))
@ -56,6 +57,7 @@ func lazyInitial() {
_disallowUserRegister = cfg.If("Web:DisallowUserRegister") _disallowUserRegister = cfg.If("Web:DisallowUserRegister")
_oss = dao.ObjectStorageService() _oss = dao.ObjectStorageService()
_ds = dao.DataService() _ds = dao.DataService()
_ac = cache.NewAppCache()
_wc = cache.NewWebCache() _wc = cache.NewWebCache()
}) })
} }

@ -25,7 +25,7 @@ type Priv struct {
DownloadAttachment func(Get, web.DownloadAttachmentReq) web.DownloadAttachmentResp `mir:"/attachment"` DownloadAttachment func(Get, web.DownloadAttachmentReq) web.DownloadAttachmentResp `mir:"/attachment"`
// CreateTweet 发布动态 // CreateTweet 发布动态
CreateTweet func(Post, web.CreateTweetReq) web.CreateTweetResp `mir:"/post"` CreateTweet func(Post, Chain, web.CreateTweetReq) web.CreateTweetResp `mir:"/post"`
// DeleteTweet 删除动态 // DeleteTweet 删除动态
DeleteTweet func(Delete, web.DeleteTweetReq) `mir:"/post"` DeleteTweet func(Delete, web.DeleteTweetReq) `mir:"/post"`

Loading…
Cancel
Save