merge from r/paopao-ce-pro branch

r/paopao-ce-xtra
Michael Li 2 years ago
commit 781aba41ad
No known key found for this signature in database

@ -30,51 +30,123 @@ All notable changes to paopao-ce are documented in this file.
# 模块开启
VITE_ENABLE_FRIENDS_BAR=true
```
- add Newest/Hots/Following tweets support in friend bar feature.
- add Newest/Hots/Following tweets support in friend bar feature.
mirgration database first(sql ddl file in `scripts/migration/**/*_home_timeline.up.sql`):
```sql
CREATE TABLE `p_post_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint unsigned NOT NULL,
`rank_score` bigint unsigned NOT NULL DEFAULT 0,
`incentive_score` int unsigned NOT NULL DEFAULT 0,
`decay_factor` int unsigned NOT NULL DEFAULT 0,
`motivation_factor` int unsigned NOT NULL DEFAULT 0,
`is_del` tinyint NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
`created_on` bigint unsigned NOT NULL DEFAULT '0',
`modified_on` bigint unsigned NOT NULL DEFAULT '0',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post_metric_post_id_rank_score` (`post_id`,`rank_score`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_post_metric (post_id, rank_score, created_on)
SELECT id AS post_id,
comment_count + upvote_count*2 + collection_count*4 AS rank_score,
created_on
FROM p_post
WHERE is_del=0;
-- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
-- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
UPDATE p_post a
SET visibility = (
SELECT
CASE visibility
WHEN 0 THEN 90
WHEN 1 THEN 0
WHEN 2 THEN 50
WHEN 3 THEN 60
ELSE 0
END
FROM
p_post b
WHERE
a.ID = b.ID
);
```sql
- add cache support for index/home etc. page.
- add hots comments support for post detail page.
```sql
CREATE TABLE `p_post_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`post_id` bigint unsigned NOT NULL,
`rank_score` bigint unsigned NOT NULL DEFAULT 0,
`incentive_score` int unsigned NOT NULL DEFAULT 0,
`decay_factor` int unsigned NOT NULL DEFAULT 0,
`motivation_factor` int unsigned NOT NULL DEFAULT 0,
`is_del` tinyint NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
`created_on` bigint unsigned NOT NULL DEFAULT '0',
`modified_on` bigint unsigned NOT NULL DEFAULT '0',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post_metric_post_id_rank_score` (`post_id`,`rank_score`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_post_metric (post_id, rank_score, created_on)
SELECT id AS post_id,
comment_count + upvote_count*2 + collection_count*4 AS rank_score,
created_on
FROM p_post
WHERE is_del=0;
-- 原来的可见性: 0公开 1私密 2好友可见 3关注可见
-- 现在的可见性: 0私密 10充电可见 20订阅可见 30保留 40保留 50好友可见 60关注可见 70保留 80保留 90公开
UPDATE p_post a, p_post b
SET a.visibility = (
CASE b.visibility
WHEN 0 THEN 90
WHEN 1 THEN 0
WHEN 2 THEN 50
WHEN 3 THEN 60
ELSE 0
END
)
WHERE a.ID = b.ID;
```
- add cache support for index/home etc. page.
- add hots comments support for post detail page.
- add highlight comments support for post detail page.
mirgration database first(sql ddl file in `scripts/migration/**/*_comment_esence.up.sql`):
```sql
ALTER TABLE `p_comment` ADD COLUMN `is_essence` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否精选';
```
- add follow/unfollow user support in index/home/collecion/message/post page.
- add simple prometheus metrics support.
add `Metrics` to `conf.yaml` 's `Features` section to enable this feature like below:
```yaml
# file config.yaml
...
Features:
Default: ["Base", "Postgres", "Meili", "LocalOSS", "Metrics", "web"]
JobManager: # Cron Job理器的配置参数
MaxOnlineInterval: "@every 5m" # 更新最大在线人数默认每5分钟更新一次
UpdateMetricsInterval: "@every 5m" # 更新Prometheus指标默认每5分钟更新一次
MetricsServer: # Prometheus Metrics服务
RunMode: debug
HttpIp: 0.0.0.0
HttpPort: 6080
ReadTimeout: 60
WriteTimeout: 60
...
```
- add full support for tweet hots comment logic and add cache support for tweet comments.
mirgration database first(sql ddl file in `scripts/migration/**/*_rank_metrics.up.sql`):
```sql
ALTER TABLE `p_comment` ADD COLUMN `reply_count` int unsigned NOT NULL DEFAULT 0 COMMENT '回复数';
UPDATE p_comment comment
SET reply_count = (
SELECT count(*) FROM p_comment_reply reply WHERE reply.comment_id=comment.id AND reply.is_del=0
)
WHERE is_del=0;
CREATE TABLE `p_comment_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comment_id` bigint unsigned NOT NULL,
`rank_score` bigint unsigned NOT NULL DEFAULT 0,
`incentive_score` int unsigned NOT NULL DEFAULT 0,
`decay_factor` int unsigned NOT NULL DEFAULT 0,
`motivation_factor` int unsigned NOT NULL DEFAULT 0,
`is_del` tinyint NOT NULL DEFAULT 0,
`created_on` bigint unsigned NOT NULL DEFAULT 0,
`modified_on` bigint unsigned NOT NULL DEFAULT 0,
`deleted_on` bigint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_comment_metric_comment_id_rank_score` (`comment_id`, `rank_score`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_comment_metric (comment_id, rank_score, created_on)
SELECT id AS comment_id,
reply_count*2 + thumbs_up_count*4 - thumbs_down_count AS rank_score,
created_on
FROM p_comment
WHERE is_del=0;
CREATE TABLE `p_user_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`tweets_count` int unsigned NOT NULL DEFAULT 0,
`latest_trends_on` bigint unsigned NOT NULL DEFAULT 0 COMMENT '最新动态时间',
`is_del` tinyint NOT NULL DEFAULT 0,
`created_on` bigint unsigned NOT NULL DEFAULT 0,
`modified_on` bigint unsigned NOT NULL DEFAULT 0,
`deleted_on` bigint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user_metric_user_id_tweets_count_trends` (`user_id`, `tweets_count`, `latest_trends_on`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_user_metric (user_id, tweets_count)
SELECT user_id, count(*) AS tweets_count
FROM p_post
WHERE is_del=0
GROUP BY user_id;
```
## 0.4.2
### Fixed

@ -18,6 +18,7 @@ type Loose interface {
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, mir.Error)
TweetComments(*web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error)
TopicList(*web.TopicListReq) (*web.TopicListResp, mir.Error)
GetUserProfile(*web.GetUserProfileReq) (*web.GetUserProfileResp, mir.Error)
@ -35,6 +36,20 @@ func RegisterLooseServant(e *gin.Engine, s Loose) {
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "/post", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.TweetDetailReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.TweetDetail(req)
s.Render(c, resp, err)
})
router.Handle("GET", "/post/comments", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -47,7 +62,12 @@ func RegisterLooseServant(e *gin.Engine, s Loose) {
return
}
resp, err := s.TweetComments(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", "/tags", func(c *gin.Context) {
select {
@ -125,6 +145,10 @@ func (UnimplementedLooseServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedLooseServant) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedLooseServant) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -27,6 +27,7 @@ type Priv interface {
ThumbsUpTweetComment(*web.TweetCommentThumbsReq) mir.Error
DeleteCommentReply(*web.DeleteCommentReplyReq) mir.Error
CreateCommentReply(*web.CreateCommentReplyReq) (*web.CreateCommentReplyResp, mir.Error)
HighlightComment(*web.HighlightCommentReq) (*web.HighlightCommentResp, mir.Error)
DeleteComment(*web.DeleteCommentReq) mir.Error
CreateComment(*web.CreateCommentReq) (*web.CreateCommentResp, mir.Error)
VisibleTweet(*web.VisibleTweetReq) (*web.VisibleTweetResp, mir.Error)
@ -184,6 +185,20 @@ func RegisterPrivServant(e *gin.Engine, s Priv, m ...PrivChain) {
resp, err := s.CreateCommentReply(req)
s.Render(c, resp, err)
})
router.Handle("POST", "/post/comment/highlight", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.HighlightCommentReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.HighlightComment(req)
s.Render(c, resp, err)
})
router.Handle("DELETE", "/post/comment", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -419,6 +434,10 @@ func (UnimplementedPrivServant) CreateCommentReply(req *web.CreateCommentReplyRe
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) HighlightComment(req *web.HighlightCommentReq) (*web.HighlightCommentResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPrivServant) DeleteComment(req *web.DeleteCommentReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -15,7 +15,6 @@ import (
type Pub interface {
_default_
TweetDetail(*web.TweetDetailReq) (*web.TweetDetailResp, mir.Error)
SendCaptcha(*web.SendCaptchaReq) mir.Error
GetCaptcha() (*web.GetCaptchaResp, mir.Error)
Register(*web.RegisterReq) (*web.RegisterResp, mir.Error)
@ -30,20 +29,6 @@ func RegisterPubServant(e *gin.Engine, s Pub) {
router := e.Group("v1")
// register routes info to router
router.Handle("GET", "/post", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.TweetDetailReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.TweetDetail(req)
s.Render(c, resp, err)
})
router.Handle("POST", "/captcha", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
@ -110,10 +95,6 @@ func RegisterPubServant(e *gin.Engine, s Pub) {
// UnimplementedPubServant can be embedded to have forward compatible implementations.
type UnimplementedPubServant struct{}
func (UnimplementedPubServant) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedPubServant) SendCaptcha(req *web.SendCaptchaReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -2,8 +2,11 @@ module github.com/rocboss/paopao-ce
go 1.21
toolchain go1.21.1
require (
github.com/Masterminds/semver/v3 v3.2.1
github.com/RoaringBitmap/roaring v1.5.0
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868
github.com/alimy/mir/v4 v4.0.0
github.com/alimy/tryst v0.8.3
@ -29,8 +32,9 @@ require (
github.com/json-iterator/go v1.1.12
github.com/meilisearch/meilisearch-go v0.25.1
github.com/minio/minio-go/v7 v7.0.63
github.com/onsi/ginkgo/v2 v2.12.0
github.com/onsi/ginkgo/v2 v2.12.1
github.com/onsi/gomega v1.27.10
github.com/prometheus/client_golang v1.16.0
github.com/pyroscope-io/client v0.7.2
github.com/redis/rueidis v1.0.18
github.com/robfig/cron/v3 v3.0.1
@ -39,11 +43,11 @@ require (
github.com/sourcegraph/conc v0.3.0
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0
github.com/sqlc-dev/sqlc v1.20.0
github.com/sqlc-dev/sqlc v1.21.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.43
github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc
go.uber.org/automaxprocs v1.5.3
google.golang.org/grpc v1.58.1
google.golang.org/grpc v1.58.2
google.golang.org/protobuf v1.31.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/resty.v1 v1.12.0
@ -59,8 +63,11 @@ require (
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
github.com/benbjohnson/clock v1.1.0 // indirect
github.com/bytecodealliance/wasmtime-go/v8 v8.0.0 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.2.0 // indirect
github.com/bytecodealliance/wasmtime-go/v12 v12.0.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/clbanning/mxj v1.8.4 // indirect
@ -70,6 +77,7 @@ require (
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
@ -83,7 +91,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/cel-go v0.17.1 // indirect
github.com/google/cel-go v0.18.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
@ -110,18 +118,24 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/mschoch/smat v0.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pganalyze/pg_query_go/v4 v4.2.1 // indirect
github.com/pganalyze/pg_query_go/v4 v4.2.3 // indirect
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 // indirect
github.com/pingcap/log v0.0.0-20210906054005-afc726e70354 // indirect
github.com/pingcap/tidb/parser v0.0.0-20220725134311-c80026e61f00 // indirect
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c // indirect
github.com/pingcap/log v1.1.0 // indirect
github.com/pingcap/tidb/parser v0.0.0-20230815160630-b69fa21942d1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.10.1 // indirect
github.com/pyroscope-io/godeltaprof v0.1.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/riza-io/grpc-go v0.2.0 // indirect
@ -140,12 +154,12 @@ require (
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.40.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/zap v1.21.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.25.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.13.0 // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/exp v0.0.0-20230724220655-d98519c11495 // indirect
golang.org/x/image v0.0.0-20210216034530-4410531fe030 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/net v0.15.0 // indirect
@ -154,8 +168,8 @@ require (
golang.org/x/text v0.13.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.12.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect

@ -112,6 +112,8 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
github.com/RoaringBitmap/roaring v1.5.0 h1:V0VCSiHjroItEYCM3guC8T83ehi5QMt3oM9EefTTOms=
github.com/RoaringBitmap/roaring v1.5.0/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868 h1:uFrPOl1VBt/Abfl2z+A/DFc+AwmFLxEHR1+Yq6cXvww=
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868/go.mod h1:srphKZ1i+yGXxl/LpBS7ZIECTjCTPzZzAMtJWoG3sLo=
@ -175,17 +177,20 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.7.2/go.mod h1:8EzeIqfWt2wWT4rJVu3f21
github.com/aws/smithy-go v1.7.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E=
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o=
github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitbus/sqlx v1.8.0 h1:2mhQscUW9Qc95gUONNswkuDaJiluVUfmBP4yw2cSK7M=
github.com/bitbus/sqlx v1.8.0/go.mod h1:MemKLfQ600g6PxUVsIDe48PlY3wOquyW2ApeiXoynFo=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
@ -201,8 +206,8 @@ github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx2
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bytecodealliance/wasmtime-go/v8 v8.0.0 h1:jP4sqm2PHgm3+eQ50zCoCdIyQFkIL/Rtkw6TT8OYPFI=
github.com/bytecodealliance/wasmtime-go/v8 v8.0.0/go.mod h1:tgazNLU7xSC2gfRAM8L4WyE+dgs5yp9FF5/tGebEQyM=
github.com/bytecodealliance/wasmtime-go/v12 v12.0.0 h1:Wga02UaZXYF3p0LIeL5xFp09/RI7UhjfT2uB0mLrwlw=
github.com/bytecodealliance/wasmtime-go/v12 v12.0.0/go.mod h1:a3PRoftJxxUzkQvgjC6sv7pKyJJK0ZsFVmH+eeEKQC4=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
@ -216,6 +221,8 @@ github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
@ -450,6 +457,8 @@ github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQL
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@ -649,8 +658,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.17.1 h1:s2151PDGy/eqpCI80/8dl4VL3xTkqI/YubXLXCFw0mw=
github.com/google/cel-go v0.17.1/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/cel-go v0.18.0 h1:u74MPiEC8mejBrkXqrTWT102g5IFEUjxOngzQIijMzU=
github.com/google/cel-go v0.18.0/go.mod h1:PVAybmSnWkNMUZR/tEWFUiJ1Np4Hz0MHsZJcgC4zln4=
github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@ -957,6 +966,8 @@ github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY=
github.com/meilisearch/meilisearch-go v0.25.1 h1:D5wY22sn5kkpRH3uYMGlwltdUEq5regIFmO7awHz3Vo=
github.com/meilisearch/meilisearch-go v0.25.1/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
@ -1007,6 +1018,8 @@ github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7P
github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mutecomm/go-sqlcipher/v4 v4.4.0/go.mod h1:PyN04SaWalavxRGH9E8ZftG6Ju7rsPrGmQRjrEaVpiY=
@ -1033,8 +1046,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
github.com/onsi/ginkgo/v2 v2.12.1 h1:uHNEO1RP2SpuZApSkel9nEh1/Mu+hmQe7Q+Pepg5OYA=
github.com/onsi/ginkgo/v2 v2.12.1/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o=
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@ -1085,19 +1098,22 @@ github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZO
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pganalyze/pg_query_go/v4 v4.2.1 h1:id/vuyIQccb9f6Yx3pzH5l4QYrxE3v6/m8RPlgMrprc=
github.com/pganalyze/pg_query_go/v4 v4.2.1/go.mod h1:aEkDNOXNM5j0YGzaAapwJ7LB3dLNj+bvbWcLv1hOVqA=
github.com/pganalyze/pg_query_go/v4 v4.2.3 h1:cNLqyiVMasV7YGWyYV+fkXyHp32gDfXVNCqoHztEGNk=
github.com/pganalyze/pg_query_go/v4 v4.2.3/go.mod h1:aEkDNOXNM5j0YGzaAapwJ7LB3dLNj+bvbWcLv1hOVqA=
github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY=
github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.8/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63 h1:+FZIDR/D97YOPik4N4lPDaUcLDF/EQPogxtlHB2ZZRM=
github.com/pingcap/errors v0.11.5-0.20210425183316-da1aaba5fb63/go.mod h1:X2r9ueLEUZgtx2cIogM0v4Zj5uvvzhuuiu7Pn8HzMPg=
github.com/pingcap/log v0.0.0-20210906054005-afc726e70354 h1:SvWCbCPh1YeHd9yQLksvJYAgft6wLTY1aNG81tpyscQ=
github.com/pingcap/log v0.0.0-20210906054005-afc726e70354/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4=
github.com/pingcap/tidb/parser v0.0.0-20220725134311-c80026e61f00 h1:aDC/yAGx/jPEyrX+UPKV3GWg+4A4yG8ifuP6jBEhDi0=
github.com/pingcap/tidb/parser v0.0.0-20220725134311-c80026e61f00/go.mod h1:wjvp+T3/T9XYt0nKqGX3Kc1AKuyUcfno6LTc6b2A6ew=
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c h1:CgbKAHto5CQgWM9fSBIvaxsJHuGP0uM74HXtv3MyyGQ=
github.com/pingcap/failpoint v0.0.0-20220801062533-2eaa32854a6c/go.mod h1:4qGtCB0QK0wBzKtFEGDhxXnSnbQApw1gc9siScUl8ew=
github.com/pingcap/log v1.1.0 h1:ELiPxACz7vdo1qAvvaWJg1NrYFoY6gqAh/+Uo6aXdD8=
github.com/pingcap/log v1.1.0/go.mod h1:DWQW5jICDR7UJh4HtxXSM20Churx4CQL0fwL/SoOSA4=
github.com/pingcap/tidb/parser v0.0.0-20230815160630-b69fa21942d1 h1:FvX5fDJ32eblK9f6KJIOVujjtu3FDEfcRGHsEDfJ4kI=
github.com/pingcap/tidb/parser v0.0.0-20230815160630-b69fa21942d1/go.mod h1:pWA6mNa/o7UTDKrg+4H75NdpRgpWRTox/cqQjaQ4ZBU=
github.com/pkg/browser v0.0.0-20210706143420-7d21f8c997e2/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@ -1121,11 +1137,15 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc=
github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@ -1134,6 +1154,8 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@ -1146,6 +1168,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/pyroscope-io/client v0.7.2 h1:OX2qdUQsS8RSkn/3C8isD7f/P0YiZQlRbAlecAaj/R8=
github.com/pyroscope-io/client v0.7.2/go.mod h1:FEocnjn+Ngzxy6EtU9ZxXWRvQ0+pffkrBxHLnPpxwi8=
@ -1188,6 +1212,7 @@ github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@ -1248,8 +1273,8 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
github.com/sqlc-dev/sqlc v1.20.0 h1:teJJBBN9PTKLhO6IFsyhsP6JD4mD0/Db73eLOyfgrgw=
github.com/sqlc-dev/sqlc v1.20.0/go.mod h1:txy7rfJ+rgUwThYp+d1+/vONbPgR34OgVCRdRYJcoWI=
github.com/sqlc-dev/sqlc v1.21.0 h1:Shtux/GLJUSMtoJupSJNoFIQgfFRQHI0LIDkwvXIQp0=
github.com/sqlc-dev/sqlc v1.21.0/go.mod h1:fHPNlsaUckfRQaHNl/hat4VwsPN3ZJZe+V1fAQoGf/Y=
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU=
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
@ -1270,8 +1295,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
@ -1384,27 +1410,28 @@ go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@ -1452,8 +1479,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/exp v0.0.0-20230724220655-d98519c11495 h1:zKGKw2WlGb8oPoRGqQ2PT8g2YoCN1w/YbbQjHXCdUWE=
golang.org/x/exp v0.0.0-20230724220655-d98519c11495/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
@ -1972,10 +1999,10 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220111164026-67b88f271998/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw=
google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44=
google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5 h1:eSaPbMR4T7WfH9FvABk36NBMacoTUKdWCvV0dx+KfOg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230803162519-f966b187b2e5/go.mod h1:zBEcrKX2ZOcEkHWxBPAIvYUWOKKMIhYcmNiUIu2ji3I=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@ -2009,8 +2036,8 @@ google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.58.1 h1:OL+Vz23DTtrrldqHK49FUOPHyY75rvFqJfXC84NYW58=
google.golang.org/grpc v1.58.1/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I=
google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@ -2064,7 +2091,6 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -21,16 +21,16 @@ func MustAlipayClient() *alipay.Client {
logrus.Fatalf("alipay.New err: %s", err)
}
// 加载应用公钥证书
if err = client.LoadAppPublicCertFromFile(s.AppPublicCertFile); err != nil {
logrus.Fatalf("client.LoadAppPublicCertFromFile err: %s\n", err)
if err = client.LoadAppCertPublicKeyFromFile(s.AppPublicCertFile); err != nil {
logrus.Fatalf("client.LoadAppCertPublicKeyFromFile err: %s\n", err)
}
// 加载支付宝根证书
if err = client.LoadAliPayRootCertFromFile(s.RootCertFile); err != nil {
logrus.Fatalf("client.LoadAliPayRootCertFromFile err: %s\n", err)
}
// 加载支付宝公钥证书
if err = client.LoadAliPayPublicCertFromFile(s.PublicCertFile); err != nil {
logrus.Fatalf("client.LoadAliPayPublicCertFromFile err: %s\n", err)
if err = client.LoadAlipayCertPublicKeyFromFile(s.PublicCertFile); err != nil {
logrus.Fatalf("client.LoadAlipayCertPublicKeyFromFile err: %s\n", err)
}
_alipayClient = client
})

@ -17,6 +17,9 @@ const (
// 以下包含一些在cache中会用到的key的前缀
const (
InfixCommentDefault = "default"
InfixCommentHots = "hots"
InfixCommentNewest = "newest"
PrefixNewestTweets = "paopao:newesttweets:"
PrefixHotsTweets = "paopao:hotstweets:"
PrefixFollowingTweets = "paopao:followingtweets:"
@ -29,6 +32,9 @@ const (
PrefixUserInfo = "paopao:userinfo:"
PrefixUserInfoById = "paopao:userinfo:id:"
PrefixUserInfoByName = "paopao:userinfo:name:"
PrefixMyFriendIds = "paopao:myfriendids:"
PrefixMyFollowIds = "paopao:myfollowids:"
PrefixTweetComment = "paopao:comment:"
KeySiteStatus = "paopao:sitestatus"
KeyHistoryMaxOnline = "history.max.online"
)
@ -42,6 +48,8 @@ var (
KeyOnlineUser cache.KeyPool[int64]
KeyUserInfoById cache.KeyPool[int64]
KeyUserInfoByName cache.KeyPool[string]
KeyMyFriendIds cache.KeyPool[int64]
KeyMyFollowIds cache.KeyPool[int64]
)
func initCacheKeyPool() {
@ -56,6 +64,8 @@ func initCacheKeyPool() {
KeyOnlineUser = intKeyPool[int64](poolSize, PrefixOnlineUser)
KeyUserInfoById = intKeyPool[int64](poolSize, PrefixUserInfoById)
KeyUserInfoByName = strKeyPool(poolSize, PrefixUserInfoById)
KeyMyFriendIds = intKeyPool[int64](poolSize, PrefixMyFriendIds)
KeyMyFollowIds = intKeyPool[int64](poolSize, PrefixMyFollowIds)
}
func strKeyPool(size int, prefix string) cache.KeyPool[string] {

@ -26,6 +26,7 @@ var (
PostgresSetting *postgresConf
Sqlite3Setting *sqlite3Conf
PprofServerSetting *httpServerConf
MetricsServerSetting *httpServerConf
WebServerSetting *httpServerConf
AdminServerSetting *httpServerConf
SpaceXServerSetting *httpServerConf
@ -78,6 +79,7 @@ func setupSetting(suite []string, noDefault bool) error {
"MetricManager": &MetricManagerSetting,
"JobManager": &JobManagerSetting,
"PprofServer": &PprofServerSetting,
"MetricsServer": &MetricsServerSetting,
"WebServer": &WebServerSetting,
"AdminServer": &AdminServerSetting,
"SpaceXServer": &SpaceXServerSetting,

@ -11,8 +11,10 @@ Cache:
UnreadMsgExpire: 60 # 未读消息过期时间,单位秒, 默认60s
UserTweetsExpire: 60 # 获取用户推文列表过期时间,单位秒, 默认60s
IndexTweetsExpire: 120 # 获取广场推文列表过期时间,单位秒, 默认120s
TweetCommentsExpire: 180 # 获取推文评论过期时间,单位秒, 默认180s
OnlineUserExpire: 300 # 标记在线用户 过期时间,单位秒, 默认300s
UserInfoExpire: 300 # 获取用户信息过期时间,单位秒, 默认300s
UserRelationExpire: 600 # 用户关系信息过期时间,单位秒, 默认600s
EventManager: # 事件管理器的配置参数
MinWorker: 64 # 最小后台工作者, 设置范围[5, ++], 默认64
MaxEventBuf: 128 # 最大log缓存条数, 设置范围[10, ++], 默认128
@ -26,7 +28,8 @@ MetricManager: # 指标监控管理器的配置参数
MaxTickCount: 60 # 最大的循环周期, 设置范围[60, ++], 默认60
TickWaitTime: 1 # 一个周期的等待时间,单位:秒 默认1s
JobManager: # Cron Job理器的配置参数
MaxOnlineInterval: "@every 5m" # 更新最大在线人数默认每5分钟更新一次
MaxOnlineInterval: "@every 5m" # 更新最大在线人数默认每5分钟更新一次
UpdateMetricsInterval: "@every 5m" # 更新Prometheus指标默认每5分钟更新一次
Features:
Default: []
WebServer: # Web服务
@ -65,6 +68,12 @@ PprofServer: # Pprof服务
HttpPort: 6060
ReadTimeout: 60
WriteTimeout: 60
MetricsServer: # Prometheus Metrics服务
RunMode: debug
HttpIp: 0.0.0.0
HttpPort: 6080
ReadTimeout: 60
WriteTimeout: 60
FrontendWebServer: # Web前端静态资源服务
RunMode: debug
HttpIp: 0.0.0.0

@ -23,6 +23,7 @@ const (
TableAttachment = "attachment"
TableCaptcha = "captcha"
TableComment = "comment"
TableCommentMetric = "comment_metric"
TableCommentContent = "comment_content"
TableCommentReply = "comment_reply"
TableFollowing = "following"
@ -39,6 +40,7 @@ const (
TablePostStar = "post_star"
TableTag = "tag"
TableUser = "user"
TableUserMetric = "user_metric"
TableWalletRecharge = "wallet_recharge"
TableWalletStatement = "wallet_statement"
)

@ -102,8 +102,10 @@ type cacheConf struct {
UnreadMsgExpire int64
UserTweetsExpire int64
IndexTweetsExpire int64
TweetCommentsExpire int64
OnlineUserExpire int64
UserInfoExpire int64
UserRelationExpire int64
}
type eventManagerConf struct {
@ -123,7 +125,8 @@ type metricManagerConf struct {
}
type jobManagerConf struct {
MaxOnlineInterval string
MaxOnlineInterval string
UpdateMetricsInterval string
}
type cacheIndexConf struct {
@ -344,6 +347,7 @@ func (s *databaseConf) TableNames() (res TableNameMap) {
TableAttachment,
TableCaptcha,
TableComment,
TableCommentMetric,
TableCommentContent,
TableCommentReply,
TableFollowing,
@ -360,6 +364,7 @@ func (s *databaseConf) TableNames() (res TableNameMap) {
TablePostStar,
TableTag,
TableUser,
TableUserMetric,
TableWalletRecharge,
TableWalletStatement,
}

@ -21,6 +21,7 @@ type CommentService interface {
// CommentManageService 评论管理服务
type CommentManageService interface {
HighlightComment(userId, commentId int64) (int8, error)
DeleteComment(comment *ms.Comment) error
CreateComment(comment *ms.Comment) (*ms.Comment, error)
CreateCommentReply(reply *ms.CommentReply) (*ms.CommentReply, error)

@ -15,9 +15,6 @@ type DataService interface {
// 话题服务
TopicService
// 广场泡泡服务
// IndexPostsService
// 推文服务
TweetService
TweetManageService
@ -25,6 +22,7 @@ type DataService interface {
// 推文指标服务
TweetMetricServantA
CommentMetricServantA
// 评论服务
CommentService
@ -34,6 +32,7 @@ type DataService interface {
UserManageService
ContactManageService
FollowingManageService
UserRelationService
// 安全服务
SecurityService

@ -13,8 +13,15 @@ type TweetMetric struct {
UpvoteCount int64
CollectionCount int64
ShareCount int64
ThumbdownCount int64
ThumbupCount int64
ThumbsUpCount int64
ThumbsDownCount int64
}
type CommentMetric struct {
CommentId int64
ReplyCount int32
ThumbsUpCount int32
ThumbsDownCount int32
}
func (m *TweetMetric) RankScore(motivationFactor int) int64 {
@ -23,3 +30,10 @@ func (m *TweetMetric) RankScore(motivationFactor int) int64 {
}
return (m.CommentCount + m.UpvoteCount*2 + m.CollectionCount*4 + m.ShareCount*8) * int64(motivationFactor)
}
func (m *CommentMetric) RankScore(motivationFactor int) int64 {
if motivationFactor == 0 {
motivationFactor = 1
}
return int64(m.ReplyCount*2+m.ThumbsUpCount*4-m.ThumbsDownCount) * int64(motivationFactor)
}

@ -9,7 +9,19 @@ import (
)
type TweetMetricServantA interface {
UpdateRankScore(metric *cs.TweetMetric) error
UpdateTweetMetric(metric *cs.TweetMetric) error
AddTweetMetric(postId int64) error
DeleteTweetMetric(postId int64) error
}
type CommentMetricServantA interface {
UpdateCommentMetric(metric *cs.CommentMetric) error
AddCommentMetric(commentId int64) error
DeleteCommentMetric(commentId int64) error
}
type UserMetricServantA interface {
UpdateUserMetric(userId int64, action uint8) error
AddUserMetric(userId int64) error
DeleteUserMetric(userId int64) error
}

@ -37,3 +37,11 @@ type FollowingManageService interface {
GetFollowCount(userId int64) (int64, int64, error)
IsFollow(userId int64, followId int64) bool
}
// UserRelationService 用户关系服务
type UserRelationService interface {
MyFriendIds(userId int64) ([]int64, error)
MyFollowIds(userId int64) ([]int64, error)
IsMyFriend(userId int64, friendIds ...int64) (map[int64]bool, error)
IsMyFollow(userId int64, followIds ...int64) (map[int64]bool, error)
}

@ -8,6 +8,7 @@ import (
"bytes"
"encoding/gob"
"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/ms"
@ -59,3 +60,47 @@ func (s *cacheDataService) GetUserByUsername(username string) (res *ms.User, err
}
return
}
func (s *cacheDataService) IsMyFriend(userId int64, friendIds ...int64) (res map[int64]bool, err error) {
size := len(friendIds)
res = make(map[int64]bool, size)
if size == 0 {
return
}
// 从缓存中获取
key := conf.KeyMyFriendIds.Get(userId)
if data, xerr := s.ac.Get(key); xerr == nil {
bitmap := roaring64.New()
if err = bitmap.UnmarshalBinary(data); err == nil {
for _, friendId := range friendIds {
res[friendId] = bitmap.Contains(uint64(friendId))
}
return
}
}
// 直接查库并触发缓存更新事件
OnCacheMyFriendIdsEvent(s.DataService, userId)
return s.DataService.IsMyFriend(userId, friendIds...)
}
func (s *cacheDataService) IsMyFollow(userId int64, followIds ...int64) (res map[int64]bool, err error) {
size := len(followIds)
res = make(map[int64]bool, size)
if size == 0 {
return
}
// 从缓存中获取
key := conf.KeyMyFollowIds.Get(userId)
if data, xerr := s.ac.Get(key); xerr == nil {
bitmap := roaring64.New()
if err = bitmap.UnmarshalBinary(data); err == nil {
for _, followId := range followIds {
res[followId] = bitmap.Contains(uint64(followId))
}
return
}
}
// 直接查库并触发缓存更新事件
OnCacheMyFollowIdsEvent(s.DataService, userId, key)
return s.DataService.IsMyFollow(userId, followIds...)
}

@ -9,23 +9,23 @@ import (
"encoding/gob"
"fmt"
"github.com/RoaringBitmap/roaring/roaring64"
"github.com/alimy/tryst/event"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/sirupsen/logrus"
)
type expireIndexTweetsEvent struct {
event.UnimplementedEvent
tweet *ms.Post
ac core.AppCache
keysPattern []string
}
type expireHotsTweetsEvent struct {
event.UnimplementedEvent
tweet *ms.Post
ac core.AppCache
keyPattern string
}
@ -46,21 +46,38 @@ type cacheUserInfoEvent struct {
expire int64
}
func onExpireIndexTweetEvent(tweet *ms.Post) {
type cacheMyFriendIdsEvent struct {
event.UnimplementedEvent
ac core.AppCache
urs core.UserRelationService
userIds []int64
expire int64
}
type cacheMyFollowIdsEvent struct {
event.UnimplementedEvent
ac core.AppCache
urs core.UserRelationService
userId int64
key string
expire int64
}
func OnExpireIndexTweetEvent(userId int64) {
// TODO: 这里暴躁的将所有 最新/热门/关注 的推文列表缓存都过期掉,后续需要更精细话处理
events.OnEvent(&expireIndexTweetsEvent{
tweet: tweet,
ac: _appCache,
ac: _appCache,
keysPattern: []string{
conf.PrefixIdxTweetsNewest + "*",
conf.PrefixIdxTweetsHots + "*",
fmt.Sprintf("%s%d:*", conf.PrefixUserTweets, tweet.UserID),
conf.PrefixIdxTweetsFollowing + "*",
fmt.Sprintf("%s%d:*", conf.PrefixUserTweets, userId),
},
})
}
func onExpireHotsTweetEvent(tweet *ms.Post) {
func OnExpireHotsTweetEvent() {
events.OnEvent(&expireHotsTweetsEvent{
tweet: tweet,
ac: _appCache,
keyPattern: conf.PrefixHotsTweets + "*",
})
@ -83,6 +100,34 @@ func onCacheUserInfoEvent(key string, data *ms.User) {
})
}
func OnCacheMyFriendIdsEvent(urs core.UserRelationService, userIds ...int64) {
if len(userIds) == 0 {
return
}
events.OnEvent(&cacheMyFriendIdsEvent{
userIds: userIds,
urs: urs,
ac: _appCache,
expire: conf.CacheSetting.UserRelationExpire,
})
}
func OnCacheMyFollowIdsEvent(urs core.UserRelationService, userId int64, key ...string) {
cacheKey := ""
if len(key) > 0 {
cacheKey = key[0]
} else {
cacheKey = conf.KeyMyFollowIds.Get(userId)
}
events.OnEvent(&cacheMyFollowIdsEvent{
userId: userId,
urs: urs,
key: cacheKey,
ac: _appCache,
expire: conf.CacheSetting.UserRelationExpire,
})
}
func (e *expireIndexTweetsEvent) Name() string {
return "expireIndexTweetsEvent"
}
@ -127,3 +172,49 @@ func (e *cacheUserInfoEvent) Action() (err error) {
}
return
}
func (e *cacheMyFriendIdsEvent) Name() string {
return "cacheMyFriendIdsEvent"
}
func (e *cacheMyFriendIdsEvent) Action() error {
logrus.Debug("cacheMyFriendIdsEvent action runnging")
for _, userId := range e.userIds {
myFriendIds, err := e.urs.MyFriendIds(userId)
if err != nil {
return err
}
bitmap := roaring64.New()
for _, friendId := range myFriendIds {
bitmap.Add(uint64(friendId))
}
data, err := bitmap.MarshalBinary()
if err != nil {
return err
}
e.ac.Set(conf.KeyMyFriendIds.Get(userId), data, e.expire)
}
return nil
}
func (e *cacheMyFollowIdsEvent) Name() string {
return "cacheMyFollowIdsEvent"
}
func (e *cacheMyFollowIdsEvent) Action() (err error) {
logrus.Debug("cacheMyFollowIdsEvent action runnging")
myFollowIds, err := e.urs.MyFollowIds(e.userId)
if err != nil {
return err
}
bitmap := roaring64.New()
for _, followId := range myFollowIds {
bitmap.Add(uint64(followId))
}
data, err := bitmap.MarshalBinary()
if err != nil {
return err
}
e.ac.Set(e.key, data, e.expire)
return nil
}

@ -19,22 +19,22 @@ func (s *eventCacheIndexSrv) SendAction(act core.IdxAct, post *ms.Post) {
err := error(nil)
switch act {
case core.IdxActUpdatePost:
err = s.tms.UpdateRankScore(&cs.TweetMetric{
err = s.tms.UpdateTweetMetric(&cs.TweetMetric{
PostId: post.ID,
CommentCount: post.CommentCount,
UpvoteCount: post.UpvoteCount,
CollectionCount: post.CollectionCount,
ShareCount: post.ShareCount,
})
onExpireIndexTweetEvent(post)
OnExpireIndexTweetEvent(post.UserID)
case core.IdxActCreatePost:
err = s.tms.AddTweetMetric(post.ID)
onExpireIndexTweetEvent(post)
OnExpireIndexTweetEvent(post.UserID)
case core.IdxActDeletePost:
err = s.tms.DeleteTweetMetric(post.ID)
onExpireIndexTweetEvent(post)
OnExpireIndexTweetEvent(post.UserID)
case core.IdxActStickPost, core.IdxActVisiblePost:
onExpireIndexTweetEvent(post)
OnExpireIndexTweetEvent(post.UserID)
}
if err != nil {
logrus.Errorf("eventCacheIndexSrv.SendAction(%s) occurs error: %s", act, err)

@ -5,6 +5,7 @@
package jinzhu
import (
"fmt"
"time"
"github.com/rocboss/paopao-ce/internal/core"
@ -61,20 +62,21 @@ func (s *commentSrv) GetCommentThumbsMap(userId int64, tweetId int64) (cs.Commen
}
func (s *commentSrv) GetComments(tweetId int64, style cs.StyleCommentType, limit int, offset int) (res []*ms.Comment, total int64, err error) {
// TODO: 需要优化一下,更精细的调整评论排序策略
sort := "id ASC"
db := s.db.Table(_comment_)
sort := "is_essence DESC, id ASC"
switch style {
case cs.StyleCommentHots:
// TOOD: 暂时简单排序,后续需要根据 rank_score=评论回复数*2+点赞*4-点踩, order byrank_score DESC
sort = "thumbs_up_count DESC, id DESC"
// rank_score=评论回复数*2+点赞*4-点踩, order byrank_score DESC
db = db.Joins(fmt.Sprintf("LEFT JOIN %s m ON %s.id=m.comment_id AND m.is_del=0", _commentMetric_, _comment_))
sort = fmt.Sprintf("is_essence DESC, m.rank_score DESC, %s.id DESC", _comment_)
case cs.StyleCommentNewest:
sort = "id DESC"
sort = "is_essence DESC, id DESC"
case cs.StyleCommentDefault:
fallthrough
default:
// sort = "id ASC"
// nothing
}
db := s.db.Table(_comment_).Where("post_id=?", tweetId)
db = db.Where("post_id=?", tweetId)
if err = db.Count(&total).Error; err != nil {
return
}
@ -117,11 +119,9 @@ func (s *commentSrv) GetCommentRepliesByID(ids []int64) ([]*ms.CommentReplyForma
"comment_id IN ?": ids,
"ORDER": "id ASC",
}, 0, 0)
if err != nil {
return nil, err
}
userIds := []int64{}
for _, reply := range replies {
userIds = append(userIds, reply.UserID, reply.AtUserID)
@ -142,44 +142,61 @@ func (s *commentSrv) GetCommentRepliesByID(ids []int64) ([]*ms.CommentReplyForma
replyFormated.AtUser = user.Format()
}
}
repliesFormated = append(repliesFormated, replyFormated)
}
return repliesFormated, nil
}
func (s *commentManageSrv) DeleteComment(comment *ms.Comment) error {
func (s *commentManageSrv) HighlightComment(userId, commentId int64) (res int8, err error) {
post := &dbr.Post{}
comment := &dbr.Comment{}
db := s.db.Model(comment)
if err = db.Where("id=?", commentId).First(comment).Error; err != nil {
return
}
if err = s.db.Table(_post_).Where("id=?", comment.PostID).First(post).Error; err != nil {
return
}
if post.UserID != userId {
return 0, cs.ErrNoPermission
}
comment.IsEssence = 1 - comment.IsEssence
return comment.IsEssence, db.Save(comment).Error
}
func (s *commentManageSrv) DeleteComment(comment *ms.Comment) (err error) {
db := s.db.Begin()
defer db.Rollback()
err := comment.Delete(s.db)
if err != nil {
return err
if err = comment.Delete(db); err != nil {
return
}
err = db.Model(&dbr.TweetCommentThumbs{}).Where("user_id=? AND tweet_id=? AND comment_id=?", comment.UserID, comment.PostID, comment.ID).Updates(map[string]any{
"deleted_on": time.Now().Unix(),
"is_del": 1,
}).Error
if err != nil {
return err
return
}
db.Commit()
return nil
return
}
func (s *commentManageSrv) CreateComment(comment *ms.Comment) (*ms.Comment, error) {
return comment.Create(s.db)
}
func (s *commentManageSrv) CreateCommentReply(reply *ms.CommentReply) (*ms.CommentReply, error) {
return reply.Create(s.db)
func (s *commentManageSrv) CreateCommentReply(reply *ms.CommentReply) (res *ms.CommentReply, err error) {
if res, err = reply.Create(s.db); err == nil {
// 宽松处理错误
s.db.Table(_comment_).Where("id=?", reply.CommentID).Update("reply_count", gorm.Expr("reply_count+1"))
}
return
}
func (s *commentManageSrv) DeleteCommentReply(reply *ms.CommentReply) (err error) {
db := s.db.Begin()
defer db.Rollback()
err = reply.Delete(s.db)
if err != nil {
return
@ -192,6 +209,8 @@ func (s *commentManageSrv) DeleteCommentReply(reply *ms.CommentReply) (err error
if err != nil {
return
}
// 宽松处理错误
db.Table(_comment_).Where("id=?", reply.CommentID).Update("reply_count", gorm.Expr("reply_count-1"))
db.Commit()
return
}

@ -17,6 +17,8 @@ type Comment struct {
UserID int64 `json:"user_id"`
IP string `json:"ip"`
IPLoc string `json:"ip_loc"`
IsEssence int8 `json:"is_essense"`
ReplyCount int32 `json:"reply_count"`
ThumbsUpCount int32 `json:"thumbs_up_count"`
ThumbsDownCount int32 `json:"-"`
}
@ -29,7 +31,9 @@ type CommentFormated struct {
Contents []*CommentContent `json:"contents"`
Replies []*CommentReplyFormated `json:"replies"`
IPLoc string `json:"ip_loc"`
ReplyCount int32 `json:"reply_count"`
ThumbsUpCount int32 `json:"thumbs_up_count"`
IsEssence int8 `json:"is_essence"`
IsThumbsUp int8 `json:"is_thumbs_up"`
IsThumbsDown int8 `json:"is_thumbs_down"`
CreatedOn int64 `json:"created_on"`
@ -48,7 +52,9 @@ func (c *Comment) Format() *CommentFormated {
Contents: []*CommentContent{},
Replies: []*CommentReplyFormated{},
IPLoc: c.IPLoc,
ReplyCount: c.ReplyCount,
ThumbsUpCount: c.ThumbsUpCount,
IsEssence: c.IsEssence,
IsThumbsUp: types.No,
IsThumbsDown: types.No,
CreatedOn: c.CreatedOn,
@ -116,7 +122,6 @@ func (c *Comment) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
func (c *Comment) Create(db *gorm.DB) (*Comment, error) {
err := db.Create(&c).Error
return c, err
}

@ -19,13 +19,53 @@ type PostMetric struct {
MotivationFactor int
}
func (p *PostMetric) Create(db *gorm.DB) (*PostMetric, error) {
err := db.Create(&p).Error
return p, err
type CommentMetric struct {
*Model
CommentId int64
RankScore int64
IncentiveScore int
DecayFactor int
MotivationFactor int
}
type UserMetric struct {
*Model
UserId int64
TweetsCount int
LatestTrendsOn int64
}
func (m *PostMetric) Create(db *gorm.DB) (*PostMetric, error) {
err := db.Create(&m).Error
return m, err
}
func (m *PostMetric) Delete(db *gorm.DB) error {
return db.Model(m).Where("post_id", m.PostId).Updates(map[string]any{
"deleted_on": time.Now().Unix(),
"is_del": 1,
}).Error
}
func (m *CommentMetric) Create(db *gorm.DB) (*CommentMetric, error) {
err := db.Create(&m).Error
return m, err
}
func (m *CommentMetric) Delete(db *gorm.DB) error {
return db.Model(m).Where("comment_id", m.CommentId).Updates(map[string]any{
"deleted_on": time.Now().Unix(),
"is_del": 1,
}).Error
}
func (m *UserMetric) Create(db *gorm.DB) (*UserMetric, error) {
err := db.Create(&m).Error
return m, err
}
func (p *PostMetric) Delete(db *gorm.DB) error {
return db.Model(p).Where("post_id", p.PostId).Updates(map[string]any{
func (m *UserMetric) Delete(db *gorm.DB) error {
return db.Model(m).Where("user_id", m.UserId).Updates(map[string]any{
"deleted_on": time.Now().Unix(),
"is_del": 1,
}).Error

@ -28,12 +28,14 @@ type User struct {
}
type UserFormated struct {
ID int64 `db:"id" json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
IsAdmin bool `json:"is_admin"`
ID int64 `db:"id" json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
IsAdmin bool `json:"is_admin"`
IsFriend bool `json:"is_friend"`
IsFollowing bool `json:"is_following"`
}
func (u *User) Format() *UserFormated {

@ -15,6 +15,7 @@ var (
_attachment_ string
_captcha_ string
_comment_ string
_commentMetric_ string
_commentContent_ string
_commentReply_ string
_following_ string
@ -31,6 +32,7 @@ var (
_postStar_ string
_tag_ string
_user_ string
_userMetric_ string
_walletRecharge_ string
_walletStatement_ string
)
@ -42,6 +44,7 @@ func initTableName() {
_attachment_ = m[conf.TableAttachment]
_captcha_ = m[conf.TableCaptcha]
_comment_ = m[conf.TableComment]
_commentMetric_ = m[conf.TableCommentMetric]
_commentContent_ = m[conf.TableCommentContent]
_commentReply_ = m[conf.TableCommentReply]
_following_ = m[conf.TableFollowing]
@ -58,6 +61,7 @@ func initTableName() {
_postStar_ = m[conf.TablePostStar]
_tag_ = m[conf.TableTag]
_user_ = m[conf.TableUser]
_userMetric_ = m[conf.TableUserMetric]
_walletRecharge_ = m[conf.TableWalletRecharge]
_walletStatement_ = m[conf.TableWalletStatement]
}

@ -38,9 +38,11 @@ type dataSrv struct {
core.TweetMetricServantA
core.CommentService
core.CommentManageService
core.CommentMetricServantA
core.UserManageService
core.ContactManageService
core.FollowingManageService
core.UserRelationService
core.SecurityService
core.AttachmentCheckService
}
@ -56,10 +58,12 @@ func NewDataService() (core.DataService, core.VersionInfo) {
lazyInitial()
db := conf.MustGormDB()
pvs := security.NewPhoneVerifyService()
tms := NewTweetMetricServentA(db)
tms := newTweetMetricServentA(db)
cms := newCommentMetricServentA(db)
cis := cache.NewEventCacheIndexSrv(tms)
ds := &dataSrv{
TweetMetricServantA: tms,
CommentMetricServantA: cms,
WalletService: newWalletService(db),
MessageService: newMessageService(db),
TopicService: newTopicService(db),
@ -71,6 +75,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
UserManageService: newUserManageService(db),
ContactManageService: newContactManageService(db),
FollowingManageService: newFollowingManageService(db),
UserRelationService: newUserRelationService(db),
SecurityService: newSecurityService(db, pvs),
AttachmentCheckService: security.NewAttachmentCheckService(),
}

@ -15,7 +15,15 @@ type tweetMetricSrvA struct {
db *gorm.DB
}
func (s *tweetMetricSrvA) UpdateRankScore(metric *cs.TweetMetric) error {
type commentMetricSrvA struct {
db *gorm.DB
}
type userMetricSrvA struct {
db *gorm.DB
}
func (s *tweetMetricSrvA) UpdateTweetMetric(metric *cs.TweetMetric) error {
return s.db.Transaction(func(tx *gorm.DB) (err error) {
postMetric := &dbr.PostMetric{PostId: metric.PostId}
db := s.db.Model(postMetric).Where("post_id=?", metric.PostId)
@ -35,8 +43,54 @@ func (s *tweetMetricSrvA) DeleteTweetMetric(postId int64) (err error) {
return (&dbr.PostMetric{PostId: postId}).Delete(s.db)
}
func NewTweetMetricServentA(db *gorm.DB) core.TweetMetricServantA {
func (s *commentMetricSrvA) UpdateCommentMetric(metric *cs.CommentMetric) error {
return s.db.Transaction(func(tx *gorm.DB) (err error) {
commentMetric := &dbr.CommentMetric{CommentId: metric.CommentId}
db := s.db.Model(commentMetric).Where("comment_id=?", metric.CommentId)
db.First(commentMetric)
commentMetric.RankScore = metric.RankScore(commentMetric.MotivationFactor)
err = db.Save(commentMetric).Error
return
})
}
func (s *commentMetricSrvA) AddCommentMetric(commentId int64) (err error) {
_, err = (&dbr.CommentMetric{CommentId: commentId}).Create(s.db)
return
}
func (s *commentMetricSrvA) DeleteCommentMetric(commentId int64) (err error) {
return (&dbr.CommentMetric{CommentId: commentId}).Delete(s.db)
}
func (s *userMetricSrvA) UpdateUserMetric(userId int64, action uint8) error {
// TODO
return cs.ErrNotImplemented
}
func (s *userMetricSrvA) AddUserMetric(userId int64) (err error) {
_, err = (&dbr.UserMetric{UserId: userId}).Create(s.db)
return
}
func (s *userMetricSrvA) DeleteUserMetric(userId int64) (err error) {
return (&dbr.UserMetric{UserId: userId}).Delete(s.db)
}
func newTweetMetricServentA(db *gorm.DB) core.TweetMetricServantA {
return &tweetMetricSrvA{
db: db,
}
}
func newCommentMetricServentA(db *gorm.DB) core.CommentMetricServantA {
return &commentMetricSrvA{
db: db,
}
}
func newUserMetricServentA(db *gorm.DB) core.UserMetricServantA {
return &userMetricSrvA{
db: db,
}
}

@ -21,12 +21,22 @@ type userManageSrv struct {
db *gorm.DB
}
type userRelationSrv struct {
db *gorm.DB
}
func newUserManageService(db *gorm.DB) core.UserManageService {
return &userManageSrv{
db: db,
}
}
func newUserRelationService(db *gorm.DB) core.UserRelationService {
return &userRelationSrv{
db: db,
}
}
func (s *userManageSrv) GetUserByID(id int64) (*ms.User, error) {
user := &dbr.User{
Model: &dbr.Model{
@ -83,3 +93,57 @@ func (s *userManageSrv) GetRegisterUserCount() (res int64, err error) {
err = s.db.Model(&dbr.User{}).Count(&res).Error
return
}
func (s *userRelationSrv) MyFriendIds(userId int64) (res []int64, err error) {
err = s.db.Table(_contact_).Where("user_id=?", userId).Select("friend_id").Find(&res).Error
return
}
func (s *userRelationSrv) MyFollowIds(userId int64) (res []int64, err error) {
err = s.db.Table(_following_).Where("user_id=?", userId).Select("follow_id").Find(&res).Error
return
}
func (s *userRelationSrv) IsMyFriend(userId int64, friendIds ...int64) (map[int64]bool, error) {
size := len(friendIds)
res := make(map[int64]bool, size)
if size == 0 {
return res, nil
}
myFriendIds, err := s.MyFriendIds(userId)
if err != nil {
return nil, err
}
for _, friendId := range friendIds {
res[friendId] = false
for _, myFriendId := range myFriendIds {
if friendId == myFriendId {
res[friendId] = true
break
}
}
}
return res, nil
}
func (s *userRelationSrv) IsMyFollow(userId int64, followIds ...int64) (map[int64]bool, error) {
size := len(followIds)
res := make(map[int64]bool, size)
if size == 0 {
return res, nil
}
myFollowIds, err := s.MyFollowIds(userId)
if err != nil {
return nil, err
}
for _, followId := range followIds {
res[followId] = false
for _, myFollowId := range myFollowIds {
if followId == myFollowId {
res[followId] = true
break
}
}
}
return res, nil
}

@ -65,6 +65,11 @@ func (s *commentManageSrv) DeleteComment(comment *ms.Comment) error {
return nil
}
func (s *commentManageSrv) HighlightComment(userId, commentId int64) (res int8, err error) {
// TODO
return 0, cs.ErrNotImplemented
}
func (s *commentManageSrv) CreateComment(comment *ms.Comment) (*ms.Comment, error) {
// TODO
debug.NotImplemented()

@ -14,7 +14,15 @@ type tweetMetricSrvA struct {
*pgxSrv
}
func (s *tweetMetricSrvA) UpdateRankScore(metric *cs.TweetMetric) error {
type commentMetricSrvA struct {
*pgxSrv
}
type userMetricSrvA struct {
*pgxSrv
}
func (s *tweetMetricSrvA) UpdateTweetMetric(metric *cs.TweetMetric) error {
// TODO
return cs.ErrNotImplemented
}
@ -29,8 +37,50 @@ func (s *tweetMetricSrvA) DeleteTweetMetric(postId int64) (err error) {
return cs.ErrNotImplemented
}
func NewTweetMetricServentA(db *pgx.Conn) core.TweetMetricServantA {
func (s *commentMetricSrvA) UpdateCommentMetric(metric *cs.CommentMetric) error {
// TODO
return cs.ErrNotImplemented
}
func (s *commentMetricSrvA) AddCommentMetric(commentId int64) (err error) {
// TODO
return cs.ErrNotImplemented
}
func (s *commentMetricSrvA) DeleteCommentMetric(commentId int64) (err error) {
// TODO
return cs.ErrNotImplemented
}
func (s *userMetricSrvA) UpdateUserMetric(userId int64, action uint8) error {
// TODO
return cs.ErrNotImplemented
}
func (s *userMetricSrvA) AddUserMetric(userId int64) (err error) {
// TODO
return cs.ErrNotImplemented
}
func (s *userMetricSrvA) DeleteUserMetric(userId int64) (err error) {
// TODO
return cs.ErrNotImplemented
}
func newTweetMetricServentA(db *pgx.Conn) core.TweetMetricServantA {
return &tweetMetricSrvA{
pgxSrv: newPgxSrv(db),
}
}
func newCommentMetricServentA(db *pgx.Conn) core.CommentMetricServantA {
return &commentMetricSrvA{
pgxSrv: newPgxSrv(db),
}
}
func newUserMetricServentA(db *pgx.Conn) core.UserMetricServantA {
return &userMetricSrvA{
pgxSrv: newPgxSrv(db),
}
}

@ -32,9 +32,11 @@ type dataSrv struct {
core.UserManageService
core.ContactManageService
core.FollowingManageService
core.UserRelationService
core.SecurityService
core.AttachmentCheckService
core.TweetMetricServantA
core.CommentMetricServantA
}
type webDataSrvA struct {
@ -47,10 +49,12 @@ type webDataSrvA struct {
func NewDataService() (core.DataService, core.VersionInfo) {
db := pgxDB()
pvs := security.NewPhoneVerifyService()
tms := NewTweetMetricServentA(db)
cms := newCommentMetricServentA(db)
tms := newTweetMetricServentA(db)
cis := cache.NewEventCacheIndexSrv(tms)
ds := &dataSrv{
TweetMetricServantA: tms,
CommentMetricServantA: cms,
WalletService: newWalletService(db),
MessageService: newMessageService(db),
TopicService: newTopicService(db),
@ -62,6 +66,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
UserManageService: newUserManageService(db),
ContactManageService: newContactManageService(db),
FollowingManageService: newFollowingManageService(db),
UserRelationService: newUserRelationService(db),
SecurityService: newSecurityService(db, pvs),
AttachmentCheckService: security.NewAttachmentCheckService(),
}

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: core.sql
package pg

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pg

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pg

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: authrity.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: comments.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: contacts.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: following.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: messages.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: security.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: timeline.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: topics.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: tweets.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: user.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: wallet.sql
package pga

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: authrity.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: comments.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: contacts.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: following.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: messages.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: security.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: timeline.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: topics.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: tweets.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: user.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
// source: wallet.sql
package pgc

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// sqlc v1.21.0
package pg

@ -7,6 +7,7 @@ package slonik
import (
"github.com/jackc/pgx/v5"
"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/pkg/debug"
)
@ -19,6 +20,10 @@ type userManageSrv struct {
*pgxSrv
}
type userRelationSrv struct {
*pgxSrv
}
func (s *userManageSrv) GetUserByID(id int64) (*ms.User, error) {
// TODO
debug.NotImplemented()
@ -67,8 +72,34 @@ func (s *userManageSrv) UpdateUser(user *ms.User) error {
return nil
}
func (s *userRelationSrv) MyFriendIds(userId int64) (res []int64, err error) {
// TODO
return nil, cs.ErrNotImplemented
}
func (s *userRelationSrv) MyFollowIds(userId int64) (res []int64, err error) {
// TODO
return nil, cs.ErrNotImplemented
}
func (s *userRelationSrv) IsMyFriend(userId int64, friendIds ...int64) (map[int64]bool, error) {
// TODO
return nil, cs.ErrNotImplemented
}
func (s *userRelationSrv) IsMyFollow(userId int64, followIds ...int64) (map[int64]bool, error) {
// TODO
return nil, cs.ErrNotImplemented
}
func newUserManageService(db *pgx.Conn) core.UserManageService {
return &userManageSrv{
pgxSrv: newPgxSrv(db),
}
}
func newUserRelationService(db *pgx.Conn) core.UserRelationService {
return &userRelationSrv{
pgxSrv: newPgxSrv(db),
}
}

@ -16,7 +16,7 @@ import (
var (
_defaultEventManager EventManager
_defaultJobManager JobManager
_defaultJobManager JobManager = emptyJobManager{}
_onceInitial sync.Once
)

@ -6,6 +6,7 @@ package events
import (
"github.com/robfig/cron/v3"
"github.com/rocboss/paopao-ce/pkg/types"
)
type (
@ -38,31 +39,49 @@ type JobManager interface {
Schedule(Job) EntryID
}
type jobManager struct {
type emptyJobManager types.Empty
type simpleJobManager struct {
m *cron.Cron
}
func (j *jobManager) Start() {
func (emptyJobManager) Start() {
// nothing
}
func (emptyJobManager) Stop() {
// nothing
}
func (emptyJobManager) Remove(id EntryID) {
// nothing
}
func (emptyJobManager) Schedule(job Job) EntryID {
return 0
}
func (j *simpleJobManager) Start() {
j.m.Start()
}
func (j *jobManager) Stop() {
func (j *simpleJobManager) Stop() {
j.m.Stop()
}
// Remove an entry from being run in the future.
func (j *jobManager) Remove(id EntryID) {
func (j *simpleJobManager) Remove(id EntryID) {
j.m.Remove(id)
}
// Schedule adds a Job to the Cron to be run on the given schedule.
// The job is wrapped with the configured Chain.
func (j *jobManager) Schedule(job Job) EntryID {
func (j *simpleJobManager) Schedule(job Job) EntryID {
return j.m.Schedule(job, job)
}
func NewJobManager() JobManager {
return &jobManager{
return &simpleJobManager{
m: cron.New(),
}
}

@ -0,0 +1,57 @@
// 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 prometheus
import (
"github.com/prometheus/client_golang/prometheus"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/sirupsen/logrus"
)
type metrics struct {
siteInfo *prometheus.GaugeVec
ds core.DataService
wc core.WebCache
}
func (m *metrics) updateSiteInfo() {
if onlineUserKeys, err := m.wc.Keys(conf.PrefixOnlineUser + "*"); err == nil {
maxOnline := len(onlineUserKeys)
m.siteInfo.With(prometheus.Labels{"name": "max_online"}).Set(float64(maxOnline))
} else {
logrus.Warnf("update promethues metrics[site_info_max_online] occurs error: %s", err)
}
if registerUserCount, err := m.ds.GetRegisterUserCount(); err == nil {
m.siteInfo.With(prometheus.Labels{"name": "register_user_count"}).Set(float64(registerUserCount))
} else {
logrus.Warnf("update promethues metrics[site_info_register_user_count] occurs error: %s", err)
}
}
func (m *metrics) onUpdate() {
logrus.Debugf("update promethues metrics job running")
m.updateSiteInfo()
}
func newMetrics(reg prometheus.Registerer, ds core.DataService, wc core.WebCache) *metrics {
m := &metrics{
siteInfo: prometheus.NewGaugeVec(
prometheus.GaugeOpts{
Namespace: "paopao",
Subsystem: "site",
Name: "simple_info",
Help: "paopao-ce site simple information.",
},
[]string{
// metric name
"name",
}),
ds: ds,
wc: wc,
}
reg.MustRegister(m.siteInfo)
return m
}

@ -0,0 +1,41 @@
// 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 prometheus
import (
"net/http"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/robfig/cron/v3"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/events"
"github.com/sirupsen/logrus"
)
func scheduleJobs(metrics *metrics) {
spec := conf.JobManagerSetting.UpdateMetricsInterval
schedule, err := cron.ParseStandard(spec)
if err != nil {
panic(err)
}
events.OnTask(schedule, metrics.onUpdate)
logrus.Debug("shedule prometheus metrics update jobs complete")
}
func NewHandler(ds core.DataService, wc core.WebCache) http.Handler {
// Create non-global registry.
registry := prometheus.NewRegistry()
// Add go runtime metrics and process collectors.
registry.MustRegister(
collectors.NewGoCollector(),
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
)
metrics := newMetrics(registry, ds, wc)
scheduleJobs(metrics)
return promhttp.HandlerFor(registry, promhttp.HandlerOpts{EnableOpenMetrics: true})
}

@ -7,8 +7,10 @@ package web
import (
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
"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/servants/base"
"github.com/rocboss/paopao-ce/pkg/app"
@ -45,7 +47,9 @@ type TweetCommentsReq struct {
PageSize int `form:"-" binding:"-"`
}
type TweetCommentsResp base.PageResp
type TweetCommentsResp struct {
joint.CachePageResp
}
type TimelineReq struct {
BaseInfo `form:"-" binding:"-"`
@ -106,6 +110,13 @@ type TopicListResp struct {
ExtralTopics cs.TagList `json:"extral_topics,omitempty"`
}
type TweetDetailReq struct {
SimpleInfo `form:"-" binding:"-"`
TweetId int64 `form:"id"`
}
type TweetDetailResp ms.PostFormated
func (r *GetUserTweetsReq) SetPageInfo(page int, pageSize int) {
r.Page, r.PageSize = page, pageSize
}
@ -137,3 +148,17 @@ func (s CommentStyleType) ToInnerValue() (res cs.StyleCommentType) {
}
return
}
func (s CommentStyleType) String() (res string) {
switch s {
case "default":
res = conf.InfixCommentDefault
case "hots":
res = conf.InfixCommentHots
case "newest":
res = conf.InfixCommentNewest
default:
res = "_"
}
return
}

@ -147,6 +147,16 @@ type DeleteCommentReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`
}
type HighlightCommentReq struct {
SimpleInfo `json:"-" binding:"-"`
CommentId int64 `json:"id" binding:"required"`
}
type HighlightCommentResp struct {
HighlightStatus int8 `json:"highlight_status"`
}
type DeleteCommentReplyReq struct {
BaseInfo `json:"-" binding:"-"`
ID int64 `json:"id" binding:"required"`

@ -5,16 +5,9 @@
package web
import (
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/pkg/version"
)
type TweetDetailReq struct {
TweetId int64 `form:"id"`
}
type TweetDetailResp ms.PostFormated
type GetCaptchaResp struct {
Id string `json:"id"`
Content string `json:"b64s"`

@ -50,14 +50,15 @@ var (
ErrGetPostsUnknowStyle = xerror.NewError(30014, "使用未知样式参数获取动态列表")
ErrGetPostsNilUser = xerror.NewError(30015, "使用游客账户获取动态详情失败")
ErrGetCommentsFailed = xerror.NewError(40001, "获取评论列表失败")
ErrCreateCommentFailed = xerror.NewError(40002, "评论发布失败")
ErrGetCommentFailed = xerror.NewError(40003, "获取评论详情失败")
ErrDeleteCommentFailed = xerror.NewError(40004, "评论删除失败")
ErrCreateReplyFailed = xerror.NewError(40005, "评论回复失败")
ErrGetReplyFailed = xerror.NewError(40006, "获取评论详情失败")
ErrMaxCommentCount = xerror.NewError(40007, "评论数已达最大限制")
ErrGetCommentThumbs = xerror.NewError(40008, "获取评论点赞信息失败")
ErrGetCommentsFailed = xerror.NewError(40001, "获取评论列表失败")
ErrCreateCommentFailed = xerror.NewError(40002, "评论发布失败")
ErrGetCommentFailed = xerror.NewError(40003, "获取评论详情失败")
ErrDeleteCommentFailed = xerror.NewError(40004, "评论删除失败")
ErrCreateReplyFailed = xerror.NewError(40005, "评论回复失败")
ErrGetReplyFailed = xerror.NewError(40006, "获取评论详情失败")
ErrMaxCommentCount = xerror.NewError(40007, "评论数已达最大限制")
ErrGetCommentThumbs = xerror.NewError(40008, "获取评论点赞信息失败")
ErrHighlightCommentFailed = xerror.NewError(40009, "设置精选评论失败")
ErrGetMessagesFailed = xerror.NewError(50001, "获取消息列表失败")
ErrReadMessageFailed = xerror.NewError(50002, "标记消息已读失败")

@ -24,6 +24,7 @@ import (
"github.com/rocboss/paopao-ce/internal/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/pkg/app"
"github.com/rocboss/paopao-ce/pkg/types"
"github.com/rocboss/paopao-ce/pkg/xerror"
)
@ -172,6 +173,109 @@ func (s *BaseServant) Render(c *gin.Context, data any, err mir.Error) {
}
}
func (s *DaoServant) PrepareUser(userId int64, user *ms.UserFormated) error {
// guest用户的userId<0
if userId < 0 {
return nil
}
// friendMap, err := s.Ds.IsMyFriend(userId, user.ID)
// if err != nil {
// return err
// }
followMap, err := s.Ds.IsMyFollow(userId, user.ID)
if err != nil {
return err
}
// user.IsFriend, user.IsFollowing = friendMap[user.ID], followMap[user.ID]
user.IsFollowing = followMap[user.ID]
return nil
}
func (s *DaoServant) PrepareMessages(userId int64, messages []*ms.MessageFormated) error {
// guest用户的userId<0
if userId < 0 {
return nil
}
userIds := make([]int64, 0, len(messages))
for _, msg := range messages {
if msg.SenderUser != nil {
userIds = append(userIds, msg.SenderUserID)
}
if msg.ReceiverUser != nil {
userIds = append(userIds, msg.ReceiverUserID)
}
}
// friendMap, err := s.Ds.IsMyFriend(userId, userIds...)
// if err != nil {
// return err
// }
followMap, err := s.Ds.IsMyFollow(userId, userIds...)
if err != nil {
return err
}
for _, msg := range messages {
if msg.SenderUser != nil {
// msg.SenderUser.IsFriend, msg.SenderUser.IsFollowing = friendMap[msg.SenderUserID], followMap[msg.SenderUserID]
msg.SenderUser.IsFollowing = followMap[msg.SenderUserID]
}
if msg.ReceiverUser != nil {
// msg.ReceiverUser.IsFriend, msg.ReceiverUser.IsFollowing = friendMap[msg.ReceiverUserID], followMap[msg.ReceiverUserID]
msg.ReceiverUser.IsFollowing = followMap[msg.ReceiverUserID]
}
}
return nil
}
func (s *DaoServant) PrepareTweet(userId int64, tweet *ms.PostFormated) error {
// 转换一下可见性的值
tweet.Visibility = ms.PostVisibleT(tweet.Visibility.ToOutValue())
// guest用户的userId<0
if userId < 0 {
return nil
}
// friendMap, err := s.Ds.IsMyFriend(userId, userIds)
// if err != nil {
// return err
// }
followMap, err := s.Ds.IsMyFollow(userId, tweet.UserID)
if err != nil {
return err
}
// tweet.User.IsFriend, tweet.User.IsFollowing = friendMap[tweet.UserID], followMap[tweet.UserID]
tweet.User.IsFollowing = followMap[tweet.UserID]
return nil
}
func (s *DaoServant) PrepareTweets(userId int64, tweets []*ms.PostFormated) error {
userIdSet := make(map[int64]types.Empty, len(tweets))
for _, tweet := range tweets {
userIdSet[tweet.UserID] = types.Empty{}
// 顺便转换一下可见性的值
tweet.Visibility = ms.PostVisibleT(tweet.Visibility.ToOutValue())
}
// guest用户的userId<0
if userId < 0 {
return nil
}
userIds := make([]int64, 0, len(userIdSet))
for id := range userIdSet {
userIds = append(userIds, id)
}
// friendMap, err := s.Ds.IsMyFriend(userId, userIds...)
// if err != nil {
// return err
// }
followMap, err := s.Ds.IsMyFollow(userId, userIds...)
if err != nil {
return err
}
for _, tweet := range tweets {
// tweet.User.IsFriend, tweet.User.IsFollowing = friendMap[tweet.UserID], followMap[tweet.UserID]
tweet.User.IsFollowing = followMap[tweet.UserID]
}
return nil
}
func (s *DaoServant) GetTweetBy(id int64) (*ms.PostFormated, error) {
post, err := s.Ds.GetPostByID(id)
if err != nil {
@ -278,15 +382,6 @@ func (s *DaoServant) DeleteSearchPost(post *ms.Post) error {
return s.Ts.DeleteDocuments([]string{fmt.Sprintf("%d", post.ID)})
}
func (s *DaoServant) GetTweetList(conditions ms.ConditionsT, offset, limit int) ([]*ms.Post, []*ms.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 (s *DaoServant) RelationTypFrom(me *ms.User, username string) (res *cs.VistUser, err error) {
res = &cs.VistUser{
RelTyp: cs.RelationSelf,

@ -125,6 +125,10 @@ func (s *coreSrv) GetMessages(req *web.GetMessagesReq) (*web.GetMessagesResp, mi
logrus.Errorf("Ds.GetMessages err: %v\n", err)
return nil, web.ErrGetMessagesFailed
}
if err = s.PrepareMessages(req.UserId, messages); err != nil {
logrus.Errorf("get messages err[2]: %v\n", err)
return nil, web.ErrGetMessagesFailed
}
totalRows, _ := s.Ds.GetMessageCount(req.UserId)
resp := base.PageRespFrom(messages, req.Page, req.PageSize, totalRows)
return (*web.GetMessagesResp)(resp), nil
@ -192,7 +196,6 @@ func (s *coreSrv) GetCollections(req *web.GetCollectionsReq) (*web.GetCollection
logrus.Errorf("Ds.GetUserPostCollectionCount err: %s", err)
return nil, web.ErrGetCollectionsFailed
}
var posts []*ms.Post
for _, collection := range collections {
posts = append(posts, collection.Post)
@ -202,8 +205,11 @@ func (s *coreSrv) GetCollections(req *web.GetCollectionsReq) (*web.GetCollection
logrus.Errorf("Ds.MergePosts err: %s", err)
return nil, web.ErrGetCollectionsFailed
}
if err = s.PrepareTweets(req.UserId, postsFormated); err != nil {
logrus.Errorf("get collections prepare tweets err: %s", err)
return nil, web.ErrGetCollectionsFailed
}
resp := base.PageRespFrom(postsFormated, req.Page, req.PageSize, totalRows)
return (*web.GetCollectionsResp)(resp), nil
}

@ -9,13 +9,26 @@ import (
"fmt"
"github.com/alimy/tryst/event"
"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/events"
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/model/web"
)
const (
_commentActionCreate uint8 = iota
_commentActionDelete
_commentActionThumbsUp
_commentActionThumbsDown
_commentActionReplyCreate
_commentActionReplyDelete
_commentActionReplyThumbsUp
_commentActionReplyThumbsDown
)
type cacheUnreadMsgEvent struct {
event.UnimplementedEvent
ds core.DataService
@ -30,6 +43,25 @@ type createMessageEvent struct {
message *ms.Message
}
type commentActionEvent struct {
event.UnimplementedEvent
ds core.DataService
ac core.AppCache
tweetId int64
commentId int64
action uint8
}
func onCommentActionEvent(tweetId int64, commentId int64, action uint8) {
events.OnEvent(&commentActionEvent{
ds: _ds,
ac: _ac,
tweetId: tweetId,
commentId: commentId,
action: action,
})
}
func onCacheUnreadMsgEvent(uid int64) {
events.OnEvent(&cacheUnreadMsgEvent{
ds: _ds,
@ -86,3 +118,51 @@ func (e *createMessageEvent) Action() (err error) {
}
return
}
func (e *commentActionEvent) Name() string {
return "updateCommentMetricEvent"
}
func (e *commentActionEvent) Action() (err error) {
// logrus.Debugf("trigger commentActionEvent action commentId[%d]", e.commentId)
switch e.action {
case _commentActionCreate:
err = e.ds.AddCommentMetric(e.commentId)
e.expireAllStyleComments()
case _commentActionDelete:
err = e.ds.DeleteCommentMetric(e.commentId)
e.expireAllStyleComments()
case _commentActionReplyCreate, _commentActionReplyDelete:
err = e.updateCommentMetric()
e.expireAllStyleComments()
case _commentActionThumbsUp, _commentActionThumbsDown:
err = e.updateCommentMetric()
e.expireHotsComments()
default:
// nothing
}
return
}
func (e *commentActionEvent) expireHotsComments() {
e.ac.DelAny(fmt.Sprintf("%s%d:%s:*", conf.PrefixTweetComment, e.tweetId, conf.InfixCommentHots))
}
func (e *commentActionEvent) expireAllStyleComments() {
e.ac.DelAny(fmt.Sprintf("%s%d:*", conf.PrefixTweetComment, e.tweetId))
}
func (e *commentActionEvent) updateCommentMetric() error {
// logrus.Debug("trigger commentActionEvent action[updateCommentMetric]")
comment, err := e.ds.GetCommentByID(e.commentId)
if err != nil {
return err
}
e.ds.UpdateCommentMetric(&cs.CommentMetric{
CommentId: e.commentId,
ReplyCount: comment.ReplyCount,
ThumbsUpCount: comment.ThumbsUpCount,
ThumbsDownCount: comment.ThumbsDownCount,
})
return nil
}

@ -8,6 +8,7 @@ import (
"github.com/alimy/mir/v4"
"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/internal/servants/chain"
@ -84,6 +85,9 @@ func (s *followshipSrv) UnfollowUser(r *web.UnfollowUserReq) mir.Error {
logrus.Errorf("Ds.UnfollowUser err: %s userId: %d followId: %d", err, r.User.ID, r.UserId)
return web.ErrUnfollowUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
return nil
}
@ -97,6 +101,9 @@ func (s *followshipSrv) FollowUser(r *web.FollowUserReq) mir.Error {
logrus.Errorf("Ds.FollowUser err: %s userId: %d followId: %d", err, r.User.ID, r.UserId)
return web.ErrUnfollowUserFailed
}
// 触发缓存更新事件
cache.OnCacheMyFollowIdsEvent(s.Ds, r.User.ID)
cache.OnExpireIndexTweetEvent(r.User.ID)
return nil
}

@ -55,6 +55,8 @@ func (s *friendshipSrv) DeleteFriend(req *web.DeleteFriendReq) mir.Error {
logrus.Errorf("Ds.DeleteFriend err: %s", err)
return web.ErrDeleteFriendFailed
}
// 触发用户关系缓存更新事件
// cache.OnCacheMyFriendIdsEvent(s.Ds, req.User.ID, req.UserId)
return nil
}
@ -89,6 +91,8 @@ func (s *friendshipSrv) AddFriend(req *web.AddFriendReq) mir.Error {
logrus.Errorf("Ds.AddFriend err: %s", err)
return web.ErrAddFriendFailed
}
// 触发用户关系缓存更新事件
// cache.OnCacheMyFriendIdsEvent(s.Ds, req.User.ID, req.UserId)
return nil
}

@ -32,10 +32,12 @@ type looseSrv struct {
ac core.AppCache
userTweetsExpire int64
idxTweetsExpire int64
tweetCommentsExpire int64
prefixUserTweets string
prefixIdxTweetsNewest string
prefixIdxTweetsHots string
prefixIdxTweetsFollowing string
prefixTweetComment string
}
func (s *looseSrv) Chain() gin.HandlersChain {
@ -61,8 +63,14 @@ func (s *looseSrv) Timeline(req *web.TimelineReq) (*web.TimelineResp, mir.Error)
logrus.Errorf("Ds.RevampPosts err: %s", err)
return nil, web.ErrGetPostsFailed
}
// TODO: 暂时处理,需要去掉这个步骤
visbleTansform(posts)
userId := int64(-1)
if req.User != nil {
userId = req.User.ID
}
if err := s.PrepareTweets(userId, posts); err != nil {
logrus.Errorf("timeline occurs error[2]: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := joint.PageRespFrom(posts, req.Page, req.PageSize, res.Total)
return &web.TimelineResp{
CachePageResp: joint.CachePageResp{
@ -98,7 +106,7 @@ func (s *looseSrv) getIndexTweets(req *web.TimelineReq, limit int, offset int) (
return nil, web.ErrGetPostsUnknowStyle
}
if xerr != nil {
logrus.Errorf("getIndexTweets occurs error: %s", xerr)
logrus.Errorf("getIndexTweets occurs error[1]: %s", xerr)
return nil, web.ErrGetPostFailed
}
postsFormated, verr := s.Ds.MergePosts(posts)
@ -106,8 +114,14 @@ func (s *looseSrv) getIndexTweets(req *web.TimelineReq, limit int, offset int) (
logrus.Errorf("getIndexTweets in merge posts occurs error: %s", verr)
return nil, web.ErrGetPostFailed
}
// TODO: 暂时处理,需要去掉这个步骤
visbleTansform(postsFormated)
userId := int64(-1)
if req.User != nil {
userId = req.User.ID
}
if err := s.PrepareTweets(userId, postsFormated); err != nil {
logrus.Errorf("getIndexTweets occurs error[2]: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := joint.PageRespFrom(postsFormated, req.Page, req.PageSize, total)
// 缓存处理
base.OnCacheRespEvent(s.ac, key, resp, s.idxTweetsExpire)
@ -119,17 +133,17 @@ func (s *looseSrv) getIndexTweets(req *web.TimelineReq, limit int, offset int) (
}
func (s *looseSrv) indexTweetsFromCache(req *web.TimelineReq, limit int, offset int) (res *web.TimelineResp, key string, ok bool) {
username := "_"
if req.User != nil {
username = req.User.Username
}
switch req.Style {
case web.StyleTweetsFollowing:
username := "_"
if req.User != nil {
username = req.User.Username
}
key = fmt.Sprintf("%s%s:%d:%d", s.prefixIdxTweetsFollowing, username, offset, limit)
case web.StyleTweetsNewest:
key = fmt.Sprintf("%s%d:%d", s.prefixIdxTweetsNewest, offset, limit)
key = fmt.Sprintf("%s%s:%d:%d", s.prefixIdxTweetsNewest, username, offset, limit)
case web.StyleTweetsHots:
key = fmt.Sprintf("%s%d:%d", s.prefixIdxTweetsHots, offset, limit)
key = fmt.Sprintf("%s%s:%d:%d", s.prefixIdxTweetsHots, username, offset, limit)
default:
return
}
@ -143,6 +157,18 @@ func (s *looseSrv) indexTweetsFromCache(req *web.TimelineReq, limit int, offset
return
}
func (s *looseSrv) tweetCommentsFromCache(req *web.TweetCommentsReq, limit int, offset int) (res *web.TweetCommentsResp, key string, ok bool) {
key = fmt.Sprintf("%s%d:%s:%d:%d", s.prefixTweetComment, req.TweetId, req.Style, limit, offset)
if data, err := s.ac.Get(key); err == nil {
ok, res = true, &web.TweetCommentsResp{
CachePageResp: joint.CachePageResp{
JsonResp: data,
},
}
}
return
}
func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTweetsResp, err mir.Error) {
user, xerr := s.RelationTypFrom(req.User, req.Username)
if xerr != nil {
@ -198,7 +224,7 @@ func (s *looseSrv) userTweetsFromCache(req *web.GetUserTweetsReq, user *cs.VistU
func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUser) (*web.GetUserTweetsResp, mir.Error) {
stars, totalRows, err := s.Ds.ListUserStarTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
if err != nil {
logrus.Errorf("Ds.GetUserPostStars err: %s", err)
logrus.Errorf("getUserStarTweets err[1]: %s", err)
return nil, web.ErrGetStarsFailed
}
var posts []*ms.Post
@ -212,8 +238,14 @@ func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, user *cs.VistUse
logrus.Errorf("Ds.MergePosts err: %s", err)
return nil, web.ErrGetStarsFailed
}
// TODO: 暂时处理,需要去掉这个步骤
visbleTansform(postsFormated)
userId := int64(-1)
if req.User != nil {
userId = req.User.ID
}
if err := s.PrepareTweets(userId, postsFormated); err != nil {
logrus.Errorf("getUserStarTweets err[2]: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := joint.PageRespFrom(postsFormated, req.Page, req.PageSize, totalRows)
return &web.GetUserTweetsResp{
CachePageResp: joint.CachePageResp{
@ -233,21 +265,27 @@ func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser)
} else if req.Style == web.UserPostsStyleMedia {
tweets, total, err = s.Ds.ListUserMediaTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
} else {
logrus.Errorf("s.listUserTweets unknow style: %s", req.Style)
logrus.Errorf("s.listUserTweets unknow style[1]: %s", req.Style)
return nil, web.ErrGetPostsFailed
}
if err != nil {
logrus.Errorf("s.listUserTweets err: %s", err)
logrus.Errorf("s.listUserTweets err[2]: %s", err)
return nil, web.ErrGetPostsFailed
}
postFormated, err := s.Ds.MergePosts(tweets)
postsFormated, err := s.Ds.MergePosts(tweets)
if err != nil {
logrus.Errorf("s.listUserTweets err: %s", err)
logrus.Errorf("s.listUserTweets err[3]: %s", err)
return nil, web.ErrGetPostsFailed
}
// TODO: 暂时处理,需要去掉这个步骤
visbleTansform(postFormated)
resp := joint.PageRespFrom(postFormated, req.Page, req.PageSize, total)
userId := int64(-1)
if req.User != nil {
userId = req.User.ID
}
if err := s.PrepareTweets(userId, postsFormated); err != nil {
logrus.Errorf("s.listUserTweets err[4]: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := joint.PageRespFrom(postsFormated, req.Page, req.PageSize, total)
return &web.GetUserTweetsResp{
CachePageResp: joint.CachePageResp{
Data: resp,
@ -281,8 +319,14 @@ func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *cs.VistUse
logrus.Errorf("s.GetTweetList error[2]: %s", err)
return nil, web.ErrGetPostsFailed
}
// TODO: 暂时处理,需要去掉这个步骤
visbleTansform(postsFormated)
userId := int64(-1)
if req.User != nil {
userId = req.User.ID
}
if err := s.PrepareTweets(userId, postsFormated); err != nil {
logrus.Errorf("s.GetTweetList error[3]: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := joint.PageRespFrom(postsFormated, req.Page, req.PageSize, total)
return &web.GetUserTweetsResp{
CachePageResp: joint.CachePageResp{
@ -364,10 +408,18 @@ func (s *looseSrv) TopicList(req *web.TopicListReq) (*web.TopicListResp, mir.Err
}, nil
}
func (s *looseSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsResp, mir.Error) {
comments, totalRows, err := s.Ds.GetComments(req.TweetId, req.Style.ToInnerValue(), req.PageSize, (req.Page-1)*req.PageSize)
if err != nil {
logrus.Errorf("get tweet comments error[1]: %s", err)
func (s *looseSrv) TweetComments(req *web.TweetCommentsReq) (res *web.TweetCommentsResp, err mir.Error) {
limit, offset := req.PageSize, (req.Page-1)*req.PageSize
// 尝试直接从缓存中获取数据
key, ok := "", false
if res, key, ok = s.tweetCommentsFromCache(req, limit, offset); ok {
logrus.Debugf("looseSrv.TweetComments from cache key:%s", key)
return
}
comments, totalRows, xerr := s.Ds.GetComments(req.TweetId, req.Style.ToInnerValue(), limit, offset)
if xerr != nil {
logrus.Errorf("looseSrv.TweetComments occurs error[1]: %s", xerr)
return nil, web.ErrGetCommentsFailed
}
@ -378,29 +430,29 @@ func (s *looseSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsR
commentIDs = append(commentIDs, comment.ID)
}
users, err := s.Ds.GetUsersByIDs(userIDs)
if err != nil {
logrus.Errorf("get tweet comments error[2]: %s", err)
users, xerr := s.Ds.GetUsersByIDs(userIDs)
if xerr != nil {
logrus.Errorf("looseSrv.TweetComments occurs error[2]: %s", xerr)
return nil, web.ErrGetCommentsFailed
}
contents, err := s.Ds.GetCommentContentsByIDs(commentIDs)
if err != nil {
logrus.Errorf("get tweet comments error[3]: %s", err)
contents, xerr := s.Ds.GetCommentContentsByIDs(commentIDs)
if xerr != nil {
logrus.Errorf("looseSrv.TweetComments occurs error[3]: %s", xerr)
return nil, web.ErrGetCommentsFailed
}
replies, err := s.Ds.GetCommentRepliesByID(commentIDs)
if err != nil {
logrus.Errorf("get tweet comments error[4]: %s", err)
replies, xerr := s.Ds.GetCommentRepliesByID(commentIDs)
if xerr != nil {
logrus.Errorf("looseSrv.TweetComments occurs error[4]: %s", xerr)
return nil, web.ErrGetCommentsFailed
}
var commentThumbs, replyThumbs cs.CommentThumbsMap
if req.Uid > 0 {
commentThumbs, replyThumbs, err = s.Ds.GetCommentThumbsMap(req.Uid, req.TweetId)
if err != nil {
logrus.Errorf("get tweet comments error[5]: %s", err)
commentThumbs, replyThumbs, xerr = s.Ds.GetCommentThumbsMap(req.Uid, req.TweetId)
if xerr != nil {
logrus.Errorf("looseSrv.TweetComments occurs error[5]: %s", xerr)
return nil, web.ErrGetCommentsFailed
}
}
@ -440,8 +492,41 @@ func (s *looseSrv) TweetComments(req *web.TweetCommentsReq) (*web.TweetCommentsR
}
commentsFormated = append(commentsFormated, commentFormated)
}
resp := base.PageRespFrom(commentsFormated, req.Page, req.PageSize, totalRows)
return (*web.TweetCommentsResp)(resp), nil
resp := joint.PageRespFrom(commentsFormated, req.Page, req.PageSize, totalRows)
// 缓存处理
base.OnCacheRespEvent(s.ac, key, resp, s.tweetCommentsExpire)
return &web.TweetCommentsResp{
CachePageResp: joint.CachePageResp{
Data: resp,
},
}, nil
}
func (s *looseSrv) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
post, err := s.Ds.GetPostByID(req.TweetId)
if err != nil {
return nil, web.ErrGetPostFailed
}
postContents, err := s.Ds.GetPostContentsByIDs([]int64{post.ID})
if err != nil {
return nil, web.ErrGetPostFailed
}
users, err := s.Ds.GetUsersByIDs([]int64{post.UserID})
if err != nil {
return nil, web.ErrGetPostFailed
}
// 数据整合
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())
}
}
s.PrepareTweet(req.Uid, postFormated)
return (*web.TweetDetailResp)(postFormated), nil
}
func newLooseSrv(s *base.DaoServant, ac core.AppCache) api.Loose {
@ -451,9 +536,11 @@ func newLooseSrv(s *base.DaoServant, ac core.AppCache) api.Loose {
ac: ac,
userTweetsExpire: cs.UserTweetsExpire,
idxTweetsExpire: cs.IndexTweetsExpire,
tweetCommentsExpire: cs.TweetCommentsExpire,
prefixUserTweets: conf.PrefixUserTweets,
prefixIdxTweetsNewest: conf.PrefixIdxTweetsNewest,
prefixIdxTweetsHots: conf.PrefixIdxTweetsHots,
prefixIdxTweetsFollowing: conf.PrefixIdxTweetsFollowing,
prefixTweetComment: conf.PrefixTweetComment,
}
}

@ -82,6 +82,8 @@ func (s *privSrv) ThumbsDownTweetComment(req *web.TweetCommentThumbsReq) mir.Err
logrus.Errorf("thumbs down tweet comment error: %s req:%v", err, req)
return web.ErrThumbsDownTweetComment
}
// 缓存处理
onCommentActionEvent(req.TweetId, req.CommentId, _commentActionThumbsDown)
return nil
}
@ -90,6 +92,8 @@ func (s *privSrv) ThumbsUpTweetComment(req *web.TweetCommentThumbsReq) mir.Error
logrus.Errorf("thumbs up tweet comment error: %s req:%v", err, req)
return web.ErrThumbsUpTweetComment
}
// 缓存处理
onCommentActionEvent(req.TweetId, req.CommentId, _commentActionThumbsUp)
return nil
}
@ -355,10 +359,14 @@ func (s *privSrv) DeleteCommentReply(req *web.DeleteCommentReplyReq) mir.Error {
logrus.Errorf("s.deletePostCommentReply err: %s", err)
return web.ErrDeleteCommentFailed
}
// 缓存处理, 宽松处理错误
if comment, err := s.Ds.GetCommentByID(reply.CommentID); err == nil {
onCommentActionEvent(comment.PostID, comment.ID, _commentActionReplyDelete)
}
return nil
}
func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (*web.CreateCommentReplyResp, mir.Error) {
func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (_ *web.CreateCommentReplyResp, xerr mir.Error) {
var (
post *ms.Post
comment *ms.Comment
@ -435,6 +443,8 @@ func (s *privSrv) CreateCommentReply(req *web.CreateCommentReplyReq) (*web.Creat
})
}
}
// 缓存处理
onCommentActionEvent(comment.PostID, comment.ID, _commentActionReplyCreate)
return (*web.CreateCommentReplyResp)(reply), nil
}
@ -463,9 +473,22 @@ func (s *privSrv) DeleteComment(req *web.DeleteCommentReq) mir.Error {
logrus.Errorf("Ds.DeleteComment err: %s", err)
return web.ErrDeleteCommentFailed
}
onCommentActionEvent(comment.PostID, comment.ID, _commentActionDelete)
return nil
}
func (s *privSrv) HighlightComment(req *web.HighlightCommentReq) (*web.HighlightCommentResp, mir.Error) {
status, err := s.Ds.HighlightComment(req.Uid, req.CommentId)
if err == cs.ErrNoPermission {
return nil, web.ErrNoPermission
} else if err != nil {
return nil, web.ErrHighlightCommentFailed
}
return &web.HighlightCommentResp{
HighlightStatus: status,
}, nil
}
func (s *privSrv) CreateComment(req *web.CreateCommentReq) (_ *web.CreateCommentResp, xerr mir.Error) {
var (
mediaContents []string
@ -555,7 +578,8 @@ func (s *privSrv) CreateComment(req *web.CreateCommentReq) (_ *web.CreateComment
CommentID: comment.ID,
})
}
// 缓存处理
onCommentActionEvent(comment.PostID, comment.ID, _commentActionCreate)
return (*web.CreateCommentResp)(comment), nil
}

@ -42,37 +42,6 @@ type pubSrv struct {
*base.DaoServant
}
func (s *pubSrv) TweetDetail(req *web.TweetDetailReq) (*web.TweetDetailResp, mir.Error) {
post, err := s.Ds.GetPostByID(req.TweetId)
if err != nil {
logrus.Errorf("get tweet detail error[1]: %s", err)
return nil, web.ErrGetPostFailed
}
postContents, err := s.Ds.GetPostContentsByIDs([]int64{post.ID})
if err != nil {
logrus.Errorf("get tweet detail error[2]: %s", err)
return nil, web.ErrGetPostFailed
}
users, err := s.Ds.GetUsersByIDs([]int64{post.UserID})
if err != nil {
logrus.Errorf("get tweet detail error[3]: %s", err)
return nil, web.ErrGetPostFailed
}
// 数据整合
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())
}
}
// TODO: 暂时处理办法,后续需要优化去掉这个步骤
postFormated.Visibility = ms.PostVisibleT(postFormated.Visibility.ToOutValue())
return (*web.TweetDetailResp)(postFormated), nil
}
func (s *pubSrv) SendCaptcha(req *web.SendCaptchaReq) mir.Error {
ctx := context.Background()

@ -207,10 +207,3 @@ func checkPermision(user *ms.User, targetUserId int64) mir.Error {
}
return nil
}
// visbleTansform 可见性等价转换,暂时处理方式,后续需要去掉这个步骤
func visbleTansform(list []*ms.PostFormated) {
for _, post := range list {
post.Visibility = ms.PostVisibleT(post.Visibility.ToOutValue())
}
}

@ -15,7 +15,9 @@ type baseHttpService struct {
}
func (s *baseHttpService) registerRoute(srv Service, h func(e *gin.Engine)) {
h(s.server.e)
if h != nil {
h(s.server.e)
}
s.server.addService(srv)
}

@ -0,0 +1,63 @@
// 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 service
import (
"fmt"
"net/http"
"github.com/Masterminds/semver/v3"
"github.com/fatih/color"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/dao"
"github.com/rocboss/paopao-ce/internal/dao/cache"
"github.com/rocboss/paopao-ce/internal/metrics/prometheus"
)
var (
_ Service = (*metricsService)(nil)
)
type metricsService struct {
*baseHttpService
}
func (s *metricsService) Name() string {
return "MetricsService"
}
func (s *metricsService) Version() *semver.Version {
return semver.MustParse("v0.1.0")
}
func (s *metricsService) OnInit() error {
s.registerRoute(s, nil)
return nil
}
func (s *metricsService) String() string {
return fmt.Sprintf("listen on %s\n", color.GreenString("http://%s:%s", conf.MetricsServerSetting.HttpIp, conf.MetricsServerSetting.HttpPort))
}
func newMetricsService() Service {
addr := conf.MetricsServerSetting.HttpIp + ":" + conf.MetricsServerSetting.HttpPort
server := httpServers.from(addr, func() *httpServer {
ds, wc := dao.DataService(), cache.NewWebCache()
mux := http.NewServeMux()
mux.Handle("/metrics", prometheus.NewHandler(ds, wc))
return &httpServer{
baseServer: newBaseServe(),
server: &http.Server{
Addr: addr,
Handler: mux,
},
}
})
return &metricsService{
baseHttpService: &baseHttpService{
server: server,
},
}
}

@ -10,7 +10,6 @@ import (
"github.com/Masterminds/semver/v3"
"github.com/fatih/color"
"github.com/gin-gonic/gin"
"github.com/rocboss/paopao-ce/internal/conf"
)
@ -31,7 +30,7 @@ func (s *pprofService) Version() *semver.Version {
}
func (s *pprofService) OnInit() error {
s.registerRoute(s, func(*gin.Engine) {})
s.registerRoute(s, nil)
return nil
}
@ -43,10 +42,8 @@ func newPprofService() Service {
addr := conf.PprofServerSetting.HttpIp + ":" + conf.PprofServerSetting.HttpPort
// notice this step just to register pprof server to start. don't share server with pprof.
server := httpServers.from(addr, func() *httpServer {
engine := newWebEngine()
return &httpServer{
baseServer: newBaseServe(),
e: engine,
server: &http.Server{
Addr: addr,
Handler: http.DefaultServeMux,

@ -75,6 +75,9 @@ func newService() (ss []Service) {
"Pprof": func() {
ss = append(ss, newPprofService())
},
"Metrics": func() {
ss = append(ss, newMetricsService())
},
})
return
}

@ -30,7 +30,7 @@ func (s *webService) Name() string {
}
func (s *webService) Version() *semver.Version {
return semver.MustParse("v0.1.0")
return semver.MustParse("v0.5.0")
}
func (s *webService) OnInit() error {

@ -29,4 +29,7 @@ type Loose struct {
// TweetComments 获取动态评论
TweetComments func(Get, web.TweetCommentsReq) web.TweetCommentsResp `mir:"/post/comments"`
// TweetDetail 获取动态详情
TweetDetail func(Get, web.TweetDetailReq) web.TweetDetailResp `mir:"/post"`
}

@ -54,6 +54,9 @@ type Priv struct {
// DeletePostComment 删除动态评论
DeleteComment func(Delete, web.DeleteCommentReq) `mir:"/post/comment"`
// HighlightComment 精选动态评论
HighlightComment func(Post, web.HighlightCommentReq) web.HighlightCommentResp `mir:"/post/comment/highlight"`
// CreateCommentReply 发布评论回复
CreateCommentReply func(Post, web.CreateCommentReplyReq) web.CreateCommentReplyResp `mir:"/post/comment/reply"`
@ -66,7 +69,7 @@ type Priv struct {
// ThumbsDownTweetComment 点踩评论
ThumbsDownTweetComment func(Post, web.TweetCommentThumbsReq) `mir:"/tweet/comment/thumbsdown"`
// ThumbsUpTweetReply 点赞评论回复
// ThumbsUpTweetReply 点赞评论回复·
ThumbsUpTweetReply func(Post, web.TweetReplyThumbsReq) `mir:"/tweet/reply/thumbsup"`
// ThumbsDownTweetReply 点踩评论回复

@ -28,7 +28,4 @@ type Pub struct {
// SendCaptcha 发送验证码
SendCaptcha func(Post, web.SendCaptchaReq) `mir:"/captcha"`
// TweetDetail 获取动态详情
TweetDetail func(Get, web.TweetDetailReq) web.TweetDetailResp `mir:"/post"`
}

@ -0,0 +1 @@
ALTER TABLE `p_comment` DROP COLUMN `is_essence`;

@ -0,0 +1 @@
ALTER TABLE `p_comment` ADD COLUMN `is_essence` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '是否精选';

@ -0,0 +1,3 @@
ALTER TABLE `p_comment` DROP COLUMN `reply_count`;
DROP TABLE IF EXISTS `p_comment_metric`;
DROP TABLE IF EXISTS `p_user_metric`;

@ -0,0 +1,48 @@
ALTER TABLE `p_comment` ADD COLUMN `reply_count` int unsigned NOT NULL DEFAULT 0 COMMENT '回复数';
UPDATE p_comment comment
SET reply_count = (
SELECT count(*) FROM p_comment_reply reply WHERE reply.comment_id=comment.id AND reply.is_del=0
)
WHERE is_del=0;
CREATE TABLE `p_comment_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`comment_id` bigint unsigned NOT NULL,
`rank_score` bigint unsigned NOT NULL DEFAULT 0,
`incentive_score` int unsigned NOT NULL DEFAULT 0,
`decay_factor` int unsigned NOT NULL DEFAULT 0,
`motivation_factor` int unsigned NOT NULL DEFAULT 0,
`is_del` tinyint NOT NULL DEFAULT 0,
`created_on` bigint unsigned NOT NULL DEFAULT 0,
`modified_on` bigint unsigned NOT NULL DEFAULT 0,
`deleted_on` bigint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_comment_metric_comment_id_rank_score` (`comment_id`, `rank_score`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_comment_metric (comment_id, rank_score, created_on)
SELECT id AS comment_id,
reply_count*2 + thumbs_up_count*4 - thumbs_down_count AS rank_score,
created_on
FROM p_comment
WHERE is_del=0;
CREATE TABLE `p_user_metric` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`tweets_count` int unsigned NOT NULL DEFAULT 0,
`latest_trends_on` bigint unsigned NOT NULL DEFAULT 0 COMMENT '最新动态时间',
`is_del` tinyint NOT NULL DEFAULT 0,
`created_on` bigint unsigned NOT NULL DEFAULT 0,
`modified_on` bigint unsigned NOT NULL DEFAULT 0,
`deleted_on` bigint unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user_metric_user_id_tweets_count_trends` (`user_id`, `tweets_count`, `latest_trends_on`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO p_user_metric (user_id, tweets_count)
SELECT user_id, count(*) AS tweets_count
FROM p_post
WHERE is_del=0
GROUP BY user_id;

@ -0,0 +1 @@
ALTER TABLE p_comment DROP COLUMN is_essence;

@ -0,0 +1 @@
ALTER TABLE p_comment ADD COLUMN is_essence SMALLINT NOT NULL DEFAULT 0;

@ -0,0 +1,3 @@
ALTER TABLE p_comment DROP COLUMN IF EXISTS reply_count;
DROP TABLE IF EXISTS p_comment_metric;
DROP TABLE IF EXISTS p_user_metric;

@ -0,0 +1,51 @@
ALTER TABLE p_comment ADD COLUMN reply_count INT NOT NULL DEFAULT 0;
WITH comment_reply AS (
SELECT comment_id, count(*) AS count
FROM p_comment_reply
WHERE is_del=0
GROUP By comment_id
)
UPDATE p_comment comment
SET reply_count = reply.count
FROM comment_reply reply
WHERE comment.id = reply.comment_id;
CREATE TABLE p_comment_metric (
id BIGSERIAL PRIMARY KEY,
comment_id BIGINT NOT NULL,
rank_score BIGINT NOT NULL DEFAULT 0,
incentive_score INT NOT NULL DEFAULT 0,
decay_factor INT NOT NULL DEFAULT 0,
motivation_factor INT NOT NULL DEFAULT 0,
is_del SMALLINT NOT NULL DEFAULT 0,
created_on BIGINT NOT NULL DEFAULT 0,
modified_on BIGINT NOT NULL DEFAULT 0,
deleted_on BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_comment_metric_comment_id_rank_score ON p_comment_metric USING btree (comment_id, rank_score);
INSERT INTO p_comment_metric (comment_id, rank_score, created_on)
SELECT id AS comment_id,
reply_count*2 + thumbs_up_count*4 - thumbs_down_count AS rank_score,
created_on
FROM p_comment
WHERE is_del=0;
CREATE TABLE p_user_metric (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
tweets_count INT NOT NULL DEFAULT 0,
latest_trends_on BIGINT NOT NULL DEFAULT 0,
is_del SMALLINT NOT NULL DEFAULT 0,
created_on BIGINT NOT NULL DEFAULT 0,
modified_on BIGINT NOT NULL DEFAULT 0,
deleted_on BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_user_metric_user_id_tweets_count_trends ON p_user_metric USING btree (user_id, tweets_count, latest_trends_on);
INSERT INTO p_user_metric (user_id, tweets_count)
SELECT user_id, count(*) AS tweets_count
FROM p_post
WHERE is_del=0
GROUP BY user_id;

@ -0,0 +1 @@
ALTER TABLE "p_comment" DROP COLUMN "is_essence";

@ -0,0 +1 @@
ALTER TABLE "p_comment" ADD COLUMN "is_essence" integer NOT NULL DEFAULT 0;

@ -0,0 +1,3 @@
ALTER TABLE "p_comment" DROP COLUMN "reply_count";
DROP TABLE IF EXISTS "p_comment_metric";
DROP TABLE IF EXISTS "p_user_metric";

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save