更新followship功能

pull/361/head
HXY 2 years ago
commit 5d133a3757

@ -47,7 +47,7 @@ jobs:
name: Test
strategy:
matrix:
go-version: [ 1.20.x ]
go-version: [ 1.20.x, 1.21.x ]
platform: [ ubuntu-latest, macos-latest ]
runs-on: ${{ matrix.platform }}
steps:
@ -66,7 +66,7 @@ jobs:
name: TestOnWindows
strategy:
matrix:
go-version: [ 1.20.x ]
go-version: [ 1.20.x, 1.21.x ]
platform: [ windows-latest ]
runs-on: ${{ matrix.platform }}
steps:

5
.gitignore vendored

@ -4,7 +4,10 @@ __debug_bin
!*.example
/config.yaml
*.log
paopao-ce*
/paopao-ce
/paopao
/.env
/.envrc
/release
/data
/custom

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

@ -7,9 +7,33 @@ 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)
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);
```
### Changed
- change man content width to 600px and optimize tweet/comment/replay text length. [#333](https://github.com/rocboss/paopao-ce/pull/333)
- optimize embed web ui to paopao execute binary file logic. [#354](https://github.com/rocboss/paopao-ce/pull/354)
```sh
# embed web ui to execute file default
make build
# use slim model to disable embed web ui to exectute file
make build TAGS='slim embed'
```
- frontend: optimize user profile page route path to domain/#/u/?s=username. [&c857142](https://github.com/rocboss/paopao-ce/commit/c857142565f0c28294344c7abc5c2df4e363b04c
- change the `Friendship` feature and `Followship` feature as builtin feature. [#362](https://github.com/rocboss/paopao-ce/pull/362)
- deprecated/remove `Lightship` feature. [#362](https://github.com/rocboss/paopao-ce/pull/362)
## 0.3.1
### Fixed

@ -23,6 +23,8 @@ WORKDIR /paopao-ce
COPY . .
COPY --from=frontend /web/dist ./web/dist
ENV GOPROXY=https://goproxy.cn
RUN [ $EMBED_UI != yes ] || make build TAGS='go_json'
RUN [ $EMBED_UI = yes ] || make build TAGS='slim embed go_json'
RUN [ $EMBED_UI != no ] || make build TAGS='embed go_json'
RUN [ $EMBED_UI = no ] || make build TAGS='go_json'
@ -34,10 +36,11 @@ ARG USE_DIST=no
ENV TZ=Asia/Shanghai
WORKDIR /app/paopao-ce
COPY --from=backend /paopao-ce/release/paopao-ce .
COPY --from=backend /paopao-ce/release/paopao .
COPY --from=backend /paopao-ce/config.yaml.sample config.yaml
VOLUME ["/app/paopao-ce/custom"]
EXPOSE 8008
HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD ps -ef | grep paopao-ce || exit 1
ENTRYPOINT ["/app/paopao-ce/paopao-ce"]
HEALTHCHECK --interval=5s --timeout=3s --retries=3 CMD ps -ef | grep paopao || exit 1
ENTRYPOINT ["/app/paopao-ce/paopao"]
CMD ["serve"]

@ -1,6 +1,7 @@
.PHONY: all build run test clean fmt pre-commit help
.PHONY: all build build-web run test clean fmt pre-commit help
TARGET = paopao-ce
PROJECT = paopao-ce
TARGET = paopao
ifeq ($(OS),Windows_NT)
TARGET := $(TARGET).exe
endif
@ -12,10 +13,10 @@ endif
RELEASE_ROOT = release
RELEASE_FILES = LICENSE README.md CHANGELOG.md config.yaml.sample docker-compose.yaml scripts docs
RELEASE_LINUX_AMD64 = $(RELEASE_ROOT)/linux-amd64/$(TARGET)
RELEASE_DARWIN_AMD64 = $(RELEASE_ROOT)/darwin-amd64/$(TARGET)
RELEASE_DARWIN_ARM64 = $(RELEASE_ROOT)/darwin-arm64/$(TARGET)
RELEASE_WINDOWS_AMD64 = $(RELEASE_ROOT)/windows-amd64/$(TARGET)
RELEASE_LINUX_AMD64 = $(RELEASE_ROOT)/linux-amd64/$(PROJECT)
RELEASE_DARWIN_AMD64 = $(RELEASE_ROOT)/darwin-amd64/$(PROJECT)
RELEASE_DARWIN_ARM64 = $(RELEASE_ROOT)/darwin-arm64/$(PROJECT)
RELEASE_WINDOWS_AMD64 = $(RELEASE_ROOT)/windows-amd64/$(PROJECT)
BUILD_VERSION := $(shell git describe --tags --always | cut -f1 -f2 -d "-")
BUILD_DATE := $(shell date +'%Y-%m-%d %H:%M:%S')
@ -34,8 +35,11 @@ build:
@echo Build paopao-ce
@go build -pgo=auto -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o $(RELEASE_ROOT)/$(TARGET)
build-web:
@cd web && rm -rf dist/* && yarn build && cd -
run:
@go run -pgo=auto -trimpath -gcflags "all=-N -l" -tags '$(TAGS)' -ldflags '$(LDFLAGS)' .
@go run -pgo=auto -trimpath -gcflags "all=-N -l" -tags '$(TAGS)' -ldflags '$(LDFLAGS)' . serve
.PHONY: release
release: linux-amd64 darwin-amd64 darwin-arm64 windows-x64
@ -44,10 +48,10 @@ release: linux-amd64 darwin-amd64 darwin-arm64 windows-x64
@cp -rf $(RELEASE_FILES) $(RELEASE_DARWIN_AMD64)
@cp -rf $(RELEASE_FILES) $(RELEASE_DARWIN_ARM64)
@cp -rf $(RELEASE_FILES) $(RELEASE_WINDOWS_AMD64)
@cd $(RELEASE_LINUX_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-linux_amd64.zip $(TARGET) && cd -
@cd $(RELEASE_DARWIN_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-darwin_amd64.zip $(TARGET) && cd -
@cd $(RELEASE_DARWIN_ARM64)/.. && rm -f *.zip && zip -r $(TARGET)-darwin_arm64.zip $(TARGET) && cd -
@cd $(RELEASE_WINDOWS_AMD64)/.. && rm -f *.zip && zip -r $(TARGET)-windows_amd64.zip $(TARGET) && cd -
@cd $(RELEASE_LINUX_AMD64)/.. && rm -f *.zip && zip -r $(PROJECT)-linux_amd64.zip $(PROJECT) && cd -
@cd $(RELEASE_DARWIN_AMD64)/.. && rm -f *.zip && zip -r $(PROJECT)-darwin_amd64.zip $(PROJECT) && cd -
@cd $(RELEASE_DARWIN_ARM64)/.. && rm -f *.zip && zip -r $(PROJECT)-darwin_arm64.zip $(PROJECT) && cd -
@cd $(RELEASE_WINDOWS_AMD64)/.. && rm -f *.zip && zip -r $(PROJECT)-windows_amd64.zip $(PROJECT) && cd -
.PHONY: linux-amd64
linux-amd64:

@ -97,11 +97,15 @@ PaoPao主要由以下优秀的开源项目/工具构建
```
编译api服务、内嵌web前端ui:
```sh
make build TAGS='embed'
make build
```
也可以使用精简模式编译不内嵌web前端ui:
```sh
make build TAGS='slim embed'
```
编译后在`release`目录可以找到对应可执行文件。
```sh
release/paopao-ce
release/paopao
```
4. 直接运行后端
@ -369,9 +373,9 @@ release/paopao-ce --no-default-features --features sqlite3,localoss,loggerfile,r
|`LoggerFile` | 日志 | 稳定 | 使用文件写日志 |
|`LoggerZinc` | 日志 | 稳定(推荐) | 使用[Zinc](https://github.com/zinclabs/zinc)写日志 |
|`LoggerMeili` | 日志 | 内测 | 使用[Meilisearch](https://github.com/meilisearch/meilisearch)写日志 |
|[`Friendship`](docs/proposal/002-关于Friendship功能项的设计.md) | 关系模式 | 内 | 弱关系好友模式,类似微信朋友圈 |
|[`Followship`](docs/proposal/003-关于Followship功能项的设计.md) | 关系模式 | WIP | 关注者模式类似Twitter的Follow模式 |
|[`Lightship`](docs/proposal/011-关于Lightship功能项的设计.md) | 关系模式 | 内测(默认) | 开放模式,所有推文都公开可见 |
|[`Friendship`](docs/proposal/22110410-关于Friendship功能项的设计.md) | 关系模式 | 内置 Builtin | 弱关系好友模式,类似微信朋友圈 |
|[`Followship`](docs/proposal/22110409-关于Followship功能项的设计.md) | 关系模式 | 内置 Builtin | 关注者模式类似Twitter的Follow模式 |
|[`Lightship`](docs/proposal/22121409-关于Lightship功能项的设计.md) | 关系模式 | 弃用 Deprecated | 开放模式,所有推文都公开可见 |
|`Alipay` | 支付 | 稳定 | 开启基于[支付宝开放平台](https://open.alipay.com/)的钱包功能 |
|`Sms` | 短信验证 | 稳定 | 开启短信验证码功能,用于手机绑定验证手机是否注册者的;功能如果没有开启,手机绑定时任意短信验证码都可以绑定手机 |
|`Docs:OpenAPI` | 开发文档 | 稳定 | 开启openapi文档功能提供web api文档说明(visit http://127.0.0.1:8008/docs/openapi) |

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

@ -18,10 +18,10 @@ type Followship interface {
// Chain provide handlers chain for gin
Chain() gin.HandlersChain
ListFollowers(*web.ListFollowersReq) (*web.ListFollowersResp, mir.Error)
ListFollowings(*web.ListFollowingsReq) (*web.ListFollowingsResp, mir.Error)
DeleteFollowing(*web.DeleteFollowingReq) mir.Error
AddFollowing(*web.AddFollowingReq) mir.Error
ListFollows(*web.ListFollowsReq) (*web.ListFollowsResp, mir.Error)
UnfollowUser(*web.UnfollowUserReq) mir.Error
FollowUser(*web.FollowUserReq) mir.Error
mustEmbedUnimplementedFollowshipServant()
}
@ -34,59 +34,59 @@ func RegisterFollowshipServant(e *gin.Engine, s Followship) {
router.Use(middlewares...)
// register routes info to router
router.Handle("GET", "/follower/list", func(c *gin.Context) {
router.Handle("GET", "/user/followings", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListFollowersReq)
req := new(web.ListFollowingsReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListFollowers(req)
resp, err := s.ListFollowings(req)
s.Render(c, resp, err)
})
router.Handle("GET", "/following/list", func(c *gin.Context) {
router.Handle("GET", "/user/follows", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.ListFollowingsReq)
req := new(web.ListFollowsReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
resp, err := s.ListFollowings(req)
resp, err := s.ListFollows(req)
s.Render(c, resp, err)
})
router.Handle("POST", "/following/delete", func(c *gin.Context) {
router.Handle("POST", "/user/unfollow", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.DeleteFollowingReq)
req := new(web.UnfollowUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.DeleteFollowing(req))
s.Render(c, nil, s.UnfollowUser(req))
})
router.Handle("POST", "/following/add", func(c *gin.Context) {
router.Handle("POST", "/user/follow", func(c *gin.Context) {
select {
case <-c.Request.Context().Done():
return
default:
}
req := new(web.AddFollowingReq)
req := new(web.FollowUserReq)
if err := s.Bind(c, req); err != nil {
s.Render(c, nil, err)
return
}
s.Render(c, nil, s.AddFollowing(req))
s.Render(c, nil, s.FollowUser(req))
})
}
@ -97,19 +97,19 @@ func (UnimplementedFollowshipServant) Chain() gin.HandlersChain {
return nil
}
func (UnimplementedFollowshipServant) ListFollowers(req *web.ListFollowersReq) (*web.ListFollowersResp, mir.Error) {
func (UnimplementedFollowshipServant) ListFollowings(req *web.ListFollowingsReq) (*web.ListFollowingsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedFollowshipServant) ListFollowings(req *web.ListFollowingsReq) (*web.ListFollowingsResp, mir.Error) {
func (UnimplementedFollowshipServant) ListFollows(req *web.ListFollowsReq) (*web.ListFollowsResp, mir.Error) {
return nil, mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedFollowshipServant) DeleteFollowing(req *web.DeleteFollowingReq) mir.Error {
func (UnimplementedFollowshipServant) UnfollowUser(req *web.UnfollowUserReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}
func (UnimplementedFollowshipServant) AddFollowing(req *web.AddFollowingReq) mir.Error {
func (UnimplementedFollowshipServant) FollowUser(req *web.FollowUserReq) mir.Error {
return mir.Errorln(http.StatusNotImplemented, http.StatusText(http.StatusNotImplemented))
}

@ -1,8 +1,8 @@
#!/bin/sh
# eg.1 : sh build-image.sh
# eg.2, set tags: sh build-image.sh 'embed go_json'
# eg.2, set tags: sh build-image.sh 'go_json'
TAGS='embed go_json'
TAGS='go_json'
if [ -n "$1" ]; then
TAGS="$1"

@ -0,0 +1,27 @@
// 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 migrate
import (
"fmt"
"github.com/rocboss/paopao-ce/cmd"
"github.com/spf13/cobra"
)
func init() {
migrateCmd := &cobra.Command{
Use: "migrate",
Short: "migrate database data",
Long: "miegrate database data when paopao-ce upgrade",
Run: migrateRun,
}
cmd.Register(migrateCmd)
}
func migrateRun(_cmd *cobra.Command, _args []string) {
// TODO: add some logic for migrate cmd feature
fmt.Println("sorry, this feature is not implemented yet.")
}

@ -0,0 +1,36 @@
// 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 cmd
import (
"github.com/spf13/cobra"
)
var (
rootCmd = &cobra.Command{
Use: "paopao",
Short: `an artistic "twitter like" community`,
Long: `an artistic "twitter like" community`,
}
)
// Setup set root command name,short-describe, long-describe
// return &cobra.Command to custom other options
func Setup(use, short, long string) *cobra.Command {
rootCmd.Use = use
rootCmd.Short = short
rootCmd.Long = long
return rootCmd
}
// Register add sub-command
func Register(cmd *cobra.Command) {
rootCmd.AddCommand(cmd)
}
// Execute start application
func Execute() {
rootCmd.Execute()
}

@ -0,0 +1,94 @@
// 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 serve
import (
"fmt"
"log"
"os"
"os/signal"
"syscall"
"time"
"github.com/alimy/cfg"
"github.com/fatih/color"
"github.com/getsentry/sentry-go"
"github.com/rocboss/paopao-ce/cmd"
"github.com/rocboss/paopao-ce/internal"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/service"
"github.com/rocboss/paopao-ce/pkg/debug"
"github.com/rocboss/paopao-ce/pkg/utils"
"github.com/rocboss/paopao-ce/pkg/version"
"github.com/sourcegraph/conc"
"github.com/spf13/cobra"
"go.uber.org/automaxprocs/maxprocs"
)
var (
noDefaultFeatures bool
features []string
)
func init() {
serveCmd := &cobra.Command{
Use: "serve",
Short: "start paopao-ce server",
Long: "start paopao-ce server",
Run: serveRun,
}
serveCmd.Flags().BoolVar(&noDefaultFeatures, "no-default-features", false, "whether not use default features")
serveCmd.Flags().StringSliceVarP(&features, "features", "f", []string{}, "use special features")
cmd.Register(serveCmd)
}
func deferFn() {
if cfg.If("Sentry") {
// Flush buffered events before the program terminates.
sentry.Flush(2 * time.Second)
}
}
func serveRun(_cmd *cobra.Command, _args []string) {
utils.PrintHelloBanner(version.VersionInfo())
// set maxprocs automatic
maxprocs.Set(maxprocs.Logger(log.Printf))
// initial configure
conf.Initial(features, noDefaultFeatures)
internal.Initial()
ss := service.MustInitService()
if len(ss) < 1 {
fmt.Fprintln(color.Output, "no service need start so just exit")
return
}
// do defer function
defer deferFn()
// start pyroscope if need
debug.StartPyroscope()
// start services
wg := conc.NewWaitGroup()
fmt.Fprintf(color.Output, "\nstarting run service...\n\n")
service.Start(wg)
// graceful stop services
wg.Go(func() {
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
fmt.Fprintf(color.Output, "\nshutting down server...\n\n")
service.Stop()
})
wg.Wait()
}

@ -0,0 +1,25 @@
// Copyright 2023 ROC. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package cmd
import (
"github.com/rocboss/paopao-ce/pkg/utils"
"github.com/rocboss/paopao-ce/pkg/version"
"github.com/spf13/cobra"
)
func init() {
versionCmd := &cobra.Command{
Use: "version",
Short: "show version information",
Long: "show version information",
Run: versionRun,
}
Register(versionCmd)
}
func versionRun(_cmd *cobra.Command, _args []string) {
utils.PrintHelloBanner(version.VersionInfo())
}

@ -12,7 +12,7 @@ Server: # 服务设置
ReadTimeout: 60
WriteTimeout: 60
Features:
Default: ["Web", "Frontend:EmbedWeb", "Zinc", "LocalOSS", "MySQL", "BigCacheIndex", "LoggerZinc", "Friendship"]
Default: ["Web", "Frontend:EmbedWeb", "Meili", "LocalOSS", "MySQL", "BigCacheIndex", "LoggerFile"]
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"]

@ -41,7 +41,7 @@ services:
# - paopao-network
redis:
image: redis/redis-stack:7.0.6-RC8
image: redis/redis-stack:7.2.0-RC3
restart: always
ports:
- 6379:6379
@ -51,33 +51,33 @@ services:
networks:
- paopao-network
zinc:
image: public.ecr.aws/zinclabs/zinc:latest
user: root
restart: always
ports:
- 4080:4080
volumes:
- ./custom/data/zinc/data:/data
environment:
ZINC_FIRST_ADMIN_USER: admin
ZINC_FIRST_ADMIN_PASSWORD: admin
DATA_PATH: /data
networks:
- paopao-network
# meili:
# image: getmeili/meilisearch:v1.2
# zinc:
# image: bitbus/zincsearch:latest
# user: zincsearch
# restart: always
# ports:
# - 7700:7700
# - 4080:4080
# volumes:
# - ./custom/data/meili/data:/meili_data
# - ./custom/data/zinc/data:/data
# environment:
# - MEILI_MASTER_KEY=paopao-meilisearch
# ZINC_FIRST_ADMIN_USER: admin
# ZINC_FIRST_ADMIN_PASSWORD: admin
# DATA_PATH: /data
# networks:
# - paopao-network
meili:
image: getmeili/meilisearch:v1.3
restart: always
ports:
- 7700:7700
volumes:
- ./custom/data/meili/data:/meili_data
environment:
- MEILI_MASTER_KEY=paopao-meilisearch
networks:
- paopao-network
# pyroscope:
# image: pyroscope/pyroscope:latest
# restart: always
@ -102,12 +102,12 @@ services:
- paopao-network
backend:
image: bitbus/paopao-ce:0.3
image: bitbus/paopao-ce:0.4
restart: always
depends_on:
- db
- redis
- zinc
- meili
# modify below to reflect your custom configure
volumes:
- ./config.yaml.sample:/app/paopao-ce/config.yaml

@ -1,28 +0,0 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 010| 北野 | 2022-12-10 | 2022-12-10 | v0.0 | 提议 |
### 关于Sqlx功能项的设计
---- 这里写简要介绍 ----
### 场景
---- 这里描述在什么使用场景下会需要本提按 ----
### 需求
TODO-TL;DR...
### 方案
TODO
### 疑问
1. 如何开启这个功能?
TODO
### 更新记录
#### v0.0(2022-12-10) - 北野
* 初始文档, 先占个位置

@ -1,6 +1,6 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 22110409| 北野 | 2022-11-04 | 2022-11-21 | v0.1 | 提议 |
| 22110409| 北野 | 2022-11-04 | 2023-08-16 | v1.0 | 提议 |
### 关于Followship功能项的设计
Followship是实现类似Twitter Timeline模式**关注者模型**的时间线信息流广场推文列表的生成将主要与推文时间、用户的关注者相关。Twitter的推文消息流是非常智能的用户体验也非常好这得益于其背后的智能推荐算法以及完善的关注者模型体系当然还有很多其他机制共同作用下的结果。本提按作为一个总纲为paopao-ce引入类似的机制这将是一个持续完善的缓慢过程一切都是为了用户体验用户就是上帝用户需要什么paopao-ce就努力提供什么
@ -35,6 +35,12 @@
#### 方案二
在方案一的基础上通过引入 *图数据库* 实现可选需求。待研究、设计~
#### 设计细节
* 参考实现(PR):
[add Followship feature #355](https://github.com/rocboss/paopao-ce/pull/355)
#### 状态:
内置 Builtin
### 疑问
@ -42,15 +48,7 @@
理论上这两个功能项是可以共存的如果设置成共存就需要同时提供这两个机制的UI交互同时广场推文列表的生成机制也需要做相应适配可能会比较复杂。前期开发将把这两个功能设置为互斥的即**用户关系模型**只能设置为其中之一,待两个功能项都趋于稳定后,再测试这两个功能项共存后的用户体验,如果效果还不错就引入共存机制。
1. 如何开启这个功能?
在配置文件config.yaml中的`Features`中添加`Followship`功能项开启该功能:
```yaml
...
# features中加上 Followship
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "MinIO", "Followship"]
Base: ["Redis", "PhoneBind"]
...
```
内置 Builtin
### 更新记录
#### v0.0(2022-11-04) - 北野
@ -58,3 +56,9 @@
#### v0.1(2022-11-21) - 北野
* 添加初始内容;
#### v1.0(2023-08-14) - 北野
* 添加参考实现;
#### v1.1(2023-08-16) - 北野
* 添加状态信息;

@ -1,6 +1,6 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 22110410 | 北野 | 2022-11-04 | 2023-01-04 | v1.0 | 提议 |
| 22110410 | 北野 | 2022-11-04 | 2023-08-16 | v1.1 | 提议 |
### Friendship功能项的设计概要
Friendship功能提供好友间分享推文信息的机制更好的帮助用户建立自己的推文分享小圈子。Friendship本质上想优化的是泡泡广场页面推文列表的生成机制开启功能后推文列表只能获取 `公开/私密/好友` 的推文,每个用户都有属于自己的个性化推文列表。在提供个性化推文列表生成机制的同时,好友体系的建立也顺便帮助用户建立自己的个性化有限范围内的灵魂社交小圈子,只有相互间拥有个性化认同感的用户才能互为好友。
@ -30,6 +30,9 @@ Friendship功能提供好友间分享推文信息的机制更好的帮助用
* 参考实现(PR):
[add support Friendship feature #192](https://github.com/rocboss/paopao-ce/pull/192)
#### 状态:
内置 Builtin
* 预览
| |
@ -46,15 +49,7 @@ Friendship功能提供好友间分享推文信息的机制更好的帮助用
2. 如何形成这种好友体系?
形成好友体系分 **建立、维持、解除** 好友关系。Friendship提供 *建立/解除* 好友关系的机制,泡泡广场的推文列表依据这个好友体系为每个用户生成个性化推文列表。而好友关系的**维持**本质上就是用户推文的持久更新、思想的持续演化使得好友对你一直保持兴趣Friendship也为每一条推文的访问权限进行**标记(私密/好友可见)**,这可以传达这样一种信息: **“时刻让好友知道我非常在乎好友、对好友特殊关照,你看这条推文就是只有我的好友(也就是你)才可以看到,够意思吧”!** 顺便一说,每一条推文的访问权限标记(公开/私密/好友可见)是建立Friendship弱关系体系的隐性催化剂可以加速Friendship的形成。
3. 如何开启这个功能?
在配置文件config.yaml中的`Features`中添加`Friendship`功能项开启该功能:
```yaml
...
# features中加上 Friendship
Features:
Default: ["Meili", "LoggerMeili", "Base", "Sqlite3", "BigCacheIndex", "MinIO", "Friendship"]
Base: ["Redis", "PhoneBind"]
...
```
内置 Builtin
### 更新记录
#### v0.1(2022-11-04) - 北野
@ -65,3 +60,6 @@ Friendship功能提供好友间分享推文信息的机制更好的帮助用
#### v1.0(2023-01-04) - 北野
* 添加参考实现PR信息
#### v1.1(2023-08-16) - 北野
* 添加状态信息;

@ -1,9 +1,9 @@
| 编号 | 作者 | 发表时间 | 变更时间 | 版本 | 状态 |
| ----- | ----- | ----- | ----- | ----- | ----- |
| 22121409 | 北野 | 2022-12-14 | 2022-01-09 | v1.1 | 提议 |
| 22121409 | 北野 | 2022-12-14 | 2023-08-16 | v1.2 | 提议 |
### 关于Lightship功能项的设计
Lightship(开放模式)功能提供完全公开的推文分享服务,有别于[Friendship](002-关于Friendship功能项的设计.md "关于Friendship功能项的设计")、[Followship](003-关于Followship功能项的设计.md "关于Followship功能项的设计")使用Lightship用户模式部署paopao-ce用户发布的所有推文都是公开可访问的广场推文列表展示的是全站所有公开推文的Timeline Tweets。
Lightship(开放模式)功能提供完全公开的推文分享服务,有别于[Friendship](22110410-关于Friendship功能项的设计.md "关于Friendship功能项的设计")、[Followship](22110410-关于Followship功能项的设计.md "关于Followship功能项的设计")使用Lightship用户模式部署paopao-ce用户发布的所有推文都是公开可访问的广场推文列表展示的是全站所有公开推文的Timeline Tweets。
### 场景
一般用于非常小的站点,或者推文更新不频繁的站点。
@ -24,6 +24,9 @@ Lightship(开放模式)功能提供完全公开的推文分享服务,有别于
#### 参考实现(PR):
[add Lightship feature support #198](https://github.com/rocboss/paopao-ce/pull/198)
#### 状态:
弃用 Deprecated
### 疑问
1. 公开模式为什么命名为Lightship
@ -45,3 +48,6 @@ Features:
#### v1.1(2022-01-09) - 北野
* 添加参考实现PR信息
#### v1.2(2023-08-16) - 北野
* 添加状态信息

@ -145,15 +145,15 @@
* [x] 业务逻辑实现
#### 关系模式:
* `Friendship` 弱关系好友模式,类似微信朋友圈(目前状态: 内);
* `Friendship` 弱关系好友模式,类似微信朋友圈(目前状态: 内置Builtin);
* [x] [提按文档](docs/proposal/22110410-关于Friendship功能项的设计.md)
* [x] 接口定义
* [x] 业务逻辑实现
* `Followship` 关注者模式类似Twitter的Follow模式(目前状态: WIP);
* `Followship` 关注者模式类似Twitter的Follow模式(目前状态: 内置Builtin);
* [ ] [提按文档](docs/proposal/22110409-关于Followship功能项的设计.md)
* [ ] 接口定义
* [ ] 业务逻辑实现
* `Lightship` 开放模式,所有推文都公开可见(目前状态: 内测、默认);
* `Lightship` 开放模式,所有推文都公开可见(目前状态: 弃用Deprecated);
* [x] [提按文档](docs/proposal/22121409-关于Lightship功能项的设计.md)
* [x] 接口定义
* [x] 业务逻辑实现

@ -7,10 +7,10 @@ require (
github.com/afocus/captcha v0.0.0-20191010092841-4bd1f21c8868
github.com/alimy/cfg v0.4.0
github.com/alimy/mir/v4 v4.0.0
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible
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.9.0
github.com/bytedance/sonic v1.9.2
github.com/bufbuild/connect-go v1.10.0
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
@ -28,31 +28,33 @@ 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.1
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.14
github.com/smartwalle/alipay/v3 v3.2.15
github.com/sourcegraph/conc v0.3.0
github.com/spf13/cobra v1.7.0
github.com/spf13/viper v1.16.0
github.com/tencentyun/cos-go-sdk-v5 v0.7.42
github.com/yinheli/mahonia v0.0.0-20131226213531-0eef680515cc
go.uber.org/automaxprocs v1.5.3
google.golang.org/grpc v1.56.2
google.golang.org/grpc v1.57.0
google.golang.org/protobuf v1.31.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/resty.v1 v1.12.0
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.1
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
)
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
@ -76,6 +78,7 @@ require (
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.3.1 // indirect
@ -102,7 +105,7 @@ require (
github.com/mozillazg/go-httpheader v0.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pyroscope-io/godeltaprof v0.1.0 // indirect
github.com/pyroscope-io/godeltaprof v0.1.2 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/rs/xid v1.5.0 // indirect
@ -128,7 +131,7 @@ require (
golang.org/x/text v0.11.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/tools v0.9.3 // indirect
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
lukechampine.com/uint128 v1.2.0 // indirect

@ -127,8 +127,8 @@ github.com/alimy/cfg v0.4.0 h1:SslKPndmxRViT1ePWLmNsEq7okYP0GVeuowQlRWZPkw=
github.com/alimy/cfg v0.4.0/go.mod h1:rOxbasTH2srl6StAjNF5Vyi8bfrdkl3fLGmOYtSw81c=
github.com/alimy/mir/v4 v4.0.0 h1:MzGfmoLjjvR69jbZEmpKJO3tUuqB0RGRv1UWPbtukBg=
github.com/alimy/mir/v4 v4.0.0/go.mod h1:d58dBvw2KImcVbAUANrciEV/of0arMNsI9c/5UNCMMc=
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible h1:KpbJFXwhVeuxNtBJ74MCGbIoaBok2uZvkD7QXp2+Wis=
github.com/aliyun/aliyun-oss-go-sdk v2.2.7+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible h1:6JF1bjhT0WN2srEmijfOFtVWwV91KZ6dJY1/JbdtGrI=
github.com/aliyun/aliyun-oss-go-sdk v2.2.8+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
@ -186,16 +186,17 @@ github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnweb
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
github.com/bufbuild/connect-go v1.9.0 h1:JIgAeNuFpo+SUPfU19Yt5TcWlznsN5Bv10/gI/6Pjoc=
github.com/bufbuild/connect-go v1.9.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8=
github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg=
github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8=
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
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/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=
@ -209,8 +210,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=
@ -363,6 +367,7 @@ github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfc
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@ -740,6 +745,8 @@ github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ=
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw=
@ -849,6 +856,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=
@ -1105,12 +1113,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.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw=
github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU=
github.com/pyroscope-io/godeltaprof v0.1.0 h1:UBqtjt0yZi4jTxqZmLAs34XG6ycS3vUTlhEUSq4NHLE=
github.com/pyroscope-io/godeltaprof v0.1.0/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/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.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=
@ -1130,6 +1138,7 @@ github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
@ -1155,8 +1164,8 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartwalle/alipay/v3 v3.2.14 h1:X7IPMzweYXjxO+30G9amEjCogzpoA/K1GZpVjM1keps=
github.com/smartwalle/alipay/v3 v3.2.14/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE=
github.com/smartwalle/alipay/v3 v3.2.15 h1:3fvFJnINKKAOXHR/Iv20k1Z7KJ+nOh3oK214lELPqG8=
github.com/smartwalle/alipay/v3 v3.2.15/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE=
github.com/smartwalle/ncrypto v1.0.2 h1:pTAhCqtPCMhpOwFXX+EcMdR6PNzruBNoGQrN2S1GbGI=
github.com/smartwalle/ncrypto v1.0.2/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk=
github.com/smartwalle/ngx v1.0.6 h1:JPNqNOIj+2nxxFtrSkJO+vKJfeNUSEQueck/Wworjps=
@ -1185,6 +1194,8 @@ github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKv
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@ -1891,8 +1902,8 @@ 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 v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A=
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19 h1:0nDDozoAU19Qb2HwhXadU8OcsiO/09cnTqhUtq2MEOM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
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=
@ -1926,8 +1937,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.56.2 h1:fVRFRnXvU+x6C4IlHZewvJOVHoOv1TUuQyoRsYnB4bI=
google.golang.org/grpc v1.56.2/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s=
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
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=
@ -2001,12 +2012,12 @@ gorm.io/gorm v1.20.12/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
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.24.3/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
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.1 h1:Ug4LcoPhrvqq71UhxtF346f+skTYoCa/nEsdjvHwEzk=
gorm.io/plugin/dbresolver v1.4.1/go.mod h1:CTbCtMWhsjXSiJqiW2R8POvJ2cq18RVOl4WGyT5nhNc=
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=
@ -2113,6 +2124,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,10 +25,13 @@ const (
TableComment = "comment"
TableCommentContent = "comment_content"
TableCommentReply = "comment_reply"
TableFollowing = "following"
TableContact = "contact"
TableContactGroup = "contact_group"
TableMessage = "message"
TablePost = "post"
TablePostByComment = "post_by_comment"
TablePostByMedia = "post_by_media"
TablePostAttachmentBill = "post_attachment_bill"
TablePostCollection = "post_collection"
TablePostContent = "post_content"

@ -305,10 +305,13 @@ func (s *databaseConf) TableNames() (res TableNameMap) {
TableComment,
TableCommentContent,
TableCommentReply,
TableFollowing,
TableContact,
TableContactGroup,
TableMessage,
TablePost,
TablePostByComment,
TablePostByMedia,
TablePostAttachmentBill,
TablePostCollection,
TablePostContent,

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

@ -4,4 +4,5 @@
// Package cs contain core data service interface type
// model define
package cs

@ -0,0 +1,13 @@
// 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 cs
import "errors"
// internal core error variable for data logic implement.
var (
ErrNotImplemented = errors.New("not implemented")
ErrNoPermission = errors.New("no permission")
)

@ -4,9 +4,28 @@
package cs
const (
RelationUnknow RelationTyp = iota
RelationSelf
RelationFriend
RelationFollower
RelationFollowing
RelationAdmin
RelationGuest
)
type (
// UserInfoList 用户信息列表
UserInfoList []*UserInfo
//
RelationTyp uint8
VistUser struct {
Username string
UserId int64
RelTyp RelationTyp
}
)
// UserInfo 用户基本信息
@ -18,3 +37,22 @@ type UserInfo struct {
Avatar string `json:"avatar"`
IsAdmin bool `json:"is_admin"`
}
func (t RelationTyp) String() string {
switch t {
case RelationSelf:
return "self"
case RelationFriend:
return "friend"
case RelationFollower:
return "follower"
case RelationFollowing:
return "following"
case RelationAdmin:
return "admin"
case RelationUnknow:
fallthrough
default:
return "unknow relation"
}
}

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

@ -15,7 +15,7 @@ type TweetService interface {
GetPosts(conditions ms.ConditionsT, offset, limit int) ([]*ms.Post, error)
GetPostCount(conditions ms.ConditionsT) (int64, error)
GetUserPostStar(postID, userID int64) (*ms.PostStar, error)
GetUserPostStars(userID int64, offset, limit int) ([]*ms.PostStar, error)
GetUserPostStars(userID int64, limit int, offset int) ([]*ms.PostStar, error)
GetUserPostStarCount(userID int64) (int64, error)
GetUserPostCollection(postID, userID int64) (*ms.PostCollection, error)
GetUserPostCollections(userID int64, offset, limit int) ([]*ms.PostCollection, error)
@ -23,6 +23,9 @@ type TweetService interface {
GetPostAttatchmentBill(postID, userID int64) (*ms.PostAttachmentBill, error)
GetPostContentsByIDs(ids []int64) ([]*ms.PostContent, error)
GetPostContentByID(id int64) (*ms.PostContent, error)
ListUserStarTweets(user *cs.VistUser, limit int, offset int) ([]*ms.PostStar, int64, error)
ListUserMediaTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
ListUserCommentTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error)
}
// TweetManageService 推文管理服务,包括创建/删除/更新推文
@ -31,6 +34,7 @@ type TweetManageService interface {
DeletePost(post *ms.Post) ([]string, error)
LockPost(post *ms.Post) error
StickPost(post *ms.Post) error
HighlightPost(userId, postId int64) (int, error)
VisiblePost(post *ms.Post, visibility PostVisibleT) error
UpdatePost(post *ms.Post) error
CreatePostStar(postID, userID int64) (*ms.PostStar, error)

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

@ -14,9 +14,7 @@ var (
)
type bigCacheTweetsCache struct {
name string
version *semver.Version
bc *bigcache.BigCache
bc *bigcache.BigCache
}
func (s *bigCacheTweetsCache) getTweetsBytes(key string) ([]byte, error) {

@ -5,6 +5,7 @@
package cache
import (
"context"
"time"
"github.com/allegro/bigcache/v3"
@ -28,7 +29,7 @@ func NewBigCacheIndexService(ips core.IndexPostsService, ams core.AuthorizationM
c.MaxEntrySize = 10000
c.Logger = logrus.StandardLogger()
bc, err := bigcache.NewBigCache(c)
bc, err := bigcache.New(context.Background(), c)
if err != nil {
logrus.Fatalf("initial bigCahceIndex failure by err: %v", err)
}

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

@ -21,6 +21,10 @@ const (
PostVisitInvalid
)
type PostByMedia = Post
type PostByComment = Post
type Post struct {
*Model
UserID int64 `json:"user_id"`

@ -7,6 +7,7 @@ package dbr
import (
"time"
"github.com/rocboss/paopao-ce/internal/core/cs"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
@ -52,18 +53,14 @@ func (p *PostStar) Delete(db *gorm.DB) error {
}).Error
}
func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*PostStar, error) {
var stars []*PostStar
var err error
func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, typ cs.RelationTyp, limit int, offset int) (res []*PostStar, err error) {
tn := db.NamingStrategy.TableName("PostStar") + "."
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
if p.UserID > 0 {
db = db.Where(tn+"user_id = ?", p.UserID)
}
for k, v := range *conditions {
if k == "ORDER" {
db = db.Order(v)
@ -71,18 +68,24 @@ func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, offset, limit int)
db = db.Where(tn+k, v)
}
}
db = db.Joins("Post").Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID).Order(clause.OrderByColumn{Column: clause.Column{Table: "Post", Name: "id"}, Desc: true})
if err = db.Find(&stars).Error; err != nil {
return nil, err
db = db.Joins("Post")
switch typ {
case cs.RelationAdmin:
// admin have all permition to visit all type tweets
case cs.RelationFriend:
db = db.Where("visibility = ? OR visibility = ?", PostVisitPublic, PostVisitFriend)
case cs.RelationSelf:
db = db.Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID)
default:
db = db.Where("visibility=?", PostVisitPublic)
}
return stars, nil
db = db.Order(clause.OrderByColumn{Column: clause.Column{Table: "Post", Name: "id"}, Desc: true})
err = db.Find(&res).Error
return
}
func (p *PostStar) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
var count int64
func (p *PostStar) Count(db *gorm.DB, typ cs.RelationTyp, conditions *ConditionsT) (res int64, err error) {
tn := db.NamingStrategy.TableName("PostStar") + "."
if p.PostID > 0 {
db = db.Where(tn+"post_id = ?", p.PostID)
}
@ -94,10 +97,17 @@ func (p *PostStar) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
db = db.Where(tn+k, v)
}
}
db = db.Joins("Post").Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID)
if err := db.Model(p).Count(&count).Error; err != nil {
return 0, err
db = db.Joins("Post")
switch typ {
case cs.RelationAdmin:
// admin have all permition to visit all type tweets
case cs.RelationFriend:
db = db.Where("visibility = ? OR visibility = ?", PostVisitPublic, PostVisitFriend)
case cs.RelationSelf:
db = db.Where("visibility <> ? OR (visibility = ? AND ? = ?)", PostVisitPrivate, PostVisitPrivate, clause.Column{Table: "Post", Name: "user_id"}, p.UserID)
default:
db = db.Where("visibility=?", PostVisitPublic)
}
return count, nil
err = db.Model(p).Count(&res).Error
return
}

@ -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,10 +17,13 @@ var (
_comment_ string
_commentContent_ string
_commentReply_ string
_following_ string
_contact_ string
_contactGroup_ string
_message_ string
_post_ string
_post_by_comment_ string
_post_by_media_ string
_postAttachmentBill_ string
_postCollection_ string
_postContent_ string
@ -40,10 +43,13 @@ 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]
_post_ = m[conf.TablePost]
_post_by_comment_ = m[conf.TablePostByComment]
_post_by_media_ = m[conf.TablePostByMedia]
_postAttachmentBill_ = m[conf.TablePostAttachmentBill]
_postCollection_ = m[conf.TablePostCollection]
_postContent_ = m[conf.TablePostContent]

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
// Core service implement base gorm+mysql/postgresql/sqlite3.
// package jinzhu Core service implement base gorm+mysql/postgresql/sqlite3.
// Jinzhu is the primary developer of gorm so use his name as
// package name as a saluter.
@ -42,6 +42,7 @@ type dataSrv struct {
core.CommentManageService
core.UserManageService
core.ContactManageService
core.FollowingManageService
core.SecurityService
core.AttachmentCheckService
core.ShareKeyService
@ -61,24 +62,12 @@ func NewDataService() (core.DataService, core.VersionInfo) {
var (
v core.VersionInfo
cis core.CacheIndexService
ips core.IndexPostsService
)
db := conf.MustGormDB()
pvs := security.NewPhoneVerifyService()
ams := NewAuthorizationManageService()
ths := newTweetHelpService(db)
// initialize core.IndexPostsService
if cfg.If("Friendship") {
ips = newFriendIndexService(db, ams, ths)
} else if cfg.If("Followship") {
ips = newFollowIndexService(db, ths)
} else if cfg.If("Lightship") {
ips = newLightIndexService(db, ths)
} else {
// default use lightship post index service
ips = newLightIndexService(db, ths)
}
ips := newShipIndexService(db, ams, ths)
// initialize core.CacheIndexService
cfg.On(cfg.Actions{
@ -88,7 +77,6 @@ func NewDataService() (core.DataService, core.VersionInfo) {
cis, v = cache.NewSimpleCacheIndexService(ips)
},
"BigCacheIndex": func() {
// TODO: make cache index post in different scence like friendship/followship/lightship
cis, v = cache.NewBigCacheIndexService(ips, ams)
},
"RedisCacheIndex": func() {
@ -112,6 +100,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(),
ShareKeyService: NewShareKeyService(db),

@ -15,35 +15,23 @@ import (
)
var (
_ core.IndexPostsService = (*friendIndexSrv)(nil)
_ core.IndexPostsService = (*followIndexSrv)(nil)
_ core.IndexPostsService = (*lightIndexSrv)(nil)
_ core.IndexPostsService = (*shipIndexSrv)(nil)
_ core.IndexPostsService = (*simpleIndexPostsSrv)(nil)
)
type friendIndexSrv struct {
type shipIndexSrv struct {
ams core.AuthorizationManageService
ths core.TweetHelpService
db *gorm.DB
}
type followIndexSrv struct {
ths core.TweetHelpService
db *gorm.DB
}
type lightIndexSrv struct {
ths core.TweetHelpService
db *gorm.DB
}
type simpleIndexPostsSrv struct {
ths core.TweetHelpService
db *gorm.DB
}
// IndexPosts 根据userId查询广场推文列表简单做到不同用户的主页都是不同的
func (s *friendIndexSrv) IndexPosts(user *ms.User, offset int, limit int) (*ms.IndexTweetList, error) {
func (s *shipIndexSrv) IndexPosts(user *ms.User, offset int, limit int) (*ms.IndexTweetList, error) {
predicates := dbr.Predicates{
"ORDER": []any{"is_top DESC, latest_replied_on DESC"},
}
@ -77,60 +65,6 @@ func (s *friendIndexSrv) IndexPosts(user *ms.User, offset int, limit int) (*ms.I
}, nil
}
func (s *friendIndexSrv) TweetTimeline(userId int64, offset int, limit int) (*cs.TweetBox, error) {
// TODO
return nil, debug.ErrNotImplemented
}
// IndexPosts 根据userId查询广场推文列表
func (s *followIndexSrv) IndexPosts(user *ms.User, offset int, limit int) (*ms.IndexTweetList, error) {
// TODO
return nil, debug.ErrNotImplemented
}
func (s *followIndexSrv) TweetTimeline(userId int64, offset int, limit int) (*cs.TweetBox, error) {
// TODO
return nil, debug.ErrNotImplemented
}
// IndexPosts 根据userId查询广场推文列表获取公开可见Tweet或者所属用户的私有Tweet
func (s *lightIndexSrv) IndexPosts(user *ms.User, offset int, limit int) (*ms.IndexTweetList, error) {
predicates := dbr.Predicates{
"ORDER": []any{"is_top DESC, latest_replied_on DESC"},
}
if user == nil {
predicates["visibility = ?"] = []any{dbr.PostVisitPublic}
} else if !user.IsAdmin {
args := []any{dbr.PostVisitPublic, dbr.PostVisitPrivate, user.ID}
predicates["visibility = ? OR (visibility = ? AND user_id = ?)"] = args
}
posts, err := (&dbr.Post{}).Fetch(s.db, predicates, offset, limit)
if err != nil {
logrus.Debugf("gormIndexPostsSrv.IndexPosts err: %v", err)
return nil, err
}
formatPosts, err := s.ths.MergePosts(posts)
if err != nil {
return nil, err
}
total, err := (&dbr.Post{}).CountBy(s.db, predicates)
if err != nil {
return nil, err
}
return &ms.IndexTweetList{
Tweets: formatPosts,
Total: total,
}, nil
}
func (s *lightIndexSrv) TweetTimeline(userId int64, offset int, limit int) (*cs.TweetBox, error) {
// TODO
return nil, debug.ErrNotImplemented
}
// simpleCacheIndexGetPosts simpleCacheIndex 专属获取广场推文列表函数
func (s *simpleIndexPostsSrv) IndexPosts(_user *ms.User, offset int, limit int) (*ms.IndexTweetList, error) {
predicates := dbr.Predicates{
@ -165,28 +99,14 @@ func (s *simpleIndexPostsSrv) TweetTimeline(userId int64, offset int, limit int)
return nil, debug.ErrNotImplemented
}
func newFriendIndexService(db *gorm.DB, ams core.AuthorizationManageService, ths core.TweetHelpService) core.IndexPostsService {
return &friendIndexSrv{
func newShipIndexService(db *gorm.DB, ams core.AuthorizationManageService, ths core.TweetHelpService) core.IndexPostsService {
return &shipIndexSrv{
ams: ams,
ths: ths,
db: db,
}
}
func newFollowIndexService(db *gorm.DB, ths core.TweetHelpService) core.IndexPostsService {
return &followIndexSrv{
ths: ths,
db: db,
}
}
func newLightIndexService(db *gorm.DB, ths core.TweetHelpService) core.IndexPostsService {
return &lightIndexSrv{
ths: ths,
db: db,
}
}
func newSimpleIndexPostsService(db *gorm.DB, ths core.TweetHelpService) core.IndexPostsService {
return &simpleIndexPostsSrv{
ths: ths,

@ -143,7 +143,7 @@ func (s *topicSrv) GetFollowTags(userId int64, limit int, offset int) (cs.TagLis
}
// 置顶排序后处理
// TODO: 垃圾办法最好是topic_user join tag 一次查询但是gorm的join真他喵的别扭F*K
res := make(cs.TagList, len(topicIds), len(topicIds))
res := make(cs.TagList, len(topicIds))
for _, tag := range formtedTags {
res[topicIdsMap[tag.ID]] = tag
}
@ -217,10 +217,8 @@ func (s *topicSrv) tagsFormatA(userId int64, tags cs.TagList) (cs.TagList, error
func (s *topicSrv) tagsFormatB(userTopicsMap map[int64]*topicInfo, tags cs.TagInfoList) (cs.TagList, error) {
// 获取创建者User IDs
userIds := []int64{}
tagIds := []int64{}
for _, tag := range tags {
userIds = append(userIds, tag.UserID)
tagIds = append(tagIds, tag.ID)
}
users, err := (&dbr.User{}).ListUserInfoById(s.db, userIds)
if err != nil {

@ -307,7 +307,26 @@ func (s *tweetManageSrv) StickPost(post *ms.Post) error {
return nil
}
func (s *tweetManageSrv) VisiblePost(post *ms.Post, visibility core.PostVisibleT) error {
func (s *tweetManageSrv) HighlightPost(userId int64, postId int64) (res int, err error) {
var post dbr.Post
tx := s.db.Begin()
defer tx.Rollback()
post.Get(tx)
if err = tx.Where("id = ? AND is_del = 0", postId).First(&post).Error; err != nil {
return
}
if post.UserID != userId {
return 0, cs.ErrNoPermission
}
post.IsEssence = 1 - post.IsEssence
if err = post.Update(tx); err != nil {
return
}
tx.Commit()
return post.IsEssence, nil
}
func (s *tweetManageSrv) VisiblePost(post *ms.Post, visibility core.PostVisibleT) (err error) {
oldVisibility := post.Visibility
post.Visibility = visibility
// TODO: 这个判断是否可以不要呢
@ -320,33 +339,32 @@ func (s *tweetManageSrv) VisiblePost(post *ms.Post, visibility core.PostVisibleT
// TODO: 置顶推文用户是否有权设置成私密? 后续完善
post.IsTop = 0
}
db := s.db.Begin()
err := post.Update(db)
if err != nil {
db.Rollback()
return err
tx := s.db.Begin()
defer tx.Rollback()
if err = post.Update(tx); err != nil {
return
}
// tag处理
tags := strings.Split(post.Tags, ",")
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
if oldVisibility == dbr.PostVisitPrivate {
// 从私密转为非私密才需要重新创建tag
createTags(db, post.UserID, tags)
createTags(tx, post.UserID, tags)
} else if visibility == dbr.PostVisitPrivate {
// 从非私密转为私密才需要删除tag
deleteTags(db, tags)
deleteTags(tx, tags)
}
db.Commit()
tx.Commit()
s.cacheIndex.SendAction(core.IdxActVisiblePost, post)
return nil
return
}
func (s *tweetManageSrv) UpdatePost(post *ms.Post) error {
if err := post.Update(s.db); err != nil {
return err
func (s *tweetManageSrv) UpdatePost(post *ms.Post) (err error) {
if err = post.Update(s.db); err != nil {
return
}
s.cacheIndex.SendAction(core.IdxActUpdatePost, post)
return nil
return
}
func (s *tweetManageSrv) CreatePostStar(postID, userID int64) (*ms.PostStar, error) {
@ -386,21 +404,67 @@ func (s *tweetSrv) GetUserPostStar(postID, userID int64) (*ms.PostStar, error) {
return star.Get(s.db)
}
func (s *tweetSrv) GetUserPostStars(userID int64, offset, limit int) ([]*ms.PostStar, error) {
func (s *tweetSrv) GetUserPostStars(userID int64, limit int, offset int) ([]*ms.PostStar, error) {
star := &dbr.PostStar{
UserID: userID,
}
return star.List(s.db, &dbr.ConditionsT{
"ORDER": s.db.NamingStrategy.TableName("PostStar") + ".id DESC",
}, offset, limit)
}, cs.RelationSelf, limit, offset)
}
func (s *tweetSrv) ListUserStarTweets(user *cs.VistUser, limit int, offset int) (res []*ms.PostStar, total int64, err error) {
star := &dbr.PostStar{
UserID: user.UserId,
}
if total, err = star.Count(s.db, user.RelTyp, &dbr.ConditionsT{}); err != nil {
return
}
res, err = star.List(s.db, &dbr.ConditionsT{
"ORDER": s.db.NamingStrategy.TableName("PostStar") + ".id DESC",
}, user.RelTyp, limit, offset)
return
}
func (s *tweetSrv) getUserTweets(db *gorm.DB, user *cs.VistUser, limit int, offset int) (res []*ms.Post, total int64, err error) {
visibilities := []core.PostVisibleT{core.PostVisitPublic}
switch user.RelTyp {
case cs.RelationAdmin, cs.RelationSelf:
visibilities = append(visibilities, core.PostVisitPrivate, core.PostVisitFriend)
case cs.RelationFriend:
visibilities = append(visibilities, core.PostVisitFriend)
case cs.RelationGuest:
fallthrough
default:
// nothing
}
db = db.Where("visibility IN ? AND is_del=0", visibilities)
err = db.Count(&total).Error
if err != nil {
return
}
if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit)
}
err = db.Order("latest_replied_on DESC").Find(&res).Error
return
}
func (s *tweetSrv) ListUserMediaTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
db := s.db.Table(_post_by_media_).Where("user_id=?", user.UserId)
return s.getUserTweets(db, user, limit, offset)
}
func (s *tweetSrv) ListUserCommentTweets(user *cs.VistUser, limit int, offset int) ([]*ms.Post, int64, error) {
db := s.db.Table(_post_by_comment_).Where("comment_user_id=?", user.UserId)
return s.getUserTweets(db, user, limit, offset)
}
func (s *tweetSrv) GetUserPostStarCount(userID int64) (int64, error) {
star := &dbr.PostStar{
UserID: userID,
}
return star.Count(s.db, &dbr.ConditionsT{})
return star.Count(s.db, cs.RelationSelf, &dbr.ConditionsT{})
}
func (s *tweetSrv) GetUserPostCollection(postID, userID int64) (*ms.PostCollection, error) {

@ -0,0 +1,16 @@
// 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 joint provider some common base type or logic for model define.
package joint
type BasePageInfo struct {
Page int `form:"-" binding:"-"`
PageSize int `form:"-" binding:"-"`
}
func (r *BasePageInfo) SetPageInfo(page int, pageSize int) {
r.Page, r.PageSize = page, pageSize
}

@ -27,14 +27,17 @@ type UserInfoReq struct {
}
type UserInfoResp struct {
Id int64 `json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Phone string `json:"phone"`
IsAdmin bool `json:"is_admin"`
Id int64 `json:"id"`
Nickname string `json:"nickname"`
Username string `json:"username"`
Status int `json:"status"`
Avatar string `json:"avatar"`
Balance int64 `json:"balance"`
Phone string `json:"phone"`
IsAdmin bool `json:"is_admin"`
CreatedOn int64 `json:"created_on"`
Follows int64 `json:"follows"`
Followings int64 `json:"followings"`
}
type GetUnreadMsgCountReq struct {

@ -4,24 +4,33 @@
package web
import "github.com/rocboss/paopao-ce/internal/servants/base"
import (
"github.com/rocboss/paopao-ce/internal/model/joint"
"github.com/rocboss/paopao-ce/internal/servants/base"
)
type AddFollowingReq struct {
type FollowUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
}
type DeleteFollowingReq struct {
type UnfollowUserReq struct {
BaseInfo `json:"-" binding:"-"`
UserId int64 `json:"user_id" binding:"required"`
}
type ListFollowingsReq struct {
type ListFollowsReq struct {
BaseInfo `json:"-" binding:"-"`
joint.BasePageInfo
Username string `form:"username" binding:"required"`
}
type ListFollowingsResp base.PageResp
type ListFollowsResp base.PageResp
type ListFollowersReq struct {
type ListFollowingsReq struct {
BaseInfo `form:"-" binding:"-"`
joint.BasePageInfo
Username string `form:"username" binding:"required"`
}
type ListFollowersResp base.PageResp
type ListFollowingsResp base.PageResp

@ -67,13 +67,17 @@ type GetUserProfileReq struct {
}
type GetUserProfileResp struct {
ID int64 `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"`
ID int64 `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"`
CreatedOn int64 `json:"created_on"`
Follows int64 `json:"follows"`
Followings int64 `json:"followings"`
}
type TopicListReq struct {

@ -46,6 +46,7 @@ var (
ErrDownloadExecFail = xerror.NewError(30010, "附件下载失败:扣费失败")
ErrStickPostFailed = xerror.NewError(30011, "动态置顶失败")
ErrVisblePostFailed = xerror.NewError(30012, "更新可见性失败")
ErrHighlightPostFailed = xerror.NewError(30013, "动态设为亮点失败")
ErrGetCommentsFailed = xerror.NewError(40001, "获取评论列表失败")
ErrCreateCommentFailed = xerror.NewError(40002, "评论发布失败")
@ -78,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, "取消关注话题失败")

@ -17,6 +17,7 @@ import (
"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/dao"
"github.com/rocboss/paopao-ce/internal/dao/cache"
@ -274,6 +275,37 @@ func (s *DaoServant) GetTweetList(conditions ms.ConditionsT, offset, limit int)
return posts, postFormated, err
}
func (s *DaoServant) RelationTypFrom(me *ms.User, username string) (res *cs.VistUser, err error) {
res = &cs.VistUser{
RelTyp: cs.RelationSelf,
Username: username,
}
// visit by self
if me != nil && me.Username == username {
res.UserId = me.ID
return
}
he, xerr := s.Ds.GetUserByUsername(username)
if xerr != nil || (he.Model != nil && he.ID <= 0) {
return nil, errors.New("get user failed with username: " + username)
}
res.UserId = he.ID
// visit by guest
if me == nil {
res.RelTyp = cs.RelationGuest
return
}
// visit by admin/friend/other
if me.IsAdmin {
res.RelTyp = cs.RelationAdmin
} else if s.Ds.IsFriend(me.ID, he.ID) {
res.RelTyp = cs.RelationFriend
} else {
res.RelTyp = cs.RelationGuest
}
return
}
func NewBindAnyFn() func(c *gin.Context, obj any) mir.Error {
if conf.UseSentryGin() {
return bindAnySentry

@ -2,8 +2,8 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build !embed
// +build !embed
//go:build slim && embed
// +build slim,embed
package statick

@ -2,8 +2,8 @@
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build embed
// +build embed
//go:build !(slim && embed)
// +build !slim !embed
package statick

@ -60,14 +60,21 @@ 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,
Username: user.Username,
Status: user.Status,
Avatar: user.Avatar,
Balance: user.Balance,
IsAdmin: user.IsAdmin,
Id: user.ID,
Nickname: user.Nickname,
Username: user.Username,
Status: user.Status,
Avatar: user.Avatar,
Balance: user.Balance,
IsAdmin: user.IsAdmin,
CreatedOn: user.CreatedOn,
Follows: follows,
Followings: followings,
}
if user.Phone != "" && len(user.Phone) == 11 {
resp.Phone = user.Phone[0:3] + "****" + user.Phone[7:]
@ -240,7 +247,7 @@ func (s *coreSrv) UserPhoneBind(req *web.UserPhoneBindReq) mir.Error {
}
func (s *coreSrv) GetStars(req *web.GetStarsReq) (*web.GetStarsResp, mir.Error) {
stars, err := s.Ds.GetUserPostStars(req.UserId, (req.Page-1)*req.PageSize, req.PageSize)
stars, err := s.Ds.GetUserPostStars(req.UserId, req.PageSize, (req.Page-1)*req.PageSize)
if err != nil {
logrus.Errorf("Ds.GetUserPostStars err: %s", err)
return nil, web.ErrGetStarsFailed
@ -250,7 +257,6 @@ func (s *coreSrv) GetStars(req *web.GetStarsReq) (*web.GetStarsResp, mir.Error)
logrus.Errorf("Ds.GetUserPostStars err: %s", err)
return nil, web.ErrGetStarsFailed
}
var posts []*ms.Post
for _, star := range stars {
posts = append(posts, star.Post)
@ -261,7 +267,6 @@ func (s *coreSrv) GetStars(req *web.GetStarsReq) (*web.GetStarsResp, mir.Error)
return nil, web.ErrGetStarsFailed
}
resp := base.PageRespFrom(postsFormated, req.Page, req.PageSize, totalRows)
return (*web.GetStarsResp)(resp), nil
}

@ -5,10 +5,13 @@
package web
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/model/web"
"github.com/rocboss/paopao-ce/internal/servants/base"
"github.com/rocboss/paopao-ce/internal/servants/chain"
"github.com/sirupsen/logrus"
)
var (
@ -24,6 +27,54 @@ func (s *followshipSrv) Chain() gin.HandlersChain {
return gin.HandlersChain{chain.JWT()}
}
func (s *followshipSrv) ListFollowings(r *web.ListFollowingsReq) (*web.ListFollowingsResp, mir.Error) {
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) {
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 {
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 {
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 {
return &followshipSrv{}
return &followshipSrv{
DaoServant: s,
}
}

@ -5,6 +5,7 @@
package web
import (
"fmt"
"github.com/alimy/mir/v4"
"github.com/gin-gonic/gin"
api "github.com/rocboss/paopao-ce/auto/api/v1"
@ -62,77 +63,36 @@ func (s *looseSrv) Timeline(req *web.TimelineReq) (*web.TimelineResp, mir.Error)
}
func (s *looseSrv) GetUserTweets(req *web.GetUserTweetsReq) (res *web.GetUserTweetsResp, err mir.Error) {
isSelf := (req.User != nil && req.User.Username == req.Username)
user, xerr := s.RelationTypFrom(req.User, req.Username)
if xerr != nil {
return nil, err
}
switch req.Style {
case web.UserPostsStyleComment:
res, err = s.getUserCommentTweets(req, isSelf)
case web.UserPostsStyleComment, web.UserPostsStyleMedia:
res, err = s.listUserTweets(req, user)
case web.UserPostsStyleHighlight:
res, err = s.getUserHighlightTweets(req, isSelf)
case web.UserPostsStyleMedia:
res, err = s.getUserMediaTweets(req, isSelf)
res, err = s.getUserPostTweets(req, user, true)
case web.UserPostsStyleStar:
res, err = s.getUserStarTweets(req, isSelf)
res, err = s.getUserStarTweets(req, user)
case web.UserPostsStylePost:
fallthrough
default:
res, err = s.getUserPostTweets(req)
res, err = s.getUserPostTweets(req, user, false)
}
return
}
func (s *looseSrv) getUserCommentTweets(req *web.GetUserTweetsReq, isSelf bool) (*web.GetUserTweetsResp, mir.Error) {
// TODO: add implement logic
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getUserHighlightTweets(req *web.GetUserTweetsReq, isSelf bool) (*web.GetUserTweetsResp, mir.Error) {
// TODO: add implement logic
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getUserMediaTweets(req *web.GetUserTweetsReq, isSelf bool) (*web.GetUserTweetsResp, mir.Error) {
// TODO: add implement logic
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getUserStarTweets(req *web.GetUserTweetsReq, isSelf bool) (*web.GetUserTweetsResp, mir.Error) {
if isSelf {
return s.getSelfStarTweets(req)
}
// TODO: add implement logic for not self star tweets
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getSelfCommentTweets(req *web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error) {
// TODO: add implement logic
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getSelfMediaTweets(req *web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error) {
// TODO: add implement logic
resp := base.PageRespFrom(nil, req.Page, req.PageSize, 0)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getSelfStarTweets(req *web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error) {
stars, err := s.Ds.GetUserPostStars(req.User.ID, (req.Page-1)*req.PageSize, req.PageSize)
if err != nil {
logrus.Errorf("Ds.GetUserPostStars err: %s", err)
return nil, web.ErrGetStarsFailed
}
totalRows, err := s.Ds.GetUserPostStarCount(req.User.ID)
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)
return nil, web.ErrGetStarsFailed
}
var posts []*ms.Post
for _, star := range stars {
posts = append(posts, star.Post)
if star.Post != nil {
posts = append(posts, star.Post)
}
}
postsFormated, err := s.Ds.MergePosts(posts)
if err != nil {
@ -143,28 +103,54 @@ func (s *looseSrv) getSelfStarTweets(req *web.GetUserTweetsReq) (*web.GetUserTwe
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq) (*web.GetUserTweetsResp, mir.Error) {
other, xerr := s.GetUserProfile(&web.GetUserProfileReq{
BaseInfo: req.BaseInfo,
Username: req.Username,
})
if xerr != nil {
return nil, xerr
func (s *looseSrv) listUserTweets(req *web.GetUserTweetsReq, user *cs.VistUser) (*web.GetUserTweetsResp, mir.Error) {
var (
tweets []*ms.Post
total int64
err error
)
fmt.Print(user, user.UserId, req.Style, req.PageSize, (req.Page-1)*req.PageSize)
if req.Style == web.UserPostsStyleComment {
tweets, total, err = s.Ds.ListUserCommentTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
} else if req.Style == web.UserPostsStyleMedia {
tweets, total, err = s.Ds.ListUserMediaTweets(user, req.PageSize, (req.Page-1)*req.PageSize)
} else {
logrus.Errorf("s.listUserTweets unknow style: %s", req.Style)
return nil, web.ErrGetPostsFailed
}
if err != nil {
logrus.Errorf("s.listUserTweets err: %s", err)
return nil, web.ErrGetPostsFailed
}
postFormated, err := s.Ds.MergePosts(tweets)
if err != nil {
logrus.Errorf("s.listUserTweets err: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := base.PageRespFrom(postFormated, req.Page, req.PageSize, total)
return (*web.GetUserTweetsResp)(resp), nil
}
func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq, user *cs.VistUser, isHighlight bool) (*web.GetUserTweetsResp, mir.Error) {
visibilities := []core.PostVisibleT{core.PostVisitPublic}
if req.User != nil {
if req.User.ID == other.ID || req.User.IsAdmin {
visibilities = append(visibilities, core.PostVisitPrivate, core.PostVisitFriend)
} else if other.IsFriend {
visibilities = append(visibilities, core.PostVisitFriend)
}
switch user.RelTyp {
case cs.RelationAdmin, cs.RelationSelf:
visibilities = append(visibilities, core.PostVisitPrivate, core.PostVisitFriend)
case cs.RelationFriend:
visibilities = append(visibilities, core.PostVisitFriend)
case cs.RelationGuest:
fallthrough
default:
// nothing
}
conditions := ms.ConditionsT{
"user_id": other.ID,
"user_id": user.UserId,
"visibility IN ?": visibilities,
"ORDER": "latest_replied_on DESC",
}
if isHighlight {
conditions["is_essence"] = 1
}
_, posts, err := s.GetTweetList(conditions, (req.Page-1)*req.PageSize, req.PageSize)
if err != nil {
logrus.Errorf("s.GetTweetList err: %s", err)
@ -175,7 +161,6 @@ func (s *looseSrv) getUserPostTweets(req *web.GetUserTweetsReq) (*web.GetUserTwe
logrus.Errorf("s.GetPostCount err: %s", err)
return nil, web.ErrGetPostsFailed
}
resp := base.PageRespFrom(posts, req.Page, req.PageSize, totalRows)
return (*web.GetUserTweetsResp)(resp), nil
}
@ -194,14 +179,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,
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
}

@ -634,9 +634,19 @@ func (s *privSrv) StickTweet(req *web.StickTweetReq) (*web.StickTweetResp, mir.E
}, nil
}
func (s *privSrv) HighlightTweet(req *web.HighlightTweetReq) (*web.HighlightTweetResp, mir.Error) {
// TODO
return nil, web.ErrNotImplemented
func (s *privSrv) HighlightTweet(req *web.HighlightTweetReq) (res *web.HighlightTweetResp, err mir.Error) {
if status, xerr := s.Ds.HighlightPost(req.User.ID, req.ID); xerr == nil {
res = &web.HighlightTweetResp{
HighlightStatus: status,
}
} else if xerr == cs.ErrNoPermission {
err = web.ErrNoPermission
logrus.Warnf("highlight tweet occurs no permision error with userId=%d postId=%d", req.User.ID, req.ID)
} else {
err = web.ErrHighlightPostFailed
logrus.Warnf("highlight tweet err: %s with userId=%d postId=%d", xerr, req.User.ID, req.ID)
}
return
}
func (s *privSrv) LockTweet(req *web.LockTweetReq) (*web.LockTweetResp, mir.Error) {

@ -34,19 +34,13 @@ func RouteWeb(e *gin.Engine) {
api.RegisterPubServant(e, newPubSrv(ds))
api.RegisterKeyQueryServant(e, NewShareKeyServant(ds))
api.RegisterRankServant(e, NewRankServant(ds))
api.RegisterFollowshipServant(e, newFollowshipSrv(ds))
api.RegisterFriendshipServant(e, newFriendshipSrv(ds))
// regster servants if needed by configure
cfg.In(cfg.Actions{
"Alipay": func() {
client := conf.MustAlipayClient()
api.RegisterAlipayPubServant(e, newAlipayPubSrv(ds))
api.RegisterAlipayPrivServant(e, newAlipayPrivSrv(ds, client))
},
"Followship": func() {
api.RegisterFollowshipServant(e, newFollowshipSrv(ds))
},
"Friendship": func() {
api.RegisterFriendshipServant(e, newFriendshipSrv(ds))
},
cfg.Be("Alipay", func() {
client := conf.MustAlipayClient()
api.RegisterAlipayPubServant(e, newAlipayPubSrv(ds))
api.RegisterAlipayPrivServant(e, newAlipayPrivSrv(ds, client))
})
}

@ -5,94 +5,11 @@
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"time"
"github.com/alimy/cfg"
"github.com/fatih/color"
"github.com/getsentry/sentry-go"
"github.com/rocboss/paopao-ce/internal"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/service"
"github.com/rocboss/paopao-ce/pkg/debug"
"github.com/rocboss/paopao-ce/pkg/utils"
"github.com/rocboss/paopao-ce/pkg/version"
"github.com/sourcegraph/conc"
_ "go.uber.org/automaxprocs"
)
var (
noDefaultFeatures bool
features suites
"github.com/rocboss/paopao-ce/cmd"
_ "github.com/rocboss/paopao-ce/cmd/migrate"
_ "github.com/rocboss/paopao-ce/cmd/serve"
)
type suites []string
func (s *suites) String() string {
return strings.Join(*s, ",")
}
func (s *suites) Set(value string) error {
for _, item := range strings.Split(value, ",") {
*s = append(*s, strings.TrimSpace(item))
}
return nil
}
func init() {
flagParse()
conf.Initial(features, noDefaultFeatures)
internal.Initial()
}
func deferFn() {
if cfg.If("Sentry") {
// Flush buffered events before the program terminates.
sentry.Flush(2 * time.Second)
}
}
func flagParse() {
flag.BoolVar(&noDefaultFeatures, "no-default-features", false, "whether not use default features")
flag.Var(&features, "features", "use special features")
flag.Parse()
}
func main() {
utils.PrintHelloBanner(version.VersionInfo())
ss := service.MustInitService()
if len(ss) < 1 {
fmt.Fprintln(color.Output, "no service need start so just exit")
return
}
// do defer function
defer deferFn()
// start pyroscope if need
debug.StartPyroscope()
// start services
wg := conc.NewWaitGroup()
fmt.Fprintf(color.Output, "\nstarting run service...\n\n")
service.Start(wg)
// graceful stop services
wg.Go(func() {
quit := make(chan os.Signal, 1)
// kill (no param) default send syscall.SIGTERM
// kill -2 is syscall.SIGINT
// kill -9 is syscall.SIGKILL but can't be catch, so don't need add it
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
fmt.Fprintf(color.Output, "\nshutting down server...\n\n")
service.Stop()
})
wg.Wait()
cmd.Execute()
}

@ -15,15 +15,15 @@ type Followship struct {
Chain `mir:"-"`
Group `mir:"v1"`
// AddFollowing 添加关注
AddFollowing func(Post, web.AddFollowingReq) `mir:"/following/add"`
// FollowUser 关注用户
FollowUser func(Post, web.FollowUserReq) `mir:"/user/follow"`
// DeleteFollowing 取消关注
DeleteFollowing func(Post, web.DeleteFollowingReq) `mir:"/following/delete"`
// UnfollowUser 取消关注用户
UnfollowUser func(Post, web.UnfollowUserReq) `mir:"/user/unfollow"`
// ListFollowings 获取用户的关注列表
ListFollowings func(Get, web.ListFollowingsReq) web.ListFollowingsResp `mir:"/following/list"`
// ListFollows 获取用户的关注列表
ListFollows func(Get, web.ListFollowsReq) web.ListFollowsResp `mir:"/user/follows"`
// ListFollowers 获取用户的追随者列表
ListFollowers func(Get, web.ListFollowersReq) web.ListFollowersResp `mir:"/follower/list"`
// ListFollowings 获取用户的追随者列表
ListFollowings func(Get, web.ListFollowingsReq) web.ListFollowingsResp `mir:"/user/followings"`
}

@ -21,6 +21,9 @@ func VersionInfo() string {
}
func ReadBuildInfo() *BuildInfo {
if version == "" {
version = "unknow"
}
return &BuildInfo{
Version: version,
Sum: commitID,

@ -1,3 +1,3 @@
ALTER TABLE `p_post_content` MODIFY COLUMN `content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
ALTER TABLE `p_comment_content` MODIFY COLUMN `content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
ALTER TABLE `p_comment_reply` MODIFY COLUMN `content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
ALTER TABLE `p_post_content` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
ALTER TABLE `p_comment_content` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';
ALTER TABLE `p_comment_reply` MODIFY COLUMN `content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容';

@ -0,0 +1,5 @@
DROP VIEW IF EXISTS p_post_by_media;
DROP VIEW IF EXISTS p_post_by_comment;

@ -0,0 +1,32 @@
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;

@ -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,5 @@
DROP VIEW IF EXISTS p_post_by_media;
DROP VIEW IF EXISTS p_post_by_comment;

@ -0,0 +1,32 @@
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;

@ -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,5 @@
DROP VIEW IF EXISTS p_post_by_media;
DROP VIEW IF EXISTS p_post_by_comment;

@ -0,0 +1,32 @@
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;

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

@ -70,7 +70,7 @@ CREATE TABLE `p_comment_content` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '内容ID',
`comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`type` tinyint unsigned NOT NULL DEFAULT '2' COMMENT '类型1标题2文字段落3图片地址4视频地址5语音地址6链接地址',
`sort` bigint unsigned NOT NULL DEFAULT '100' COMMENT '排序,越小越靠前',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
@ -93,7 +93,7 @@ CREATE TABLE `p_comment_reply` (
`comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`at_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '@用户ID',
`content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP城市地址',
`thumbs_up_count` int unsigned NOT NULL DEFAULT '0' COMMENT '点赞数',
@ -224,7 +224,7 @@ CREATE TABLE `p_post_content` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '内容ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`content` varchar(65535) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`content` varchar(4000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`type` tinyint unsigned NOT NULL DEFAULT '2' COMMENT '类型1标题2文字段落3图片地址4视频地址5语音地址6链接地址7附件资源8收费资源',
`sort` int unsigned NOT NULL DEFAULT '100' COMMENT '排序,越小越靠前',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
@ -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
-- ----------------------------
@ -395,4 +411,39 @@ CREATE TABLE `p_wallet_statement` (
KEY `idx_wallet_statement_user_id` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10010 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='钱包流水';
DROP VIEW IF EXISTS p_post_by_media;
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;
DROP VIEW IF EXISTS p_post_by_comment;
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;
SET FOREIGN_KEY_CHECKS = 1;

@ -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,
@ -325,3 +337,38 @@ CREATE TABLE p_wallet_statement (
is_del SMALLINT NOT NULL DEFAULT 0
);
CREATE INDEX idx_wallet_statement_user_id ON p_wallet_statement USING btree (user_id);
DROP VIEW IF EXISTS p_post_by_media;
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;
DROP VIEW IF EXISTS p_post_by_comment;
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;

@ -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
-- ----------------------------
@ -356,6 +371,41 @@ CREATE TABLE "p_wallet_statement" (
PRIMARY KEY ("id")
);
DROP VIEW IF EXISTS p_post_by_media;
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;
DROP VIEW IF EXISTS p_post_by_comment;
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;
-- ----------------------------
-- Indexes structure for table p_attachment
-- ----------------------------
@ -429,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
-- ----------------------------

@ -1 +1 @@
import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-18d4a8d3.js";import{u as a}from"./vue-router-b8e3382f.js";import{F as i,e as c,a2 as u}from"./naive-ui-62663ad7.js";import{d as l,c as d,V as t,a1 as o,o as f,e as x}from"./@vue-e0e89260.js";import{_ as g}from"./index-08d8af97.js";import"./vuex-473b3783.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./@vicons-6332ad63.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const M=g(v,[["__scopeId","data-v-e62daa85"]]);export{M as default};
import{_ as s}from"./main-nav.vue_vue_type_style_index_0_lang-32d416c3.js";import{u as a}from"./vue-router-b8e3382f.js";import{F as i,e as c,a2 as u}from"./naive-ui-62663ad7.js";import{d as l,c as d,V as t,a1 as o,o as f,e as x}from"./@vue-e0e89260.js";import{_ as g}from"./index-152c5794.js";import"./vuex-473b3783.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./@vicons-0524c43e.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const v=l({__name:"404",setup(h){const e=a(),_=()=>{e.push({path:"/"})};return(k,w)=>{const n=s,p=c,r=u,m=i;return f(),d("div",null,[t(n,{title:"404"}),t(m,{class:"main-content-wrap wrap404",bordered:""},{default:o(()=>[t(r,{status:"404",title:"404 资源不存在",description:"再看看其他的吧"},{footer:o(()=>[t(p,{onClick:_},{default:o(()=>[x("回主页")]),_:1})]),_:1})]),_:1})])}}});const M=g(v,[["__scopeId","data-v-e62daa85"]]);export{M as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +1 @@
import{_ as F}from"./post-skeleton-41befd31.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-18d4a8d3.js";import{u as V}from"./vuex-473b3783.js";import{b as z}from"./vue-router-b8e3382f.js";import{a as A}from"./formatTime-cdf4e6f1.js";import{d as R,r as n,j as S,c as o,V as a,a1 as p,o as e,_ as u,O as l,F as I,a4 as L,Q as M,a as s,M as _,L as O}from"./@vue-e0e89260.js";import{F as P,G as j,I as q,H as D}from"./naive-ui-62663ad7.js";import{_ as E}from"./index-08d8af97.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./@vicons-6332ad63.js";import"./moment-2ab8298d.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const G={key:0,class:"pagination-wrap"},H={key:0,class:"skeleton-wrap"},Q={key:1},T={key:0,class:"empty-wrap"},U={class:"bill-line"},$=R({__name:"Anouncement",setup(J){const d=V(),g=z(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{i.value=m};return S(()=>{}),(m,K)=>{const y=N,k=j,x=F,w=q,B=D,C=P;return e(),o("div",null,[a(y,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(e(),o("div",G,[a(k,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":c.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(e(),o("div",H,[a(x,{num:f.value},null,8,["num"])])):(e(),o("div",Q,[r.value.length===0?(e(),o("div",T,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(e(!0),o(I,null,L(r.value,t=>(e(),M(B,{key:t.id},{default:p(()=>[s("div",U,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:O({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(A)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const kt=E($,[["__scopeId","data-v-d4d04859"]]);export{kt as default};
import{_ as F}from"./post-skeleton-d2e839d6.js";import{_ as N}from"./main-nav.vue_vue_type_style_index_0_lang-32d416c3.js";import{u as V}from"./vuex-473b3783.js";import{b as z}from"./vue-router-b8e3382f.js";import{a as A}from"./formatTime-4210fcd1.js";import{d as R,r as n,j as S,c as o,V as a,a1 as p,o as e,_ as u,O as l,F as I,a4 as L,Q as M,a as s,M as _,L as O}from"./@vue-e0e89260.js";import{F as P,G as j,I as q,H as D}from"./naive-ui-62663ad7.js";import{_ as E}from"./index-152c5794.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./@vicons-0524c43e.js";import"./moment-2ab8298d.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";/* empty css */const G={key:0,class:"pagination-wrap"},H={key:0,class:"skeleton-wrap"},Q={key:1},T={key:0,class:"empty-wrap"},U={class:"bill-line"},$=R({__name:"Anouncement",setup(J){const d=V(),g=z(),v=n(!1),r=n([]),i=n(+g.query.p||1),f=n(20),c=n(0),h=m=>{i.value=m};return S(()=>{}),(m,K)=>{const y=N,k=j,x=F,w=q,B=D,C=P;return e(),o("div",null,[a(y,{title:"公告"}),a(C,{class:"main-content-wrap",bordered:""},{footer:p(()=>[c.value>1?(e(),o("div",G,[a(k,{page:i.value,"onUpdate:page":h,"page-slot":u(d).state.collapsedRight?5:8,"page-count":c.value},null,8,["page","page-slot","page-count"])])):l("",!0)]),default:p(()=>[v.value?(e(),o("div",H,[a(x,{num:f.value},null,8,["num"])])):(e(),o("div",Q,[r.value.length===0?(e(),o("div",T,[a(w,{size:"large",description:"暂无数据"})])):l("",!0),(e(!0),o(I,null,L(r.value,t=>(e(),M(B,{key:t.id},{default:p(()=>[s("div",U,[s("div",null,"NO."+_(t.id),1),s("div",null,_(t.reason),1),s("div",{class:O({income:t.change_amount>=0,out:t.change_amount<0})},_((t.change_amount>0?"+":"")+(t.change_amount/100).toFixed(2)),3),s("div",null,_(u(A)(t.created_on)),1)])]),_:2},1024))),128))]))]),_:1})])}}});const kt=E($,[["__scopeId","data-v-d4d04859"]]);export{kt as default};

@ -0,0 +1 @@
import{_ as N,a as P}from"./post-item.vue_vue_type_style_index_0_lang-25504051.js";import{_ as S}from"./post-skeleton-d2e839d6.js";import{_ as V}from"./main-nav.vue_vue_type_style_index_0_lang-32d416c3.js";import{u as $}from"./vuex-473b3783.js";import{b as I}from"./vue-router-b8e3382f.js";import{N as R,_ as j}from"./index-152c5794.js";import{d as q,r as s,j as E,c as o,V as e,a1 as c,_ as g,O as v,o as t,F as f,a4 as h,Q as k}from"./@vue-e0e89260.js";import{F as G,G as H,I as L,H as O}from"./naive-ui-62663ad7.js";import"./content-ff44e340.js";import"./@vicons-0524c43e.js";import"./paopao-video-player-aa5e8b3f.js";import"./formatTime-4210fcd1.js";import"./moment-2ab8298d.js";import"./copy-to-clipboard-1dd3075d.js";import"./toggle-selection-93f4ad84.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./axios-4a70c6fc.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const Q={key:0,class:"skeleton-wrap"},T={key:1},U={key:0,class:"empty-wrap"},A={key:1},D={key:2},J={key:0,class:"pagination-wrap"},K=q({__name:"Collection",setup(W){const m=$(),y=I(),_=s(!1),i=s([]),p=s(+y.query.p||1),l=s(20),r=s(0),u=()=>{_.value=!0,R({page:p.value,page_size:l.value}).then(n=>{_.value=!1,i.value=n.list,r.value=Math.ceil(n.pager.total_rows/l.value),window.scrollTo(0,0)}).catch(n=>{_.value=!1})},w=n=>{p.value=n,u()};return E(()=>{u()}),(n,X)=>{const C=V,b=S,x=L,z=N,d=O,B=P,F=G,M=H;return t(),o("div",null,[e(C,{title:"收藏"}),e(F,{class:"main-content-wrap",bordered:""},{default:c(()=>[_.value?(t(),o("div",Q,[e(b,{num:l.value},null,8,["num"])])):(t(),o("div",T,[i.value.length===0?(t(),o("div",U,[e(x,{size:"large",description:"暂无数据"})])):v("",!0),g(m).state.desktopModelShow?(t(),o("div",A,[(t(!0),o(f,null,h(i.value,a=>(t(),k(d,{key:a.id},{default:c(()=>[e(z,{post:a},null,8,["post"])]),_:2},1024))),128))])):(t(),o("div",D,[(t(!0),o(f,null,h(i.value,a=>(t(),k(d,{key:a.id},{default:c(()=>[e(B,{post:a},null,8,["post"])]),_:2},1024))),128))]))]))]),_:1}),r.value>0?(t(),o("div",J,[e(M,{page:p.value,"onUpdate:page":w,"page-slot":g(m).state.collapsedRight?5:8,"page-count":r.value},null,8,["page","page-slot","page-count"])])):v("",!0)])}}});const Mt=j(K,[["__scopeId","data-v-a5302c9b"]]);export{Mt as default};

@ -1 +0,0 @@
import{_ as P,a as S}from"./post-item.vue_vue_type_style_index_0_lang-3baf8ba8.js";import{_ as V}from"./post-skeleton-41befd31.js";import{_ as $}from"./main-nav.vue_vue_type_style_index_0_lang-18d4a8d3.js";import{u as I}from"./vuex-473b3783.js";import{b as L}from"./vue-router-b8e3382f.js";import{L as N,_ as R}from"./index-08d8af97.js";import{d as j,r as s,j as q,c as o,V as e,a1 as c,_ as g,O as v,o as t,F as f,a4 as h,Q as k}from"./@vue-e0e89260.js";import{F as E,G,I as H,H as O}from"./naive-ui-62663ad7.js";import"./content-91ba374b.js";import"./@vicons-6332ad63.js";import"./paopao-video-player-aa5e8b3f.js";import"./formatTime-cdf4e6f1.js";import"./moment-2ab8298d.js";import"./copy-to-clipboard-1dd3075d.js";import"./toggle-selection-93f4ad84.js";import"./vooks-a50491fd.js";import"./evtd-b614532e.js";import"./axios-4a70c6fc.js";/* empty css */import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./@css-render-580d83ec.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";const Q={key:0,class:"skeleton-wrap"},T={key:1},U={key:0,class:"empty-wrap"},A={key:1},D={key:2},J={key:0,class:"pagination-wrap"},K=j({__name:"Collection",setup(W){const m=I(),y=L(),_=s(!1),i=s([]),p=s(+y.query.p||1),l=s(20),r=s(0),u=()=>{_.value=!0,N({page:p.value,page_size:l.value}).then(n=>{_.value=!1,i.value=n.list,r.value=Math.ceil(n.pager.total_rows/l.value),window.scrollTo(0,0)}).catch(n=>{_.value=!1})},w=n=>{p.value=n,u()};return q(()=>{u()}),(n,X)=>{const C=$,b=V,x=H,z=P,d=O,B=S,F=E,M=G;return t(),o("div",null,[e(C,{title:"收藏"}),e(F,{class:"main-content-wrap",bordered:""},{default:c(()=>[_.value?(t(),o("div",Q,[e(b,{num:l.value},null,8,["num"])])):(t(),o("div",T,[i.value.length===0?(t(),o("div",U,[e(x,{size:"large",description:"暂无数据"})])):v("",!0),g(m).state.desktopModelShow?(t(),o("div",A,[(t(!0),o(f,null,h(i.value,a=>(t(),k(d,{key:a.id},{default:c(()=>[e(z,{post:a},null,8,["post"])]),_:2},1024))),128))])):(t(),o("div",D,[(t(!0),o(f,null,h(i.value,a=>(t(),k(d,{key:a.id},{default:c(()=>[e(B,{post:a},null,8,["post"])]),_:2},1024))),128))]))]))]),_:1}),r.value>0?(t(),o("div",J,[e(M,{page:p.value,"onUpdate:page":w,"page-slot":g(m).state.collapsedRight?5:8,"page-count":r.value},null,8,["page","page-slot","page-count"])])):v("",!0)])}}});const Mt=R(K,[["__scopeId","data-v-a5302c9b"]]);export{Mt as default};

@ -1 +0,0 @@
import{u as N,b as P}from"./vue-router-b8e3382f.js";import{d as k,o as e,c as n,a as s,V as a,M as d,r as c,j as R,a1 as f,_ as S,O as h,F as y,a4 as U,Q as q}from"./@vue-e0e89260.js";import{o as x,F as D,G as O,I as T,H as j}from"./naive-ui-62663ad7.js";import{_ as b,O as E}from"./index-08d8af97.js";import{_ as G}from"./post-skeleton-41befd31.js";import{_ as H}from"./main-nav.vue_vue_type_style_index_0_lang-18d4a8d3.js";import{u as L}from"./vuex-473b3783.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./evtd-b614532e.js";import"./@css-render-580d83ec.js";import"./vooks-a50491fd.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";import"./@vicons-6332ad63.js";/* empty css */const Q={class:"avatar"},A={class:"base-info"},J={class:"username"},K={class:"uid"},W=k({__name:"contact-item",props:{contact:{}},setup(C){const l=N(),u=t=>{l.push({name:"user",query:{username:t}})};return(t,o)=>{const _=x;return e(),n("div",{class:"contact-item",onClick:o[0]||(o[0]=r=>u(t.contact.username))},[s("div",Q,[a(_,{size:"large",src:t.contact.avatar},null,8,["src"])]),s("div",A,[s("div",J,[s("strong",null,d(t.contact.nickname),1),s("span",null," @"+d(t.contact.username),1)]),s("div",K,"UID. "+d(t.contact.user_id),1)])])}}});const X=b(W,[["__scopeId","data-v-08ee9b2e"]]),Y={key:0,class:"skeleton-wrap"},Z={key:1},tt={key:0,class:"empty-wrap"},et={key:0,class:"pagination-wrap"},ot=k({__name:"Contacts",setup(C){const l=L(),u=P(),t=c(!1),o=c([]),_=c(+u.query.p||1),r=c(20),m=c(0),w=i=>{_.value=i,v()};R(()=>{v()});const v=(i=!1)=>{o.value.length===0&&(t.value=!0),E({page:_.value,page_size:r.value}).then(p=>{t.value=!1,o.value=p.list,m.value=Math.ceil(p.pager.total_rows/r.value),i&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(p=>{t.value=!1})};return(i,p)=>{const $=H,I=G,z=T,B=X,V=j,F=D,M=O;return e(),n(y,null,[s("div",null,[a($,{title:"好友"}),a(F,{class:"main-content-wrap",bordered:""},{default:f(()=>[t.value?(e(),n("div",Y,[a(I,{num:r.value},null,8,["num"])])):(e(),n("div",Z,[o.value.length===0?(e(),n("div",tt,[a(z,{size:"large",description:"暂无数据"})])):h("",!0),(e(!0),n(y,null,U(o.value,g=>(e(),q(V,{key:g.user_id},{default:f(()=>[a(B,{contact:g},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})]),m.value>0?(e(),n("div",et,[a(M,{page:_.value,"onUpdate:page":w,"page-slot":S(l).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):h("",!0)],64)}}});const zt=b(ot,[["__scopeId","data-v-3b2bf978"]]);export{zt as default};

@ -0,0 +1 @@
.contact-item[data-v-0f1bf4ae]{display:flex;width:100%;padding:12px 16px}.contact-item[data-v-0f1bf4ae]:hover{background:#f7f9f9;cursor:pointer}.contact-item .avatar[data-v-0f1bf4ae]{width:55px}.contact-item .base-info[data-v-0f1bf4ae]{position:relative;width:calc(100% - 55px)}.contact-item .base-info .username[data-v-0f1bf4ae]{line-height:16px;font-size:16px}.contact-item .base-info .uid[data-v-0f1bf4ae]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.dark .contact-item[data-v-0f1bf4ae]{background-color:#101014bf}.dark .contact-item[data-v-0f1bf4ae]:hover{background:#18181c}.pagination-wrap[data-v-3b2bf978]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-3b2bf978],.dark .empty-wrap[data-v-3b2bf978],.dark .skeleton-wrap[data-v-3b2bf978]{background-color:#101014bf}

@ -1 +0,0 @@
.contact-item[data-v-08ee9b2e]{display:flex;width:100%;padding:12px 16px}.contact-item[data-v-08ee9b2e]:hover{background:#f7f9f9;cursor:pointer}.contact-item .avatar[data-v-08ee9b2e]{width:55px}.contact-item .base-info[data-v-08ee9b2e]{position:relative;width:calc(100% - 55px)}.contact-item .base-info .username[data-v-08ee9b2e]{line-height:16px;font-size:16px}.contact-item .base-info .uid[data-v-08ee9b2e]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.dark .contact-item[data-v-08ee9b2e]{background-color:#101014bf}.dark .contact-item[data-v-08ee9b2e]:hover{background:#18181c}.pagination-wrap[data-v-3b2bf978]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-3b2bf978],.dark .empty-wrap[data-v-3b2bf978],.dark .skeleton-wrap[data-v-3b2bf978]{background-color:#101014bf}

@ -0,0 +1 @@
import{u as N,b as P}from"./vue-router-b8e3382f.js";import{d as k,o as e,c as n,a as s,V as a,M as d,r as c,j as R,a1 as f,_ as S,O as h,F as y,a4 as U,Q as q}from"./@vue-e0e89260.js";import{o as x,F as D,G as Q,I as T,H as j}from"./naive-ui-62663ad7.js";import{_ as b,Q as E}from"./index-152c5794.js";import{_ as G}from"./post-skeleton-d2e839d6.js";import{_ as H}from"./main-nav.vue_vue_type_style_index_0_lang-32d416c3.js";import{u as L}from"./vuex-473b3783.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./evtd-b614532e.js";import"./@css-render-580d83ec.js";import"./vooks-a50491fd.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";import"./@vicons-0524c43e.js";/* empty css */const O={class:"avatar"},A={class:"base-info"},J={class:"username"},K={class:"uid"},W=k({__name:"contact-item",props:{contact:{}},setup(C){const l=N(),u=t=>{l.push({name:"user",query:{s:t}})};return(t,o)=>{const _=x;return e(),n("div",{class:"contact-item",onClick:o[0]||(o[0]=r=>u(t.contact.username))},[s("div",O,[a(_,{size:"large",src:t.contact.avatar},null,8,["src"])]),s("div",A,[s("div",J,[s("strong",null,d(t.contact.nickname),1),s("span",null," @"+d(t.contact.username),1)]),s("div",K,"UID. "+d(t.contact.user_id),1)])])}}});const X=b(W,[["__scopeId","data-v-0f1bf4ae"]]),Y={key:0,class:"skeleton-wrap"},Z={key:1},tt={key:0,class:"empty-wrap"},et={key:0,class:"pagination-wrap"},ot=k({__name:"Contacts",setup(C){const l=L(),u=P(),t=c(!1),o=c([]),_=c(+u.query.p||1),r=c(20),m=c(0),w=i=>{_.value=i,v()};R(()=>{v()});const v=(i=!1)=>{o.value.length===0&&(t.value=!0),E({page:_.value,page_size:r.value}).then(p=>{t.value=!1,o.value=p.list,m.value=Math.ceil(p.pager.total_rows/r.value),i&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(p=>{t.value=!1})};return(i,p)=>{const $=H,I=G,z=T,B=X,V=j,F=D,M=Q;return e(),n(y,null,[s("div",null,[a($,{title:"好友"}),a(F,{class:"main-content-wrap",bordered:""},{default:f(()=>[t.value?(e(),n("div",Y,[a(I,{num:r.value},null,8,["num"])])):(e(),n("div",Z,[o.value.length===0?(e(),n("div",tt,[a(z,{size:"large",description:"暂无数据"})])):h("",!0),(e(!0),n(y,null,U(o.value,g=>(e(),q(V,{key:g.user_id},{default:f(()=>[a(B,{contact:g},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})]),m.value>0?(e(),n("div",et,[a(M,{page:_.value,"onUpdate:page":w,"page-slot":S(l).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):h("",!0)],64)}}});const zt=b(ot,[["__scopeId","data-v-3b2bf978"]]);export{zt as default};

@ -0,0 +1 @@
import{u as j,b as E}from"./vue-router-b8e3382f.js";import{d as q,o as s,c as l,a as _,V as o,M as h,r as c,j as G,_ as F,a1 as y,O as $,F as U,a4 as H,Q as L}from"./@vue-e0e89260.js";import{o as O,F as Q,G as A,f as J,g as K,I as W,H as X}from"./naive-ui-62663ad7.js";import{_ as z,R as Y,S as Z}from"./index-152c5794.js";import{_ as ee}from"./post-skeleton-d2e839d6.js";import{_ as oe}from"./main-nav.vue_vue_type_style_index_0_lang-32d416c3.js";import{u as te}from"./vuex-473b3783.js";import"./seemly-76b7b838.js";import"./vueuc-59ca65c3.js";import"./evtd-b614532e.js";import"./@css-render-580d83ec.js";import"./vooks-a50491fd.js";import"./vdirs-b0483831.js";import"./@juggle-41516555.js";import"./css-render-6a5c5852.js";import"./@emotion-8a8e73f6.js";import"./lodash-es-8412e618.js";import"./treemate-25c27bff.js";import"./async-validator-dee29e8b.js";import"./date-fns-975a2d8f.js";import"./axios-4a70c6fc.js";import"./@vicons-0524c43e.js";/* empty css */const ne={class:"avatar"},ae={class:"base-info"},se={class:"username"},le={class:"uid"},_e=q({__name:"follow-item",props:{contact:{}},setup(I){const v=j(),u=e=>{v.push({name:"user",query:{s:e}})};return(e,t)=>{const g=O;return s(),l("div",{class:"follow-item",onClick:t[0]||(t[0]=f=>u(e.contact.username))},[_("div",ne,[o(g,{size:"large",src:e.contact.avatar},null,8,["src"])]),_("div",ae,[_("div",se,[_("strong",null,h(e.contact.nickname),1),_("span",null," @"+h(e.contact.username),1)]),_("div",le,"UID. "+h(e.contact.user_id),1)])])}}});const ue=z(_e,[["__scopeId","data-v-6e07cba3"]]),ce={key:0,class:"skeleton-wrap"},ie={key:1},re={key:0,class:"empty-wrap"},pe={key:0,class:"pagination-wrap"},me=q({__name:"Following",setup(I){const v=te(),u=E(),e=c(!1),t=c([]),g=u.query.n||"粉丝详情",f=u.query.s||"",r=c(u.query.t||"follows"),p=c(+u.query.p||1),i=c(20),m=c(0),T=a=>{p.value=a,w()},B=a=>{r.value=a,w()},w=()=>{r.value==="follows"?C(f):r.value==="followings"&&M(f)},C=(a,d=!1)=>{t.value.length===0&&(e.value=!0),Y({username:a,page:p.value,page_size:i.value}).then(n=>{e.value=!1,t.value=n.list||[],m.value=Math.ceil(n.pager.total_rows/i.value),d&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(n=>{e.value=!1})},M=(a,d=!1)=>{t.value.length===0&&(e.value=!0),Z({username:a,page:p.value,page_size:i.value}).then(n=>{e.value=!1,t.value=n.list||[],m.value=Math.ceil(n.pager.total_rows/i.value),d&&setTimeout(()=>{window.scrollTo(0,99999)},50)}).catch(n=>{e.value=!1})};return G(()=>{w()}),(a,d)=>{const n=oe,k=J,P=K,R=ee,S=W,V=ue,N=X,x=Q,D=A;return s(),l(U,null,[_("div",null,[o(n,{title:F(g),back:!0},null,8,["title"]),o(x,{class:"main-content-wrap",bordered:""},{default:y(()=>[o(P,{type:"line",animated:"","default-value":r.value,"onUpdate:value":B},{default:y(()=>[o(k,{name:"follows",tab:"正在关注"}),o(k,{name:"followings",tab:"我的粉丝"})]),_:1},8,["default-value"]),e.value?(s(),l("div",ce,[o(R,{num:i.value},null,8,["num"])])):(s(),l("div",ie,[t.value.length===0?(s(),l("div",re,[o(S,{size:"large",description:"暂无数据"})])):$("",!0),(s(!0),l(U,null,H(t.value,b=>(s(),L(N,{key:b.user_id},{default:y(()=>[o(V,{contact:b},null,8,["contact"])]),_:2},1024))),128))]))]),_:1})]),m.value>0?(s(),l("div",pe,[o(D,{page:p.value,"onUpdate:page":T,"page-slot":F(v).state.collapsedRight?5:8,"page-count":m.value},null,8,["page","page-slot","page-count"])])):$("",!0)],64)}}});const Ne=z(me,[["__scopeId","data-v-1f0f223d"]]);export{Ne as default};

@ -0,0 +1 @@
.follow-item[data-v-6e07cba3]{display:flex;width:100%;padding:12px 16px}.follow-item[data-v-6e07cba3]:hover{background:#f7f9f9;cursor:pointer}.follow-item .avatar[data-v-6e07cba3]{width:55px}.follow-item .base-info[data-v-6e07cba3]{position:relative;width:calc(100% - 55px)}.follow-item .base-info .username[data-v-6e07cba3]{line-height:16px;font-size:16px}.follow-item .base-info .uid[data-v-6e07cba3]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.dark .follow-item[data-v-6e07cba3]{background-color:#101014bf}.dark .follow-item[data-v-6e07cba3]:hover{background:#18181c}.main-content-wrap[data-v-1f0f223d]{padding:20px}.pagination-wrap[data-v-1f0f223d]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .main-content-wrap[data-v-1f0f223d],.dark .empty-wrap[data-v-1f0f223d],.dark .skeleton-wrap[data-v-1f0f223d]{background-color:#101014bf}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
.message-item[data-v-07fc447f]{padding:16px}.message-item.unread[data-v-07fc447f]{background:#fcfffc}.message-item .sender-wrap[data-v-07fc447f]{display:flex;align-items:center}.message-item .sender-wrap .username[data-v-07fc447f]{opacity:.75;font-size:14px}.message-item .timestamp[data-v-07fc447f]{opacity:.75;font-size:12px;display:flex;align-items:center}.message-item .timestamp .timestamp-txt[data-v-07fc447f]{margin-left:6px}.message-item .brief-wrap[data-v-07fc447f]{margin-top:10px}.message-item .brief-wrap .brief-content[data-v-07fc447f],.message-item .brief-wrap .whisper-content-wrap[data-v-07fc447f],.message-item .brief-wrap .requesting-friend-wrap[data-v-07fc447f]{display:flex;width:100%}.message-item .view-link[data-v-07fc447f]{margin-left:8px;display:flex;align-items:center}.message-item .status-info[data-v-07fc447f]{margin-left:8px;align-items:center}.dark .message-item[data-v-07fc447f]{background-color:#101014bf}.dark .message-item.unread[data-v-07fc447f]{background:#0f180b}.dark .message-item .brief-wrap[data-v-07fc447f]{background-color:#18181c}.skeleton-item[data-v-01d2e871]{padding:12px;display:flex}.skeleton-item .content[data-v-01d2e871]{width:100%}.dark .skeleton-item[data-v-01d2e871]{background-color:#101014bf}.pagination-wrap[data-v-4e7b1342]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .empty-wrap[data-v-4e7b1342],.dark .messages-wrap[data-v-4e7b1342],.dark .pagination-wrap[data-v-4e7b1342]{background-color:#101014bf}

@ -1 +0,0 @@
.message-item[data-v-4a0e27fa]{padding:16px}.message-item.unread[data-v-4a0e27fa]{background:#fcfffc}.message-item .sender-wrap[data-v-4a0e27fa]{display:flex;align-items:center}.message-item .sender-wrap .username[data-v-4a0e27fa]{opacity:.75;font-size:14px}.message-item .timestamp[data-v-4a0e27fa]{opacity:.75;font-size:12px;display:flex;align-items:center}.message-item .timestamp .timestamp-txt[data-v-4a0e27fa]{margin-left:6px}.message-item .brief-wrap[data-v-4a0e27fa]{margin-top:10px}.message-item .brief-wrap .brief-content[data-v-4a0e27fa],.message-item .brief-wrap .whisper-content-wrap[data-v-4a0e27fa],.message-item .brief-wrap .requesting-friend-wrap[data-v-4a0e27fa]{display:flex;width:100%}.message-item .view-link[data-v-4a0e27fa]{margin-left:8px;display:flex;align-items:center}.message-item .status-info[data-v-4a0e27fa]{margin-left:8px;align-items:center}.dark .message-item[data-v-4a0e27fa]{background-color:#101014bf}.dark .message-item.unread[data-v-4a0e27fa]{background:#0f180b}.dark .message-item .brief-wrap[data-v-4a0e27fa]{background-color:#18181c}.skeleton-item[data-v-01d2e871]{padding:12px;display:flex}.skeleton-item .content[data-v-01d2e871]{width:100%}.dark .skeleton-item[data-v-01d2e871]{background-color:#101014bf}.pagination-wrap[data-v-4e7b1342]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .empty-wrap[data-v-4e7b1342],.dark .messages-wrap[data-v-4e7b1342],.dark .pagination-wrap[data-v-4e7b1342]{background-color:#101014bf}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
.profile-baseinfo[data-v-08661398]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-08661398]{width:55px}.profile-baseinfo .base-info[data-v-08661398]{position:relative;width:calc(100% - 55px)}.profile-baseinfo .base-info .username[data-v-08661398]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .uid[data-v-08661398]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-tabs-wrap[data-v-08661398]{padding:0 16px}.pagination-wrap[data-v-08661398]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .profile-baseinfo[data-v-08661398]{background-color:#18181c}.dark .profile-wrap[data-v-08661398],.dark .pagination-wrap[data-v-08661398]{background-color:#101014bf}

@ -0,0 +1 @@
.profile-baseinfo[data-v-79a284ce]{display:flex;padding:16px}.profile-baseinfo .avatar[data-v-79a284ce]{width:72px}.profile-baseinfo .base-info[data-v-79a284ce]{position:relative;margin-left:12px;width:calc(100% - 84px)}.profile-baseinfo .base-info .username[data-v-79a284ce]{line-height:16px;font-size:16px}.profile-baseinfo .base-info .userinfo[data-v-79a284ce]{font-size:14px;line-height:14px;margin-top:10px;opacity:.75}.profile-baseinfo .base-info .userinfo .info-item[data-v-79a284ce]{margin-right:12px}.profile-baseinfo .base-info .top-tag[data-v-79a284ce]{transform:scale(.75)}.profile-tabs-wrap[data-v-79a284ce]{padding:0 16px}.pagination-wrap[data-v-79a284ce]{padding:10px;display:flex;justify-content:center;overflow:hidden}.dark .profile-wrap[data-v-79a284ce],.dark .pagination-wrap[data-v-79a284ce]{background-color:#101014bf}

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

Loading…
Cancel
Save