Merge branch 'x/gorm' into x/sqlx

r/paopao-ce-plus
Michael Li 1 year ago
commit 4f1b995412
No known key found for this signature in database

@ -7,6 +7,9 @@
"request": "launch",
"mode": "exec",
"program": "${workspaceFolder}/.vscode/__debug_bin",
"args":[
"serve"
],
"preLaunchTask": "go: build (debug)",
"cwd": "${workspaceFolder}"
}

@ -7,13 +7,29 @@ All notable changes to paopao-ce are documented in this file.
- use compiler profile-guided optimization (PGO) to further optimize builds. [#327](https://github.com/rocboss/paopao-ce/pull/327)
- frontend: re-add stars page embed to profile page. [#339](https://github.com/rocboss/paopao-ce/pull/339)
- simple support for user posts filter by style(post/comment/media/star). [#345](https://github.com/rocboss/paopao-ce/pull/345)
mirgration database first(sql ddl file in `scripts/migration/**/*_create_view_post_filter.up.sql`):
```sql
CREATE VIEW p_post_by_media AS SELECT post.*FROM (SELECT DISTINCT post_id FROM p_post_content WHERE (TYPE=3 OR TYPE=4 OR TYPE=7 OR TYPE=8) AND is_del=0) media JOIN p_post post ON media.post_id=post.ID WHERE post.is_del=0;
CREATE VIEW p_post_by_comment AS SELECT P.*,C.user_id comment_user_id FROM (SELECT post_id,user_id FROM p_comment WHERE is_del=0 UNION SELECT post_id,reply.user_id user_id FROM p_comment_reply reply JOIN p_comment COMMENT ON reply.comment_id=COMMENT.ID WHERE reply.is_del=0 AND COMMENT.is_del=0) C JOIN p_post P ON C.post_id=P.ID WHERE P.is_del=0;
```
migration database first(sql ddl file in `scripts/migration/**/*_create_view_post_filter.up.sql`):
```sql
CREATE VIEW p_post_by_media AS SELECT post.*FROM (SELECT DISTINCT post_id FROM p_post_content WHERE (TYPE=3 OR TYPE=4 OR TYPE=7 OR TYPE=8) AND is_del=0) media JOIN p_post post ON media.post_id=post.ID WHERE post.is_del=0;
CREATE VIEW p_post_by_comment AS SELECT P.*,C.user_id comment_user_id FROM (SELECT post_id,user_id FROM p_comment WHERE is_del=0 UNION SELECT post_id,reply.user_id user_id FROM p_comment_reply reply JOIN p_comment COMMENT ON reply.comment_id=COMMENT.ID WHERE reply.is_del=0 AND COMMENT.is_del=0) C JOIN p_post P ON C.post_id=P.ID WHERE P.is_del=0;
```
- add user highlight tweet support include custom tweet set to highlight and list in user/profile page.
- add cli subcommand to start paopao-ce serve or other task. [#354](https://github.com/rocboss/paopao-ce/pull/354)
- add `Friendship` feature . [#355](https://github.com/rocboss/paopao-ce/pull/355)
migration database first(sql ddl file in `scripts/migration/**/*_user_following.up.sql`):
```sql
DROP TABLE IF EXISTS p_following;
CREATE TABLE p_following (ID BIGSERIAL PRIMARY KEY,user_id BIGINT NOT NULL,follow_id BIGINT NOT NULL,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_following_user_follow ON p_following USING btree (user_id,follow_id);
```
custom set config.yaml in `Features` section add `Followship` to enable Followship feature:
```yaml
...
# add Followship to enable this feature
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "MinIO", "Followship"]
Base: ["Redis", "PhoneBind"]
...
```
### Changed
- change man content width to 600px and optimize tweet/comment/replay text length. [#333](https://github.com/rocboss/paopao-ce/pull/333)

@ -3,18 +3,22 @@
## paopao-ce roadmap
#### dev+
* [ ] add `Followship` feature
* [ ] add `Auth:Bcrypt` feature
* [ ] add `Auth:MD5` feature (just for compatible)
* [x] add extend base ORM code for implement data logic base sqlx/sqlc
* [ ] optimize media tweet submit logic
* [ ] optimize search logic service
#### v0.4.0
* [x] add `Followship` feature.
* [x] add extend base ORM code for implement data logic base sqlx/sqlc.
* [x] user/profile page add comment/highlight/media/likes sub-page.
* [x] add tweet highlight feature to enable user set a tweet as highlight.
#### v0.3.0
* [x] remove `Deprecated:OldWeb` feature
* [x] add user topic follow feature support
* [x] add tweet link share support
* [ ] add comment thumbsUp/thumbsDown support
* [x] add comment thumbsUp/thumbsDown support
* [x] add `RedisCacheIndex` feature
* [x] add `Sentry` feature

@ -12,7 +12,7 @@ Server: # 服务设置
ReadTimeout: 60
WriteTimeout: 60
Features:
Default: ["Web", "Frontend:EmbedWeb", "Zinc", "LocalOSS", "MySQL", "BigCacheIndex", "LoggerZinc", "Friendship"]
Default: ["Web", "Frontend:EmbedWeb", "Zinc", "LocalOSS", "MySQL", "BigCacheIndex", "LoggerZinc", "Friendship", "Followship"]
Develop: ["Base", "MySQL", "BigCacheIndex", "Meili", "Sms", "AliOSS", "LoggerMeili", "OSS:Retention"]
Demo: ["Base", "MySQL", "Option", "Zinc", "Sms", "MinIO", "LoggerZinc", "Migration"]
Slim: ["Base", "Sqlite3", "LocalOSS", "LoggerFile", "OSS:TempDir"]

@ -1,6 +1,6 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 22110409| 北野 | 2022-11-04 | 2022-11-21 | v0.1 | 提议 |
| 22110409| 北野 | 2022-11-04 | 2023-08-14 | v1.0 | 提议 |
### 关于Followship功能项的设计
Followship是实现类似Twitter Timeline模式**关注者模型**的时间线信息流广场推文列表的生成将主要与推文时间、用户的关注者相关。Twitter的推文消息流是非常智能的用户体验也非常好这得益于其背后的智能推荐算法以及完善的关注者模型体系当然还有很多其他机制共同作用下的结果。本提按作为一个总纲为paopao-ce引入类似的机制这将是一个持续完善的缓慢过程一切都是为了用户体验用户就是上帝用户需要什么paopao-ce就努力提供什么
@ -35,6 +35,9 @@
#### 方案二
在方案一的基础上通过引入 *图数据库* 实现可选需求。待研究、设计~
#### 设计细节
* 参考实现(PR):
[add Followship feature #355](https://github.com/rocboss/paopao-ce/pull/355)
### 疑问
@ -57,4 +60,7 @@
* 初始文档, 先占个位置;
#### v0.1(2022-11-21) - 北野
* 添加初始内容;
* 添加初始内容;
#### v1.0(2023-08-14) - 北野
* 添加参考实现;

@ -11,7 +11,7 @@ require (
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible
github.com/allegro/bigcache/v3 v3.1.0
github.com/bufbuild/connect-go v1.10.0
github.com/bytedance/sonic v1.9.2
github.com/bytedance/sonic v1.10.0
github.com/cockroachdb/errors v1.10.0
github.com/disintegration/imaging v1.6.2
github.com/fatih/color v1.15.0
@ -30,8 +30,8 @@ require (
github.com/minio/minio-go/v7 v7.0.61
github.com/onsi/ginkgo/v2 v2.11.0
github.com/onsi/gomega v1.27.10
github.com/pyroscope-io/client v0.7.2-0.20230804044655-36760e422a95
github.com/redis/rueidis v1.0.14
github.com/pyroscope-io/client v0.7.2
github.com/redis/rueidis v1.0.15
github.com/sirupsen/logrus v1.9.3
github.com/smartwalle/alipay/v3 v3.2.15
github.com/sourcegraph/conc v0.3.0
@ -47,8 +47,8 @@ require (
gorm.io/driver/mysql v1.5.1
gorm.io/driver/postgres v1.5.2
gorm.io/driver/sqlite v1.5.2
gorm.io/gorm v1.25.2
gorm.io/plugin/dbresolver v1.4.2
gorm.io/gorm v1.25.3
gorm.io/plugin/dbresolver v1.4.4
gorm.io/plugin/soft_delete v1.2.1
modernc.org/sqlite v1.25.0
)
@ -57,7 +57,8 @@ replace github.com/jmoiron/sqlx => github.com/bitbus/sqlx v1.4.0
require (
github.com/andybalholm/brotli v1.0.5 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // 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
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/redact v1.1.5 // indirect

@ -198,8 +198,9 @@ github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8n
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/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.2 h1:GDaNjuWSGu09guE9Oql0MSTNhNCLlWwO8y/xM5BzcbM=
github.com/bytedance/sonic v1.9.2/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@ -213,8 +214,11 @@ github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOo
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=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -854,6 +858,7 @@ github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@ -1110,12 +1115,12 @@ github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
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/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/pyroscope-io/client v0.7.2-0.20230804044655-36760e422a95 h1:ZpE0l+tD3ykwYWQh/UVdIpxHmr9B6DTtOUBiGw3lB2E=
github.com/pyroscope-io/client v0.7.2-0.20230804044655-36760e422a95/go.mod h1:FEocnjn+Ngzxy6EtU9ZxXWRvQ0+pffkrBxHLnPpxwi8=
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=
github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4=
github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE=
github.com/redis/rueidis v1.0.14 h1:qdFZahk1F/2L+sZeOECx5E2N5J4Qc51b7ezSUpQXJfs=
github.com/redis/rueidis v1.0.14/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/redis/rueidis v1.0.15 h1:KjTaoP4ab6lpxyCwgIEZ3/rqvKfKnbICe83tVaaItxQ=
github.com/redis/rueidis v1.0.15/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
@ -2010,10 +2015,11 @@ gorm.io/gorm v1.21.4/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
gorm.io/gorm v1.23.0/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/plugin/dbresolver v1.4.2 h1:IeLSH20ayxbo4rN6HMIQ0ccdsh/fkLK23pp6ivZrqBI=
gorm.io/plugin/dbresolver v1.4.2/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gorm.io/gorm v1.25.3 h1:zi4rHZj1anhZS2EuEODMhDisGy+Daq9jtPrNGgbQYD8=
gorm.io/gorm v1.25.3/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/plugin/dbresolver v1.4.4 h1:Dtpr/jSVy48f+BnkPwdoHTLaxcE977wJ7QPt7fxI5Hs=
gorm.io/plugin/dbresolver v1.4.4/go.mod h1:l4Cn87EHLEYuqUncpEeTC2tTJQkjngPSD+lo8hIvcT0=
gorm.io/plugin/soft_delete v1.2.1 h1:qx9D/c4Xu6w5KT8LviX8DgLcB9hkKl6JC9f44Tj7cGU=
gorm.io/plugin/soft_delete v1.2.1/go.mod h1:Zv7vQctOJTGOsJ/bWgrN1n3od0GBAZgnLjEx+cApLGk=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
@ -2120,6 +2126,7 @@ modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfp
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/zappy v1.0.0/go.mod h1:hHe+oGahLVII/aTTyWK/b53VDHMAGCBYYeZ9sn83HC4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

@ -25,6 +25,7 @@ const (
TableComment = "comment"
TableCommentContent = "comment_content"
TableCommentReply = "comment_reply"
TableFollowing = "following"
TableContact = "contact"
TableContactGroup = "contact_group"
TableMessage = "message"

@ -305,6 +305,7 @@ func (s *databaseConf) TableNames() (res TableNameMap) {
TableComment,
TableCommentContent,
TableCommentReply,
TableFollowing,
TableContact,
TableContactGroup,
TableMessage,

@ -30,6 +30,7 @@ type DataService interface {
// 用户服务
UserManageService
ContactManageService
FollowingManageService
// 安全服务
SecurityService

@ -7,10 +7,11 @@ package ms
type (
ContactItem struct {
UserId int64 `json:"user_id"`
UserName string `db:"username" json:"username"`
Username string `db:"username" json:"username"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
Phone string `json:"phone"`
Phone string `json:"phone,omitempty"`
IsFollow bool `json:"is_follow,omitempty"`
}
ContactList struct {

@ -26,3 +26,13 @@ type ContactManageService interface {
GetContacts(userId int64, offset int, limit int) (*ms.ContactList, error)
IsFriend(userID int64, friendID int64) bool
}
// FollowingManageService 关注管理服务
type FollowingManageService interface {
FollowUser(userId int64, followId int64) error
UnfollowUser(userId int64, followId int64) error
ListFollows(userId int64, limit, offset int) (*ms.ContactList, error)
ListFollowings(userId int64, limit, offset int) (*ms.ContactList, error)
GetFollowCount(userId int64) (int64, int64, error)
IsFollow(userId int64, followId int64) bool
}

@ -249,7 +249,7 @@ func (s *contactManageSrv) GetContacts(userId int64, offset int, limit int) (*ms
if c.User != nil {
resp.Contacts = append(resp.Contacts, ms.ContactItem{
UserId: c.FriendId,
UserName: c.User.Username,
Username: c.User.Username,
Nickname: c.User.Nickname,
Avatar: c.User.Avatar,
Phone: c.User.Phone,

@ -0,0 +1,87 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package dbr
import (
"github.com/sirupsen/logrus"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
type Following struct {
*Model
User *User `json:"-" gorm:"foreignKey:ID;references:FollowId"`
UserId int64 `json:"user_id"`
FollowId int64 `json:"friend_id"`
}
func (f *Following) GetFollowing(db *gorm.DB, userId, followId int64) (*Following, error) {
var following Following
err := db.Omit("User").Unscoped().Where("user_id = ? AND follow_id = ?", userId, followId).First(&following).Error
if err != nil {
logrus.Debugf("Following.GetFollowing get following error:%s", err)
return nil, err
}
return &following, nil
}
func (f *Following) DelFollowing(db *gorm.DB, userId, followId int64) error {
return db.Omit("User").Unscoped().Where("user_id = ? AND follow_id = ?", userId, followId).Delete(f).Error
}
func (f *Following) ListFollows(db *gorm.DB, userId int64, limit int, offset int) (res []*Following, total int64, err error) {
db = db.Model(f).Where("user_id=?", userId)
if err = db.Count(&total).Error; err != nil {
return
}
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
db.Joins("User").Order(clause.OrderByColumn{Column: clause.Column{Table: "User", Name: "nickname"}, Desc: false})
if err = db.Find(&res).Error; err != nil {
return
}
return
}
func (f *Following) ListFollowingIds(db *gorm.DB, userId int64, limit, offset int) (ids []int64, total int64, err error) {
db = db.Model(f).Where("follow_id=?", userId)
if err = db.Count(&total).Error; err != nil {
return
}
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
if err = db.Omit("User").Select("user_id").Find(&ids).Error; err != nil {
return
}
return
}
func (f *Following) FollowCount(db *gorm.DB, userId int64) (follows int64, followings int64, err error) {
if err = db.Model(f).Where("user_id=?", userId).Count(&follows).Error; err != nil {
return
}
if err = db.Model(f).Where("follow_id=?", userId).Count(&followings).Error; err != nil {
return
}
return
}
func (s *Following) IsFollow(db *gorm.DB, userId int64, followId int64) bool {
if _, err := s.GetFollowing(db, userId, followId); err == nil {
return true
}
return false
}
func (f *Following) Create(db *gorm.DB) (*Following, error) {
err := db.Omit("User").Create(f).Error
return f, err
}
func (c *Following) UpdateInUnscoped(db *gorm.DB) error {
return db.Unscoped().Omit("User").Save(c).Error
}

@ -0,0 +1,104 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package jinzhu
import (
"github.com/rocboss/paopao-ce/internal/core"
"github.com/rocboss/paopao-ce/internal/core/ms"
"github.com/rocboss/paopao-ce/internal/dao/jinzhu/dbr"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var (
_ core.FollowingManageService = (*followingManageSrv)(nil)
)
type followingManageSrv struct {
db *gorm.DB
f *dbr.Following
u *dbr.User
}
func newFollowingManageService(db *gorm.DB) core.FollowingManageService {
return &followingManageSrv{
db: db,
f: &dbr.Following{},
u: &dbr.User{},
}
}
func (s *followingManageSrv) FollowUser(userId int64, followId int64) error {
if _, err := s.f.GetFollowing(s.db, userId, followId); err != nil {
following := &dbr.Following{
UserId: userId,
FollowId: followId,
}
if _, err = following.Create(s.db); err != nil {
logrus.Errorf("contactManageSrv.fetchOrNewContact create new contact err:%s", err)
return err
}
}
return nil
}
func (s *followingManageSrv) UnfollowUser(userId int64, followId int64) error {
return s.f.DelFollowing(s.db, userId, followId)
}
func (s *followingManageSrv) ListFollows(userId int64, limit, offset int) (*ms.ContactList, error) {
follows, totoal, err := s.f.ListFollows(s.db, userId, limit, offset)
if err != nil {
return nil, err
}
res := &ms.ContactList{
Total: totoal,
}
for _, f := range follows {
res.Contacts = append(res.Contacts, ms.ContactItem{
UserId: f.User.ID,
Username: f.User.Username,
Nickname: f.User.Nickname,
Avatar: f.User.Avatar,
IsFollow: true,
})
}
return res, nil
}
func (s *followingManageSrv) ListFollowings(userId int64, limit, offset int) (*ms.ContactList, error) {
followingIds, totoal, err := s.f.ListFollowingIds(s.db, userId, limit, offset)
if err != nil {
return nil, err
}
followings, err := s.u.ListUserInfoById(s.db, followingIds)
if err != nil {
return nil, err
}
res := &ms.ContactList{
Total: totoal,
}
for _, user := range followings {
res.Contacts = append(res.Contacts, ms.ContactItem{
UserId: user.ID,
Username: user.Username,
Nickname: user.Nickname,
Avatar: user.Avatar,
IsFollow: s.IsFollow(userId, user.ID),
})
}
return res, nil
}
func (s *followingManageSrv) GetFollowCount(userId int64) (int64, int64, error) {
return s.f.FollowCount(s.db, userId)
}
func (s *followingManageSrv) IsFollow(userId int64, followId int64) bool {
if _, err := s.f.GetFollowing(s.db, userId, followId); err == nil {
return true
}
return false
}

@ -17,6 +17,7 @@ var (
_comment_ string
_commentContent_ string
_commentReply_ string
_following_ string
_contact_ string
_contactGroup_ string
_message_ string
@ -42,6 +43,7 @@ func initTableName() {
_comment_ = m[conf.TableComment]
_commentContent_ = m[conf.TableCommentContent]
_commentReply_ = m[conf.TableCommentReply]
_following_ = m[conf.TableFollowing]
_contact_ = m[conf.TableContact]
_contactGroup_ = m[conf.TableContactGroup]
_message_ = m[conf.TableMessage]

@ -42,6 +42,7 @@ type dataSrv struct {
core.CommentManageService
core.UserManageService
core.ContactManageService
core.FollowingManageService
core.SecurityService
core.AttachmentCheckService
}
@ -110,6 +111,7 @@ func NewDataService() (core.DataService, core.VersionInfo) {
CommentManageService: newCommentManageService(db),
UserManageService: newUserManageService(db),
ContactManageService: newContactManageService(db),
FollowingManageService: newFollowingManageService(db),
SecurityService: newSecurityService(db, pvs),
AttachmentCheckService: security.NewAttachmentCheckService(),
}

@ -36,8 +36,8 @@ type UserInfoResp struct {
Phone string `json:"phone"`
IsAdmin bool `json:"is_admin"`
CreatedOn int64 `json:"created_on"`
Follows int `json:"follows"`
Followings int `json:"followings"`
Follows int64 `json:"follows"`
Followings int64 `json:"followings"`
}
type GetUnreadMsgCountReq struct {

@ -76,8 +76,8 @@ type GetUserProfileResp struct {
IsFriend bool `json:"is_friend"`
IsFollowing bool `json:"is_following"`
CreatedOn int64 `json:"created_on"`
Follows int `json:"follows"`
Followings int `json:"followings"`
Follows int64 `json:"follows"`
Followings int64 `json:"followings"`
}
type TopicListReq struct {

@ -79,6 +79,11 @@ var (
ErrDeleteFriendFailed = xerror.NewError(80006, "删除好友失败")
ErrGetContactsFailed = xerror.NewError(80007, "获取联系人列表失败")
ErrNoActionToSelf = xerror.NewError(80008, "不允许对自己操作")
ErrFolloUserFailed = xerror.NewError(80100, "关注失败")
ErrUnfollowUserFailed = xerror.NewError(80101, "取消关注失败")
ErrListFollowsFailed = xerror.NewError(80102, "获取关注列表失败")
ErrListFollowingsFailed = xerror.NewError(80103, "获取粉丝列表列表失败")
ErrGetFollowCountFailed = xerror.NewError(80104, "获取关注计数信息失败")
ErrFollowTopicFailed = xerror.NewError(90001, "关注话题失败")
ErrUnfollowTopicFailed = xerror.NewError(90002, "取消关注话题失败")

@ -60,6 +60,10 @@ func (s *coreSrv) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Erro
if user.Model == nil || user.ID < 0 {
return nil, xerror.UnauthorizedAuthNotExist
}
follows, followings, err := s.Ds.GetFollowCount(user.ID)
if err != nil {
return nil, web.ErrGetFollowCountFailed
}
resp := &web.UserInfoResp{
Id: user.ID,
Nickname: user.Nickname,
@ -69,8 +73,8 @@ func (s *coreSrv) GetUserInfo(req *web.UserInfoReq) (*web.UserInfoResp, mir.Erro
Balance: user.Balance,
IsAdmin: user.IsAdmin,
CreatedOn: user.CreatedOn,
Follows: 0, // TODO
Followings: 0, // TODO
Follows: follows,
Followings: followings,
}
if user.Phone != "" && len(user.Phone) == 11 {
resp.Phone = user.Phone[0:3] + "****" + user.Phone[7:]

@ -8,10 +8,10 @@ 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/core/ms"
"github.com/rocboss/paopao-ce/internal/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/sirupsen/logrus"
)
var (
@ -28,27 +28,49 @@ func (s *followshipSrv) Chain() gin.HandlersChain {
}
func (s *followshipSrv) ListFollowings(r *web.ListFollowingsReq) (*web.ListFollowingsResp, mir.Error) {
// TODO
res := ms.ContactList{}
he, err := s.Ds.GetUserByUsername(r.Username)
if err != nil {
logrus.Errorf("Ds.GetUserByUsername err: %s", err)
return nil, web.ErrNoExistUsername
}
res, err := s.Ds.ListFollowings(he.ID, r.PageSize, r.Page-1)
if err != nil {
logrus.Errorf("Ds.ListFollowings err: %s", err)
return nil, web.ErrListFollowingsFailed
}
resp := base.PageRespFrom(res.Contacts, r.Page, r.PageSize, res.Total)
return (*web.ListFollowingsResp)(resp), nil
}
func (s *followshipSrv) ListFollows(r *web.ListFollowsReq) (*web.ListFollowsResp, mir.Error) {
// TODO
res := ms.ContactList{}
he, err := s.Ds.GetUserByUsername(r.Username)
if err != nil {
logrus.Errorf("Ds.GetUserByUsername err: %s", err)
return nil, web.ErrNoExistUsername
}
res, err := s.Ds.ListFollows(he.ID, r.PageSize, r.Page-1)
if err != nil {
logrus.Errorf("Ds.ListFollows err: %s", err)
return nil, web.ErrListFollowsFailed
}
resp := base.PageRespFrom(res.Contacts, r.Page, r.PageSize, res.Total)
return (*web.ListFollowsResp)(resp), nil
}
func (s *followshipSrv) UnfollowUser(r *web.UnfollowUserReq) mir.Error {
// TODO
return web.ErrNotImplemented
if err := s.Ds.UnfollowUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.UnfollowUser err: %s userId: %d followId: %d", err, r.User.ID, r.UserId)
return web.ErrUnfollowUserFailed
}
return nil
}
func (s *followshipSrv) FollowUser(r *web.FollowUserReq) mir.Error {
// TODO
return web.ErrNotImplemented
if err := s.Ds.FollowUser(r.User.ID, r.UserId); err != nil {
logrus.Errorf("Ds.FollowUser err: %s userId: %d followId: %d", err, r.User.ID, r.UserId)
return web.ErrUnfollowUserFailed
}
return nil
}
func newFollowshipSrv(s *base.DaoServant) api.Followship {

@ -177,17 +177,26 @@ func (s *looseSrv) GetUserProfile(req *web.GetUserProfileReq) (*web.GetUserProfi
if req.User != nil && req.User.ID != he.ID {
isFriend = s.Ds.IsFriend(req.User.ID, he.ID)
}
isFollowing := false
if req.User != nil {
isFollowing = s.Ds.IsFollow(req.User.ID, he.ID)
}
follows, followings, err := s.Ds.GetFollowCount(he.ID)
if err != nil {
return nil, web.ErrGetPostsFailed
}
return &web.GetUserProfileResp{
ID: he.ID,
Nickname: he.Nickname,
Username: he.Username,
Status: he.Status,
Avatar: he.Avatar,
IsAdmin: he.IsAdmin,
IsFriend: isFriend,
CreatedOn: he.CreatedOn,
Follows: 0, // TODO
Followings: 0, // TODO
ID: he.ID,
Nickname: he.Nickname,
Username: he.Username,
Status: he.Status,
Avatar: he.Avatar,
IsAdmin: he.IsAdmin,
IsFriend: isFriend,
IsFollowing: isFollowing,
CreatedOn: he.CreatedOn,
Follows: follows,
Followings: followings,
}, nil
}

@ -0,0 +1 @@
DROP TABLE IF EXISTS `p_following`;

@ -0,0 +1,11 @@
CREATE TABLE `p_following` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`follow_id` bigint unsigned NOT NULL,
`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_following_user_follow` (`user_id`,`follow_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

@ -0,0 +1 @@
DROP TABLE IF EXISTS p_following;

@ -0,0 +1,10 @@
CREATE TABLE p_following (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
follow_id BIGINT NOT NULL,
is_del SMALLINT NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
created_on BIGINT NOT NULL DEFAULT 0,
modified_on BIGINT NOT NULL DEFAULT 0,
deleted_on BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_following_user_follow ON p_following USING btree (user_id, follow_id);

@ -0,0 +1 @@
DROP TABLE IF EXISTS "p_following";

@ -0,0 +1,15 @@
CREATE TABLE "p_following" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"follow_id" integer NOT NULL,
"is_del" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
CREATE INDEX "idx_following_user_follow"
ON "p_following" (
"user_id" ASC,
"follow_id" ASC
);

@ -318,6 +318,22 @@ CREATE TABLE `p_user` (
KEY `idx_user_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=100058 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户';
-- ----------------------------
-- Table structure for p_following
-- ----------------------------
DROP TABLE IF EXISTS `p_following`;
CREATE TABLE `p_following` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL,
`follow_id` bigint unsigned NOT NULL,
`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_following_user_follow` (`user_id`,`follow_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ----------------------------
-- Table structure for p_contact
-- ----------------------------

@ -265,6 +265,18 @@ CREATE TABLE p_user (
CREATE UNIQUE INDEX idx_user_username ON p_user USING btree (username);
CREATE INDEX idx_user_phone ON p_user USING btree (phone);
DROP TABLE IF EXISTS p_following;
CREATE TABLE p_following (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
follow_id BIGINT NOT NULL,
is_del SMALLINT NOT NULL DEFAULT 0, -- 是否删除, 0否, 1是
created_on BIGINT NOT NULL DEFAULT 0,
modified_on BIGINT NOT NULL DEFAULT 0,
deleted_on BIGINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_following_user_follow ON p_following USING btree (user_id, follow_id);
DROP TABLE IF EXISTS p_contact;
CREATE TABLE p_contact (
id BIGSERIAL PRIMARY KEY,

@ -113,6 +113,21 @@ CREATE TABLE "p_tweet_comment_thumbs" (
"is_del" integer NOT NULL DEFAULT 0 -- 是否删除 0 为未删除、1 为已删除
);
-- ----------------------------
-- Table structure for p_following
-- ----------------------------
DROP TABLE IF EXISTS "p_following";
CREATE TABLE "p_following" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"follow_id" integer NOT NULL,
"is_del" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_contact
-- ----------------------------
@ -464,6 +479,15 @@ ON "p_tweet_comment_thumbs"(
"tweet_id" ASC
);
-- ----------------------------
-- Indexes structure for table p_following
-- ----------------------------
CREATE INDEX "idx_following_user_follow"
ON "p_following" (
"user_id" ASC,
"follow_id" ASC
);
-- ----------------------------
-- Indexes structure for table p_contact
-- ----------------------------

Loading…
Cancel
Save