diff --git a/README.md b/README.md index c2ca44ce..b793da4e 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,6 @@ release/paopao-ce --no-default-features --features sqlite3,localoss,loggerfile,r |`Docs` | 子服务 | WIP | 开启开发者文档服务| |`Frontend:Web` | 子服务 | 稳定 | 开启独立前端服务| |`Frontend:EmbedWeb` | 子服务 | 稳定 | 开启内嵌于后端Web API服务中的前端服务| -|`Deprecated:Web` | 子服务 | 稳定 | 开启旧的Web服务| |`Gorm` | 数据库 | 稳定(默认) | 使用[gorm](https://github.com/go-gorm/gorm)作为数据库的ORM,默认使用 `Gorm` + `MySQL`组合| |`Sqlx`| 数据库 | WIP | 使用[sqlx](https://github.com/jmoiron/sqlx)作为数据库的ORM| |`Sqlc`| 数据库 | WIP | 使用[sqlc](https://github.com/kyleconroy/sqlc)自动生成ORM代码| diff --git a/ROADMAP.md b/ROADMAP.md index 7ef7c15b..da95ed77 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -9,7 +9,7 @@ * [ ] add extend base ORM code for implement data logic base sqlx/sqlc * [ ] optimize media tweet submit logic * [ ] optimize search logic service -* [ ] remove `Deprecated:OldWeb` feature +* [x] remove `Deprecated:OldWeb` feature #### v0.2.0 diff --git a/features-status.md b/features-status.md index 132121da..413ab55f 100644 --- a/features-status.md +++ b/features-status.md @@ -42,7 +42,7 @@ * `Frontend:EmbedWeb` 开启内嵌于后端Web API服务中的前端服务(目前状态: 内测) * [ ] 提按文档 * [x] 服务初始化逻辑 -* `Deprecated:OldWeb` 开启旧的Web服务(目前状态: 内测) +* `Deprecated:OldWeb` 开启旧的Web服务(目前状态: 已弃,不可用) * [ ] 提按文档 * [x] 服务初始化逻辑 diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 0554b38e..1f90bfd1 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -24,7 +24,6 @@ var ( MysqlSetting *MySQLSettingS PostgresSetting *PostgresSettingS Sqlite3Setting *Sqlite3SettingS - ServerSetting *HttpServerSettingS WebServerSetting *HttpServerSettingS AdminServerSetting *HttpServerSettingS SpaceXServerSetting *HttpServerSettingS @@ -68,7 +67,6 @@ func setupSetting(suite []string, noDefault bool) error { objects := map[string]any{ "App": &AppSetting, - "Server": &ServerSetting, "WebServer": &WebServerSetting, "AdminServer": &AdminServerSetting, "SpaceXServer": &SpaceXServerSetting, @@ -155,8 +153,5 @@ func GetOssDomain() string { } func RunMode() string { - if !cfg.If("Deprecated:OldWeb") { - return ServerSetting.RunMode - } return AppSetting.RunMode } diff --git a/internal/conf/config.yaml b/internal/conf/config.yaml index f8d34aaf..6f0324db 100644 --- a/internal/conf/config.yaml +++ b/internal/conf/config.yaml @@ -5,12 +5,6 @@ App: # APP基础设置项 DefaultContextTimeout: 60 DefaultPageSize: 10 MaxPageSize: 100 -Server: # 服务设置 - RunMode: debug - HttpIp: 0.0.0.0 - HttpPort: 8008 - ReadTimeout: 60 - WriteTimeout: 60 Features: Default: [] WebServer: # Web服务 @@ -19,31 +13,37 @@ WebServer: # Web服务 ReadTimeout: 60 WriteTimeout: 60 AdminServer: # Admin后台运维服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8014 ReadTimeout: 60 WriteTimeout: 60 SpaceXServer: # SpaceX服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8012 ReadTimeout: 60 WriteTimeout: 60 BotServer: # Bot服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8016 ReadTimeout: 60 WriteTimeout: 60 LocalossServer: # Localoss服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8018 ReadTimeout: 60 WriteTimeout: 60 FrontendWebServer: # Web前端静态资源服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8006 ReadTimeout: 60 WriteTimeout: 60 DocsServer: # 开发文档服务 + RunMode: debug HttpIp: 0.0.0.0 HttpPort: 8011 ReadTimeout: 60 diff --git a/internal/dao/dao.go b/internal/dao/dao.go index 2a70016f..8238cdc6 100644 --- a/internal/dao/dao.go +++ b/internal/dao/dao.go @@ -20,85 +20,112 @@ import ( var ( ts core.TweetSearchService ds core.DataService + dsa core.DataServantA oss core.ObjectStorageService - onceTs, onceDs, onceOss sync.Once + _onceInitial sync.Once ) func DataService() core.DataService { - onceDs.Do(func() { - var v core.VersionInfo - if cfg.If("Gorm") { - ds, v = jinzhu.NewDataService() - } else if cfg.If("Sqlx") { - ds, v = sakila.NewDataService() - } else if cfg.If("Sqlc") && (cfg.If("Postgres") || cfg.If("PostgreSQL")) { - ds, v = slonik.NewDataService() - } else { - // default use gorm as orm for sql database - ds, v = jinzhu.NewDataService() - } - logrus.Infof("use %s as data service with version %s", v.Name(), v.Version()) - }) + lazyInitial() return ds } +func DataServantA() core.DataServantA { + lazyInitial() + return dsa +} + func ObjectStorageService() core.ObjectStorageService { - onceOss.Do(func() { - var v core.VersionInfo - if cfg.If("AliOSS") { - oss, v = storage.MustAliossService() - } else if cfg.If("COS") { - oss, v = storage.NewCosService() - } else if cfg.If("HuaweiOBS") { - oss, v = storage.MustHuaweiobsService() - } else if cfg.If("MinIO") { - oss, v = storage.MustMinioService() - } else if cfg.If("S3") { - oss, v = storage.MustS3Service() - logrus.Infof("use S3 as object storage by version %s", v.Version()) - return - } else if cfg.If("LocalOSS") { - oss, v = storage.MustLocalossService() - } else { - // default use AliOSS as object storage service - oss, v = storage.MustAliossService() - logrus.Infof("use default AliOSS as object storage by version %s", v.Version()) - return - } - logrus.Infof("use %s as object storage by version %s", v.Name(), v.Version()) - }) + lazyInitial() return oss } func TweetSearchService() core.TweetSearchService { - onceTs.Do(func() { - var v core.VersionInfo - ams := newAuthorizationManageService() - if cfg.If("Zinc") { - ts, v = search.NewZincTweetSearchService(ams) - } else if cfg.If("Meili") { - ts, v = search.NewMeiliTweetSearchService(ams) - } else { - // default use Zinc as tweet search service - ts, v = search.NewZincTweetSearchService(ams) - } - logrus.Infof("use %s as tweet search serice by version %s", v.Name(), v.Version()) - - ts = search.NewBridgeTweetSearchService(ts) - }) + lazyInitial() return ts } -func newAuthorizationManageService() (s core.AuthorizationManageService) { +func newAuthorizationManageService() (ams core.AuthorizationManageService) { if cfg.If("Gorm") { - s = jinzhu.NewAuthorizationManageService() + ams = jinzhu.NewAuthorizationManageService() } else if cfg.If("Sqlx") { - s = sakila.NewAuthorizationManageService() + ams = sakila.NewAuthorizationManageService() } else if cfg.If("Sqlc") && (cfg.If("Postgres") || cfg.If("PostgreSQL")) { - s = slonik.NewAuthorizationManageService() + ams = slonik.NewAuthorizationManageService() } else { - s = jinzhu.NewAuthorizationManageService() + ams = jinzhu.NewAuthorizationManageService() } return } + +// lazyInitial do some package lazy initialize for performance +func lazyInitial() { + _onceInitial.Do(func() { + initDsX() + initOSS() + initTsX() + }) +} + +func initDsX() { + var dsVer, dsaVer core.VersionInfo + if cfg.If("Gorm") { + ds, dsVer = jinzhu.NewDataService() + dsa, dsaVer = jinzhu.NewDataServantA() + } else if cfg.If("Sqlx") { + ds, dsVer = sakila.NewDataService() + dsa, dsaVer = sakila.NewDataServantA() + } else if cfg.If("Sqlc") && (cfg.If("Postgres") || cfg.If("PostgreSQL")) { + ds, dsVer = slonik.NewDataService() + dsa, dsaVer = slonik.NewDataServantA() + } else { + // default use gorm as orm for sql database + ds, dsVer = jinzhu.NewDataService() + dsa, dsaVer = jinzhu.NewDataServantA() + } + logrus.Infof("use %s as core.DataService with version %s", dsVer.Name(), dsVer.Version()) + logrus.Infof("use %s as core.ServantA with version %s", dsaVer.Name(), dsaVer.Version()) +} + +func initOSS() { + var v core.VersionInfo + if cfg.If("AliOSS") { + oss, v = storage.MustAliossService() + } else if cfg.If("COS") { + oss, v = storage.NewCosService() + } else if cfg.If("HuaweiOBS") { + oss, v = storage.MustHuaweiobsService() + } else if cfg.If("MinIO") { + oss, v = storage.MustMinioService() + } else if cfg.If("S3") { + oss, v = storage.MustS3Service() + logrus.Infof("use S3 as object storage by version %s", v.Version()) + return + } else if cfg.If("LocalOSS") { + oss, v = storage.MustLocalossService() + } else { + // default use AliOSS as object storage service + oss, v = storage.MustAliossService() + logrus.Infof("use default AliOSS as object storage by version %s", v.Version()) + return + } + logrus.Infof("use %s as object storage by version %s", v.Name(), v.Version()) +} + +func initTsX() { + var v core.VersionInfo + ams := newAuthorizationManageService() + cfg.On(cfg.Actions{ + "Zinc": func() { + ts, v = search.NewZincTweetSearchService(ams) + }, + "Meili": func() { + ts, v = search.NewMeiliTweetSearchService(ams) + }, + }, func() { + ts, v = search.NewZincTweetSearchService(ams) + }) + logrus.Infof("use %s as tweet search serice by version %s", v.Name(), v.Version()) + ts = search.NewBridgeTweetSearchService(ts) +} diff --git a/internal/dao/jinzhu/jinzhu.go b/internal/dao/jinzhu/jinzhu.go index 51fc28db..d2267046 100644 --- a/internal/dao/jinzhu/jinzhu.go +++ b/internal/dao/jinzhu/jinzhu.go @@ -21,13 +21,16 @@ import ( ) var ( - _ core.DataService = (*dataServant)(nil) - _ core.VersionInfo = (*dataServant)(nil) + _ core.DataService = (*dataSrv)(nil) + _ core.VersionInfo = (*dataSrv)(nil) + + _ core.DataServantA = (*dataSrvA)(nil) + _ core.VersionInfo = (*dataSrvA)(nil) _onceInitial sync.Once ) -type dataServant struct { +type dataSrv struct { core.IndexPostsService core.WalletService core.MessageService @@ -43,6 +46,13 @@ type dataServant struct { core.AttachmentCheckService } +type dataSrvA struct { + core.TopicServantA + core.TweetServantA + core.TweetManageServantA + core.TweetHelpServantA +} + func NewDataService() (core.DataService, core.VersionInfo) { lazyInitial() @@ -81,7 +91,7 @@ func NewDataService() (core.DataService, core.VersionInfo) { } logrus.Infof("use %s as cache index service by version: %s", v.Name(), v.Version()) - ds := &dataServant{ + ds := &dataSrv{ IndexPostsService: cis, WalletService: newWalletService(db), MessageService: newMessageService(db), @@ -99,18 +109,40 @@ func NewDataService() (core.DataService, core.VersionInfo) { return ds, ds } +func NewDataServantA() (core.DataServantA, core.VersionInfo) { + lazyInitial() + + db := conf.MustGormDB() + + ds := &dataSrvA{ + TopicServantA: newTopicServantA(db), + TweetServantA: newTweetServantA(db), + TweetManageServantA: newTweetManageServantA(db), + TweetHelpServantA: newTweetHelpServantA(db), + } + return ds, ds +} + func NewAuthorizationManageService() core.AuthorizationManageService { return newAuthorizationManageService(conf.MustGormDB()) } -func (s *dataServant) Name() string { +func (s *dataSrv) Name() string { return "Gorm" } -func (s *dataServant) Version() *semver.Version { +func (s *dataSrv) Version() *semver.Version { return semver.MustParse("v0.2.0") } +func (s *dataSrvA) Name() string { + return "Gorm" +} + +func (s *dataSrvA) Version() *semver.Version { + return semver.MustParse("v0.1.0") +} + // lazyInitial do some package lazy initialize for performance func lazyInitial() { _onceInitial.Do(func() { diff --git a/internal/dao/jinzhu/tweets.go b/internal/dao/jinzhu/tweets.go index 8b9d939f..f716ca9b 100644 --- a/internal/dao/jinzhu/tweets.go +++ b/internal/dao/jinzhu/tweets.go @@ -43,8 +43,7 @@ type tweetSrvA struct { } type tweetManageSrvA struct { - cacheIndex core.CacheIndexService - db *gorm.DB + db *gorm.DB } type tweetHelpSrvA struct { @@ -70,6 +69,24 @@ func newTweetHelpService(db *gorm.DB) core.TweetHelpService { } } +func newTweetServantA(db *gorm.DB) core.TweetServantA { + return &tweetSrvA{ + db: db, + } +} + +func newTweetManageServantA(db *gorm.DB) core.TweetManageServantA { + return &tweetManageSrvA{ + db: db, + } +} + +func newTweetHelpServantA(db *gorm.DB) core.TweetHelpServantA { + return &tweetHelpSrvA{ + db: db, + } +} + // MergePosts post数据整合 func (s *tweetHelpSrv) MergePosts(posts []*core.Post) ([]*core.PostFormated, error) { postIds := make([]int64, 0, len(posts)) diff --git a/internal/dao/sakila/sakila.go b/internal/dao/sakila/sakila.go index 20df522c..9e9f2e3c 100644 --- a/internal/dao/sakila/sakila.go +++ b/internal/dao/sakila/sakila.go @@ -93,6 +93,11 @@ func NewDataService() (core.DataService, core.VersionInfo) { return ds, ds } +func NewDataServantA() (core.DataServantA, core.VersionInfo) { + logrus.Fatal("not support now") + return nil, nil +} + func NewAuthorizationManageService() core.AuthorizationManageService { lazyInitial() return newAuthorizationManageService(_db) diff --git a/internal/dao/slonik/slonik.go b/internal/dao/slonik/slonik.go index bb8f82ad..f217c6d1 100644 --- a/internal/dao/slonik/slonik.go +++ b/internal/dao/slonik/slonik.go @@ -17,6 +17,11 @@ func NewDataService() (core.DataService, core.VersionInfo) { return nil, nil } +func NewDataServantA() (core.DataServantA, core.VersionInfo) { + logrus.Fatal("not support now") + return nil, nil +} + func NewAuthorizationManageService() core.AuthorizationManageService { logrus.Fatal("not support now") return nil diff --git a/internal/internal.go b/internal/internal.go index a8c0934f..038a33a5 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -6,15 +6,9 @@ package internal import ( "github.com/rocboss/paopao-ce/internal/migration" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/internal/servants/web/routers/api" ) func Initialize() { // migrate database if needed migration.Run() - - // initialize service - broker.Initialize() - api.Initialize() } diff --git a/internal/servants/base/base.go b/internal/servants/base/base.go index 2f33b8f6..5fe7afb7 100644 --- a/internal/servants/base/base.go +++ b/internal/servants/base/base.go @@ -14,7 +14,9 @@ import ( "github.com/alimy/mir/v3" "github.com/gin-gonic/gin" "github.com/redis/go-redis/v9" + "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/internal/core" + "github.com/rocboss/paopao-ce/internal/dao" "github.com/rocboss/paopao-ce/pkg/app" "github.com/rocboss/paopao-ce/pkg/types" "github.com/rocboss/paopao-ce/pkg/xerror" @@ -24,6 +26,7 @@ type BaseServant types.Empty type DaoServant struct { Redis *redis.Client + Dsa core.DataServantA Ds core.DataService Ts core.TweetSearchService } @@ -113,6 +116,15 @@ func RenderAny(c *gin.Context, data any, err mir.Error) { } } +func NewDaoServant() *DaoServant { + return &DaoServant{ + Redis: conf.MustRedis(), + Dsa: dao.DataServantA(), + Ds: dao.DataService(), + Ts: dao.TweetSearchService(), + } +} + func (s *DaoServant) GetTweetBy(id int64) (*core.PostFormated, error) { post, err := s.Ds.GetPostByID(id) if err != nil { diff --git a/internal/servants/chain/admin.go b/internal/servants/chain/admin.go index 35a0fbc3..4660baa8 100644 --- a/internal/servants/chain/admin.go +++ b/internal/servants/chain/admin.go @@ -8,7 +8,6 @@ import ( "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/errcode" ) func Admin() gin.HandlerFunc { @@ -23,7 +22,7 @@ func Admin() gin.HandlerFunc { } response := app.NewResponse(c) - response.ToErrorResponse(errcode.NoAdminPermission) + response.ToErrorResponse(_errNoAdminPermission) c.Abort() } } diff --git a/internal/servants/chain/jwt.go b/internal/servants/chain/jwt.go index 62a50e93..efdf2231 100644 --- a/internal/servants/chain/jwt.go +++ b/internal/servants/chain/jwt.go @@ -11,7 +11,7 @@ import ( "github.com/golang-jwt/jwt/v4" "github.com/rocboss/paopao-ce/internal/conf" "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/errcode" + "github.com/rocboss/paopao-ce/pkg/xerror" ) func JWT() gin.HandlerFunc { @@ -19,7 +19,7 @@ func JWT() gin.HandlerFunc { return func(c *gin.Context) { var ( token string - ecode = errcode.Success + ecode = xerror.Success ) if s, exist := c.GetQuery("token"); exist { token = s @@ -29,7 +29,7 @@ func JWT() gin.HandlerFunc { // 验证前端传过来的token格式,不为空,开头为Bearer if token == "" || !strings.HasPrefix(token, "Bearer ") { response := app.NewResponse(c) - response.ToErrorResponse(errcode.UnauthorizedTokenError) + response.ToErrorResponse(xerror.UnauthorizedTokenError) c.Abort() return } @@ -38,15 +38,15 @@ func JWT() gin.HandlerFunc { token = token[7:] } if token == "" { - ecode = errcode.InvalidParams + ecode = xerror.InvalidParams } else { claims, err := app.ParseToken(token) if err != nil { switch err.(*jwt.ValidationError).Errors { case jwt.ValidationErrorExpired: - ecode = errcode.UnauthorizedTokenTimeout + ecode = xerror.UnauthorizedTokenTimeout default: - ecode = errcode.UnauthorizedTokenError + ecode = xerror.UnauthorizedTokenError } } else { c.Set("UID", claims.UID) @@ -57,17 +57,17 @@ func JWT() gin.HandlerFunc { if err == nil { c.Set("USER", user) } else { - ecode = errcode.UnauthorizedAuthNotExist + ecode = xerror.UnauthorizedAuthNotExist } // 强制下线机制 if (conf.JWTSetting.Issuer + ":" + user.Salt) != claims.Issuer { - ecode = errcode.UnauthorizedTokenTimeout + ecode = xerror.UnauthorizedTokenTimeout } } } - if ecode != errcode.Success { + if ecode != xerror.Success { response := app.NewResponse(c) response.ToErrorResponse(ecode) c.Abort() diff --git a/internal/servants/chain/priv.go b/internal/servants/chain/priv.go index 6c09d0a8..c2a017ca 100644 --- a/internal/servants/chain/priv.go +++ b/internal/servants/chain/priv.go @@ -9,7 +9,6 @@ import ( "github.com/gin-gonic/gin" "github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/errcode" ) func Priv() gin.HandlerFunc { @@ -20,7 +19,7 @@ func Priv() gin.HandlerFunc { if user.Status == core.UserStatusNormal { if user.Phone == "" { response := app.NewResponse(c) - response.ToErrorResponse(errcode.AccountNoPhoneBind) + response.ToErrorResponse(_errAccountNoPhoneBind) c.Abort() return } @@ -30,7 +29,7 @@ func Priv() gin.HandlerFunc { } } response := app.NewResponse(c) - response.ToErrorResponse(errcode.UserHasBeenBanned) + response.ToErrorResponse(_errUserHasBeenBanned) c.Abort() } } else { @@ -42,7 +41,7 @@ func Priv() gin.HandlerFunc { } } response := app.NewResponse(c) - response.ToErrorResponse(errcode.UserHasBeenBanned) + response.ToErrorResponse(_errUserHasBeenBanned) c.Abort() } } diff --git a/internal/servants/chain/xerror.go b/internal/servants/chain/xerror.go new file mode 100644 index 00000000..6bebbd98 --- /dev/null +++ b/internal/servants/chain/xerror.go @@ -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 chain + +import ( + "github.com/rocboss/paopao-ce/pkg/xerror" +) + +// nolint +var ( + _errUserHasBeenBanned = xerror.NewError(20006, "该账户已被封停") + _errAccountNoPhoneBind = xerror.NewError(20013, "拒绝操作: 账户未绑定手机号") + _errNoAdminPermission = xerror.NewError(20022, "无管理权限") +) diff --git a/internal/servants/web/broker/attachment.go b/internal/servants/web/broker/attachment.go deleted file mode 100644 index 26845a1a..00000000 --- a/internal/servants/web/broker/attachment.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "github.com/rocboss/paopao-ce/internal/core/cs" -) - -func CreateAttachment(obj *cs.Attachment) (int64, error) { - return ds.CreateAttachment(obj) -} diff --git a/internal/servants/web/broker/avatar.go b/internal/servants/web/broker/avatar.go deleted file mode 100644 index 04e183f4..00000000 --- a/internal/servants/web/broker/avatar.go +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "math/rand" - "time" -) - -var defaultAvatars = []string{ - "https://assets.paopao.info/public/avatar/default/zoe.png", - "https://assets.paopao.info/public/avatar/default/william.png", - "https://assets.paopao.info/public/avatar/default/walter.png", - "https://assets.paopao.info/public/avatar/default/thomas.png", - "https://assets.paopao.info/public/avatar/default/taylor.png", - "https://assets.paopao.info/public/avatar/default/sophia.png", - "https://assets.paopao.info/public/avatar/default/sam.png", - "https://assets.paopao.info/public/avatar/default/ryan.png", - "https://assets.paopao.info/public/avatar/default/ruby.png", - "https://assets.paopao.info/public/avatar/default/quinn.png", - "https://assets.paopao.info/public/avatar/default/paul.png", - "https://assets.paopao.info/public/avatar/default/owen.png", - "https://assets.paopao.info/public/avatar/default/olivia.png", - "https://assets.paopao.info/public/avatar/default/norman.png", - "https://assets.paopao.info/public/avatar/default/nora.png", - "https://assets.paopao.info/public/avatar/default/natalie.png", - "https://assets.paopao.info/public/avatar/default/naomi.png", - "https://assets.paopao.info/public/avatar/default/miley.png", - "https://assets.paopao.info/public/avatar/default/mike.png", - "https://assets.paopao.info/public/avatar/default/lucas.png", - "https://assets.paopao.info/public/avatar/default/kylie.png", - "https://assets.paopao.info/public/avatar/default/julia.png", - "https://assets.paopao.info/public/avatar/default/joshua.png", - "https://assets.paopao.info/public/avatar/default/john.png", - "https://assets.paopao.info/public/avatar/default/jane.png", - "https://assets.paopao.info/public/avatar/default/jackson.png", - "https://assets.paopao.info/public/avatar/default/ivy.png", - "https://assets.paopao.info/public/avatar/default/isaac.png", - "https://assets.paopao.info/public/avatar/default/henry.png", - "https://assets.paopao.info/public/avatar/default/harry.png", - "https://assets.paopao.info/public/avatar/default/harold.png", - "https://assets.paopao.info/public/avatar/default/hanna.png", - "https://assets.paopao.info/public/avatar/default/grace.png", - "https://assets.paopao.info/public/avatar/default/george.png", - "https://assets.paopao.info/public/avatar/default/freddy.png", - "https://assets.paopao.info/public/avatar/default/frank.png", - "https://assets.paopao.info/public/avatar/default/finn.png", - "https://assets.paopao.info/public/avatar/default/emma.png", - "https://assets.paopao.info/public/avatar/default/emily.png", - "https://assets.paopao.info/public/avatar/default/edward.png", - "https://assets.paopao.info/public/avatar/default/clara.png", - "https://assets.paopao.info/public/avatar/default/claire.png", - "https://assets.paopao.info/public/avatar/default/chloe.png", - "https://assets.paopao.info/public/avatar/default/audrey.png", - "https://assets.paopao.info/public/avatar/default/arthur.png", - "https://assets.paopao.info/public/avatar/default/anna.png", - "https://assets.paopao.info/public/avatar/default/andy.png", - "https://assets.paopao.info/public/avatar/default/alfred.png", - "https://assets.paopao.info/public/avatar/default/alexa.png", - "https://assets.paopao.info/public/avatar/default/abigail.png", -} - -func GetRandomAvatar() string { - rand.Seed(time.Now().UnixMicro()) - return defaultAvatars[rand.Intn(len(defaultAvatars))] -} diff --git a/internal/servants/web/broker/broker.go b/internal/servants/web/broker/broker.go deleted file mode 100644 index 08ca7633..00000000 --- a/internal/servants/web/broker/broker.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "github.com/alimy/cfg" - "github.com/redis/go-redis/v9" - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/dao" - "github.com/sirupsen/logrus" -) - -var ( - ds core.DataService - ts core.TweetSearchService - oss core.ObjectStorageService - redisClient *redis.Client - DisablePhoneVerify bool -) - -func Initialize() { - ds = dao.DataService() - ts = dao.TweetSearchService() - oss = dao.ObjectStorageService() - redisClient = conf.MustRedis() - DisablePhoneVerify = !cfg.If("Sms") -} - -// persistMediaContents 获取媒体内容并持久化 -func persistMediaContents(contents []*PostContentItem) (items []string, err error) { - items = make([]string, 0, len(contents)) - for _, item := range contents { - switch item.Type { - case core.ContentTypeImage, - core.ContentTypeVideo, - core.ContentTypeAudio, - core.ContentTypeAttachment, - core.ContentTypeChargeAttachment: - items = append(items, item.Content) - if err != nil { - continue - } - if err = oss.PersistObject(oss.ObjectKey(item.Content)); err != nil { - logrus.Errorf("service.persistMediaContents failed: %s", err) - } - } - } - return -} diff --git a/internal/servants/web/broker/comment.go b/internal/servants/web/broker/comment.go deleted file mode 100644 index 564507c0..00000000 --- a/internal/servants/web/broker/comment.go +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "time" - - "github.com/rocboss/paopao-ce/internal/core" - - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/rocboss/paopao-ce/pkg/utils" -) - -type CommentCreationReq struct { - PostID int64 `json:"post_id" binding:"required"` - Contents []*PostContentItem `json:"contents" binding:"required"` - Users []string `json:"users" binding:"required"` -} -type CommentReplyCreationReq struct { - CommentID int64 `json:"comment_id" binding:"required"` - Content string `json:"content" binding:"required"` - AtUserID int64 `json:"at_user_id"` -} - -type CommentDelReq struct { - ID int64 `json:"id" binding:"required"` -} -type ReplyDelReq struct { - ID int64 `json:"id" binding:"required"` -} - -func GetPostComments(postID int64, sort string, offset, limit int) ([]*core.CommentFormated, int64, error) { - conditions := &core.ConditionsT{ - "post_id": postID, - "ORDER": sort, - } - comments, err := ds.GetComments(conditions, offset, limit) - - if err != nil { - return nil, 0, err - } - - userIDs := []int64{} - commentIDs := []int64{} - for _, comment := range comments { - userIDs = append(userIDs, comment.UserID) - commentIDs = append(commentIDs, comment.ID) - } - - users, err := ds.GetUsersByIDs(userIDs) - if err != nil { - return nil, 0, err - } - - contents, err := ds.GetCommentContentsByIDs(commentIDs) - if err != nil { - return nil, 0, err - } - - replies, err := ds.GetCommentRepliesByID(commentIDs) - if err != nil { - return nil, 0, err - } - - commentsFormated := []*core.CommentFormated{} - for _, comment := range comments { - commentFormated := comment.Format() - for _, content := range contents { - if content.CommentID == comment.ID { - commentFormated.Contents = append(commentFormated.Contents, content) - } - } - for _, reply := range replies { - if reply.CommentID == commentFormated.ID { - commentFormated.Replies = append(commentFormated.Replies, reply) - } - } - for _, user := range users { - if user.ID == comment.UserID { - commentFormated.User = user.Format() - } - } - - commentsFormated = append(commentsFormated, commentFormated) - } - - // 获取总量 - totalRows, _ := ds.GetCommentCount(conditions) - - return commentsFormated, totalRows, nil -} - -func CreatePostComment(ctx *gin.Context, userID int64, param CommentCreationReq) (comment *core.Comment, err error) { - var mediaContents []string - - defer func() { - if err != nil { - deleteOssObjects(mediaContents) - } - }() - - if mediaContents, err = persistMediaContents(param.Contents); err != nil { - return - } - - // 加载Post - post, err := ds.GetPostByID(param.PostID) - - if err != nil { - return nil, err - } - - if post.CommentCount >= conf.AppSetting.MaxCommentCount { - return nil, errcode.MaxCommentCount - } - ip := ctx.ClientIP() - comment = &core.Comment{ - PostID: post.ID, - UserID: userID, - IP: ip, - IPLoc: utils.GetIPLoc(ip), - } - comment, err = ds.CreateComment(comment) - if err != nil { - return nil, err - } - - for _, item := range param.Contents { - // 检查附件是否是本站资源 - if item.Type == core.ContentTypeImage || item.Type == core.ContentTypeVideo || item.Type == core.ContentTypeAttachment { - if err := ds.CheckAttachment(item.Content); err != nil { - continue - } - } - - postContent := &core.CommentContent{ - CommentID: comment.ID, - UserID: userID, - Content: item.Content, - Type: item.Type, - Sort: item.Sort, - } - ds.CreateCommentContent(postContent) - } - - // 更新Post回复数 - post.CommentCount++ - post.LatestRepliedOn = time.Now().Unix() - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - // 创建用户消息提醒 - postMaster, err := ds.GetUserByID(post.UserID) - if err == nil && postMaster.ID != userID { - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: postMaster.ID, - Type: core.MsgtypeComment, - Brief: "在泡泡中评论了你", - PostID: post.ID, - CommentID: comment.ID, - }) - } - for _, u := range param.Users { - user, err := ds.GetUserByUsername(u) - if err != nil || user.ID == userID || user.ID == postMaster.ID { - continue - } - - // 创建消息提醒 - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: user.ID, - Type: core.MsgtypeComment, - Brief: "在泡泡评论中@了你", - PostID: post.ID, - CommentID: comment.ID, - }) - } - - return comment, nil -} - -func GetPostComment(id int64) (*core.Comment, error) { - return ds.GetCommentByID(id) -} - -func DeletePostComment(comment *core.Comment) error { - // 加载post - post, err := ds.GetPostByID(comment.PostID) - if err == nil { - // 更新post回复数 - post.CommentCount-- - ds.UpdatePost(post) - } - - return ds.DeleteComment(comment) -} - -func createPostPreHandler(commentID int64, userID, atUserID int64) (*core.Post, *core.Comment, int64, - error) { - // 加载Comment - comment, err := ds.GetCommentByID(commentID) - if err != nil { - return nil, nil, atUserID, err - } - - // 加载comment的post - post, err := ds.GetPostByID(comment.PostID) - if err != nil { - return nil, nil, atUserID, err - } - - if post.CommentCount >= conf.AppSetting.MaxCommentCount { - return nil, nil, atUserID, errcode.MaxCommentCount - } - - if userID == atUserID { - atUserID = 0 - } - - if atUserID > 0 { - // 检测目前用户是否存在 - users, _ := ds.GetUsersByIDs([]int64{atUserID}) - if len(users) == 0 { - atUserID = 0 - } - } - - return post, comment, atUserID, nil -} - -func CreatePostCommentReply(ctx *gin.Context, commentID int64, content string, userID, atUserID int64) (*core.CommentReply, error) { - var ( - post *core.Post - comment *core.Comment - err error - ) - if post, comment, atUserID, err = createPostPreHandler(commentID, userID, atUserID); err != nil { - return nil, err - } - - // 创建评论 - ip := ctx.ClientIP() - reply := &core.CommentReply{ - CommentID: commentID, - UserID: userID, - Content: content, - AtUserID: atUserID, - IP: ip, - IPLoc: utils.GetIPLoc(ip), - } - - reply, err = ds.CreateCommentReply(reply) - if err != nil { - return nil, err - } - - // 更新Post回复数 - post.CommentCount++ - post.LatestRepliedOn = time.Now().Unix() - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - // 创建用户消息提醒 - commentMaster, err := ds.GetUserByID(comment.UserID) - if err == nil && commentMaster.ID != userID { - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: commentMaster.ID, - Type: core.MsgTypeReply, - Brief: "在泡泡评论下回复了你", - PostID: post.ID, - CommentID: comment.ID, - ReplyID: reply.ID, - }) - } - postMaster, err := ds.GetUserByID(post.UserID) - if err == nil && postMaster.ID != userID && commentMaster.ID != postMaster.ID { - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: postMaster.ID, - Type: core.MsgTypeReply, - Brief: "在泡泡评论下发布了新回复", - PostID: post.ID, - CommentID: comment.ID, - ReplyID: reply.ID, - }) - } - if atUserID > 0 { - user, err := ds.GetUserByID(atUserID) - if err == nil && user.ID != userID && commentMaster.ID != user.ID && postMaster.ID != user.ID { - // 创建消息提醒 - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: user.ID, - Type: core.MsgTypeReply, - Brief: "在泡泡评论的回复中@了你", - PostID: post.ID, - CommentID: comment.ID, - ReplyID: reply.ID, - }) - } - } - - return reply, nil -} - -func GetPostCommentReply(id int64) (*core.CommentReply, error) { - return ds.GetCommentReplyByID(id) -} - -func DeletePostCommentReply(reply *core.CommentReply) error { - err := ds.DeleteCommentReply(reply) - if err != nil { - return err - } - - // 加载Comment - comment, err := ds.GetCommentByID(reply.CommentID) - if err != nil { - return err - } - - // 加载comment的post - post, err := ds.GetPostByID(comment.PostID) - if err != nil { - return err - } - - // 更新Post回复数 - post.CommentCount-- - post.LatestRepliedOn = time.Now().Unix() - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - return nil -} diff --git a/internal/servants/web/broker/message.go b/internal/servants/web/broker/message.go deleted file mode 100644 index 9bb59c8b..00000000 --- a/internal/servants/web/broker/message.go +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "fmt" - "time" - - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" -) - -// MAX_WHISPER_NUM_DAILY 当日单用户私信总数限制(TODO 配置化、积分兑换等) -const MAX_WHISPER_NUM_DAILY = 20 - -type ReadMessageReq struct { - ID int64 `json:"id" binding:"required"` -} -type WhisperReq struct { - UserID int64 `json:"user_id" binding:"required"` - Content string `json:"content" binding:"required"` -} - -// CreateWhisper 创建私信 -func CreateWhisper(c *gin.Context, msg *core.Message) (*core.Message, error) { - whisperKey := fmt.Sprintf("WhisperTimes:%d", msg.SenderUserID) - - // 今日频次限制 - if res, _ := redisClient.Get(c, whisperKey).Result(); convert.StrTo(res).MustInt() >= MAX_WHISPER_NUM_DAILY { - return nil, errcode.TooManyWhisperNum - } - - // 创建私信 - msg, err := ds.CreateMessage(msg) - if err != nil { - return nil, err - } - - // 写入当日(自然日)计数缓存 - redisClient.Incr(c, whisperKey).Result() - - currentTime := time.Now() - endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location()) - redisClient.Expire(c, whisperKey, endTime.Sub(currentTime)) - - return msg, err -} - -func GetUnreadCount(userID int64) (int64, error) { - return ds.GetUnreadCount(userID) -} -func ReadMessage(id, userID int64) error { - // 获取message - message, err := ds.GetMessageByID(id) - if err != nil { - return err - } - - if message.ReceiverUserID != userID { - return errcode.NoPermission - } - - // 已读消息 - return ds.ReadMessage(message) -} - -func GetMessages(userID int64, offset, limit int) ([]*core.MessageFormated, int64, error) { - conditions := &core.ConditionsT{ - "receiver_user_id": userID, - "ORDER": "id DESC", - } - messages, err := ds.GetMessages(conditions, offset, limit) - - for _, mf := range messages { - - if mf.SenderUserID > 0 { - user, err := ds.GetUserByID(mf.SenderUserID) - if err == nil { - mf.SenderUser = user.Format() - } - - } - - // 好友申请消息不需要获取其他信息 - if mf.Type == core.MsgTypeRequestingFriend { - continue - } - - if mf.PostID > 0 { - post, err := GetPost(mf.PostID) - if err == nil { - mf.Post = post - if mf.CommentID > 0 { - comment, err := GetPostComment(mf.CommentID) - - if err == nil { - mf.Comment = comment - - if mf.ReplyID > 0 { - reply, err := GetPostCommentReply(mf.ReplyID) - if err == nil { - mf.Reply = reply - } - } - } - } - } - } - } - - if err != nil { - return nil, 0, err - } - - // 获取总量 - totalRows, _ := ds.GetMessageCount(conditions) - - return messages, totalRows, nil -} diff --git a/internal/servants/web/broker/post.go b/internal/servants/web/broker/post.go deleted file mode 100644 index 10292d99..00000000 --- a/internal/servants/web/broker/post.go +++ /dev/null @@ -1,549 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "errors" - "fmt" - "math" - "strings" - "time" - - "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/pkg/errcode" - "github.com/rocboss/paopao-ce/pkg/utils" - "github.com/sirupsen/logrus" -) - -type TagType = cs.TagType - -type PostListReq struct { - Conditions *core.ConditionsT - Offset int - Limit int -} -type PostTagsReq struct { - Type TagType `json:"type" form:"type" binding:"required"` - Num int `json:"num" form:"num" binding:"required"` -} - -type PostCreationReq struct { - Contents []*PostContentItem `json:"contents" binding:"required"` - Tags []string `json:"tags" binding:"required"` - Users []string `json:"users" binding:"required"` - AttachmentPrice int64 `json:"attachment_price"` - Visibility core.PostVisibleT `json:"visibility"` -} - -type PostDelReq struct { - ID int64 `json:"id" binding:"required"` -} - -type PostLockReq struct { - ID int64 `json:"id" binding:"required"` -} - -type PostStickReq struct { - ID int64 `json:"id" binding:"required"` -} - -type PostVisibilityReq struct { - ID int64 `json:"id" binding:"required"` - Visibility core.PostVisibleT `json:"visibility"` -} - -type PostStarReq struct { - ID int64 `json:"id" binding:"required"` -} - -type PostCollectionReq struct { - ID int64 `json:"id" binding:"required"` -} - -type PostContentItem struct { - Content string `json:"content" binding:"required"` - Type core.PostContentT `json:"type" binding:"required"` - Sort int64 `json:"sort" binding:"required"` -} - -// Check 检查PostContentItem属性 -func (p *PostContentItem) Check() error { - // 检查附件是否是本站资源 - if p.Type == core.ContentTypeImage || p.Type == core.ContentTypeVideo || p.Type == core.ContentTypeAttachment { - if err := ds.CheckAttachment(p.Content); err != nil { - return err - } - } - // 检查链接是否合法 - if p.Type == core.ContentTypeLink { - if strings.Index(p.Content, "http://") != 0 && strings.Index(p.Content, "https://") != 0 { - return fmt.Errorf("链接不合法") - } - } - return nil -} - -func tagsFrom(originTags []string) []string { - tags := make([]string, 0, len(originTags)) - for _, tag := range originTags { - // TODO: 优化tag有效性检测 - if tag = strings.TrimSpace(tag); len(tag) > 0 { - tags = append(tags, tag) - } - } - return tags -} - -// CreatePost 创建文章 -// TODO: 推文+推文内容需要在一个事务中添加,后续优化 -func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (_ *core.PostFormated, err error) { - var mediaContents []string - - defer func() { - if err != nil { - deleteOssObjects(mediaContents) - } - }() - - if mediaContents, err = persistMediaContents(param.Contents); err != nil { - return - } - - ip := c.ClientIP() - tags := tagsFrom(param.Tags) - post := &core.Post{ - UserID: userID, - Tags: strings.Join(tags, ","), - IP: ip, - IPLoc: utils.GetIPLoc(ip), - AttachmentPrice: param.AttachmentPrice, - Visibility: param.Visibility, - } - post, err = ds.CreatePost(post) - if err != nil { - return nil, err - } - - for _, item := range param.Contents { - if err := item.Check(); err != nil { - // 属性非法 - logrus.Infof("contents check err: %v", err) - continue - } - - if item.Type == core.ContentTypeAttachment && param.AttachmentPrice > 0 { - item.Type = core.ContentTypeChargeAttachment - } - - postContent := &core.PostContent{ - PostID: post.ID, - UserID: userID, - Content: item.Content, - Type: item.Type, - Sort: item.Sort, - } - if _, err = ds.CreatePostContent(postContent); err != nil { - return nil, err - } - } - - // 私密推文不创建标签与用户提醒 - if post.Visibility != core.PostVisitPrivate { - // 创建标签 - ds.UpsertTags(userID, tags) - - // 创建用户消息提醒 - for _, u := range param.Users { - user, err := ds.GetUserByUsername(u) - if err != nil || user.ID == userID { - continue - } - - // 创建消息提醒 - // TODO: 优化消息提醒处理机制 - go ds.CreateMessage(&core.Message{ - SenderUserID: userID, - ReceiverUserID: user.ID, - Type: core.MsgTypePost, - Brief: "在新发布的泡泡动态中@了你", - PostID: post.ID, - }) - } - } - - // 推送Search - PushPostToSearch(post) - - formatedPosts, err := ds.RevampPosts([]*core.PostFormated{post.Format()}) - if err != nil { - return nil, err - } - return formatedPosts[0], nil -} - -func DeletePost(user *core.User, id int64) *errcode.Error { - if user == nil { - return errcode.NoPermission - } - - post, err := ds.GetPostByID(id) - if err != nil { - return errcode.GetPostFailed - } - if post.UserID != user.ID && !user.IsAdmin { - return errcode.NoPermission - } - - mediaContents, err := ds.DeletePost(post) - if err != nil { - logrus.Errorf("service.DeletePost delete post failed: %s", err) - return errcode.DeletePostFailed - } - - // 删除推文的媒体内容 - deleteOssObjects(mediaContents) - - // 删除索引 - DeleteSearchPost(post) - - return nil -} - -// deleteOssObjects 删除推文的媒体内容, 宽松处理错误(就是不处理), 后续完善 -func deleteOssObjects(mediaContents []string) { - mediaContentsSize := len(mediaContents) - if mediaContentsSize > 1 { - objectKeys := make([]string, 0, mediaContentsSize) - for _, cUrl := range mediaContents { - objectKeys = append(objectKeys, oss.ObjectKey(cUrl)) - } - // TODO: 优化处理尽量使用channel传递objectKeys使用可控数量的Goroutine集中处理object删除动作,后续完善 - go oss.DeleteObjects(objectKeys) - } else if mediaContentsSize == 1 { - oss.DeleteObject(oss.ObjectKey(mediaContents[0])) - } -} - -func LockPost(id int64) error { - post, _ := ds.GetPostByID(id) - - err := ds.LockPost(post) - - if err != nil { - return err - } - - return nil -} - -func StickPost(id int64) error { - post, _ := ds.GetPostByID(id) - - err := ds.StickPost(post) - - if err != nil { - return err - } - - return nil -} - -func VisiblePost(user *core.User, postId int64, visibility core.PostVisibleT) *errcode.Error { - if visibility >= core.PostVisitInvalid { - return errcode.InvalidParams - } - - post, err := ds.GetPostByID(postId) - if err != nil { - return errcode.GetPostFailed - } - - if err := checkPermision(user, post.UserID); err != nil { - return err - } - - if err = ds.VisiblePost(post, visibility); err != nil { - logrus.Warnf("update post failure: %v", err) - return errcode.VisblePostFailed - } - - // 推送Search - post.Visibility = visibility - PushPostToSearch(post) - - return nil -} - -func GetPostStar(postID, userID int64) (*core.PostStar, error) { - return ds.GetUserPostStar(postID, userID) -} - -func CreatePostStar(postID, userID int64) (*core.PostStar, error) { - // 加载Post - post, err := ds.GetPostByID(postID) - if err != nil { - return nil, err - } - - // 私密post不可操作 - if post.Visibility == core.PostVisitPrivate { - return nil, errors.New("no permision") - } - - star, err := ds.CreatePostStar(postID, userID) - if err != nil { - return nil, err - } - - // 更新Post点赞数 - post.UpvoteCount++ - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - return star, nil -} - -func DeletePostStar(star *core.PostStar) error { - err := ds.DeletePostStar(star) - if err != nil { - return err - } - // 加载Post - post, err := ds.GetPostByID(star.PostID) - if err != nil { - return err - } - - // 私密post不可操作 - if post.Visibility == core.PostVisitPrivate { - return errors.New("no permision") - } - - // 更新Post点赞数 - post.UpvoteCount-- - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - return nil -} - -func GetPostCollection(postID, userID int64) (*core.PostCollection, error) { - return ds.GetUserPostCollection(postID, userID) -} - -func CreatePostCollection(postID, userID int64) (*core.PostCollection, error) { - // 加载Post - post, err := ds.GetPostByID(postID) - if err != nil { - return nil, err - } - - // 私密post不可操作 - if post.Visibility == core.PostVisitPrivate { - return nil, errors.New("no permision") - } - - collection, err := ds.CreatePostCollection(postID, userID) - if err != nil { - return nil, err - } - - // 更新Post点赞数 - post.CollectionCount++ - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - return collection, nil -} - -func DeletePostCollection(collection *core.PostCollection) error { - err := ds.DeletePostCollection(collection) - if err != nil { - return err - } - // 加载Post - post, err := ds.GetPostByID(collection.PostID) - if err != nil { - return err - } - - // 私密post不可操作 - if post.Visibility == core.PostVisitPrivate { - return errors.New("no permision") - } - - // 更新Post点赞数 - post.CollectionCount-- - ds.UpdatePost(post) - - // 更新索引 - PushPostToSearch(post) - - return nil -} - -func GetPost(id int64) (*core.PostFormated, error) { - post, err := ds.GetPostByID(id) - - if err != nil { - return nil, err - } - - postContents, err := ds.GetPostContentsByIDs([]int64{post.ID}) - if err != nil { - return nil, err - } - - users, err := ds.GetUsersByIDs([]int64{post.UserID}) - if err != nil { - return nil, err - } - - // 数据整合 - postFormated := post.Format() - for _, user := range users { - postFormated.User = user.Format() - } - for _, content := range postContents { - if content.PostID == post.ID { - postFormated.Contents = append(postFormated.Contents, content.Format()) - } - } - return postFormated, nil -} - -func GetPostContentByID(id int64) (*core.PostContent, error) { - return ds.GetPostContentByID(id) -} - -func GetIndexPosts(user *core.User, offset int, limit int) (*core.IndexTweetList, error) { - return ds.IndexPosts(user, offset, limit) -} - -func GetPostList(req *PostListReq) ([]*core.Post, []*core.PostFormated, error) { - posts, err := ds.GetPosts(req.Conditions, req.Offset, req.Limit) - if err != nil { - return nil, nil, err - } - postFormated, err := ds.MergePosts(posts) - return posts, postFormated, err -} - -func GetPostCount(conditions *core.ConditionsT) (int64, error) { - return ds.GetPostCount(conditions) -} - -func GetPostListFromSearch(user *core.User, q *core.QueryReq, offset, limit int) ([]*core.PostFormated, int64, error) { - resp, err := ts.Search(user, q, offset, limit) - if err != nil { - return nil, 0, err - } - posts, err := ds.RevampPosts(resp.Items) - if err != nil { - return nil, 0, err - } - return posts, resp.Total, nil -} - -func GetPostListFromSearchByQuery(user *core.User, query string, offset, limit int) ([]*core.PostFormated, int64, error) { - q := &core.QueryReq{ - Query: query, - Type: "search", - } - return GetPostListFromSearch(user, q, offset, limit) -} - -func PushPostToSearch(post *core.Post) { - postFormated := post.Format() - postFormated.User = &core.UserFormated{ - ID: post.UserID, - } - contents, _ := ds.GetPostContentsByIDs([]int64{post.ID}) - for _, content := range contents { - postFormated.Contents = append(postFormated.Contents, content.Format()) - } - - contentFormated := "" - for _, content := range postFormated.Contents { - if content.Type == core.ContentTypeText || content.Type == core.ContentTypeTitle { - contentFormated = contentFormated + content.Content + "\n" - } - } - - docs := []core.TsDocItem{{ - Post: post, - Content: contentFormated, - }} - ts.AddDocuments(docs, fmt.Sprintf("%d", post.ID)) -} - -func DeleteSearchPost(post *core.Post) error { - return ts.DeleteDocuments([]string{fmt.Sprintf("%d", post.ID)}) -} - -func PushPostsToSearch(c *gin.Context) { - if ok, _ := redisClient.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok { - defer redisClient.Del(c, "JOB_PUSH_TO_SEARCH") - - splitNum := 1000 - totalRows, _ := GetPostCount(&core.ConditionsT{ - "visibility IN ?": []core.PostVisibleT{core.PostVisitPublic, core.PostVisitFriend}, - }) - - pages := math.Ceil(float64(totalRows) / float64(splitNum)) - nums := int(pages) - - for i := 0; i < nums; i++ { - posts, postsFormated, err := GetPostList(&PostListReq{ - Conditions: &core.ConditionsT{}, - Offset: i * splitNum, - Limit: splitNum, - }) - if err != nil || len(posts) != len(postsFormated) { - continue - } - for i, pf := range postsFormated { - contentFormated := "" - for _, content := range pf.Contents { - if content.Type == core.ContentTypeText || content.Type == core.ContentTypeTitle { - contentFormated = contentFormated + content.Content + "\n" - } - } - docs := []core.TsDocItem{{ - Post: posts[i], - Content: contentFormated, - }} - ts.AddDocuments(docs, fmt.Sprintf("%d", posts[i].ID)) - } - } - } -} - -func GetPostTags(param *PostTagsReq) (cs.TagList, error) { - num := param.Num - if num > conf.AppSetting.MaxPageSize { - num = conf.AppSetting.MaxPageSize - } - tags, err := ds.ListTags(param.Type, num, 0) - if err != nil { - return nil, err - } - return tags, nil -} - -func CheckPostAttachmentIsPaid(postID, userID int64) bool { - bill, err := ds.GetPostAttatchmentBill(postID, userID) - - return err == nil && bill.Model != nil && bill.ID > 0 -} diff --git a/internal/servants/web/broker/sign.go b/internal/servants/web/broker/sign.go deleted file mode 100644 index 313cc285..00000000 --- a/internal/servants/web/broker/sign.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "fmt" - "sort" - - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/pkg/utils" - "github.com/sirupsen/logrus" -) - -func GetParamSign(param map[string]any, secretKey string) string { - signRaw := "" - - rawStrs := []string{} - for k, v := range param { - if k != "sign" { - rawStrs = append(rawStrs, k+"="+fmt.Sprintf("%v", v)) - } - } - - sort.Strings(rawStrs) - for _, v := range rawStrs { - signRaw += v - } - - if conf.ServerSetting.RunMode == "debug" { - logrus.Info(map[string]string{ - "signRaw": signRaw, - "sysSign": utils.EncodeMD5(signRaw + secretKey), - }) - } - - return utils.EncodeMD5(signRaw + secretKey) -} diff --git a/internal/servants/web/broker/user.go b/internal/servants/web/broker/user.go deleted file mode 100644 index e1abb775..00000000 --- a/internal/servants/web/broker/user.go +++ /dev/null @@ -1,487 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "fmt" - "regexp" - "strings" - "time" - "unicode/utf8" - - "github.com/gin-gonic/gin" - "github.com/gofrs/uuid" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/rocboss/paopao-ce/pkg/utils" - "github.com/sirupsen/logrus" -) - -const _MaxCaptchaTimes = 2 - -type PhoneCaptchaReq struct { - Phone string `json:"phone" form:"phone" binding:"required"` - ImgCaptcha string `json:"img_captcha" form:"img_captcha" binding:"required"` - ImgCaptchaID string `json:"img_captcha_id" form:"img_captcha_id" binding:"required"` -} - -type UserPhoneBindReq struct { - Phone string `json:"phone" form:"phone" binding:"required"` - Captcha string `json:"captcha" form:"captcha" binding:"required"` -} - -type AuthRequest struct { - Username string `json:"username" form:"username" binding:"required"` - Password string `json:"password" form:"password" binding:"required"` -} - -type RegisterRequest struct { - Username string `json:"username" form:"username" binding:"required"` - Password string `json:"password" form:"password" binding:"required"` -} - -type ChangePasswordReq struct { - Password string `json:"password" form:"password" binding:"required"` - OldPassword string `json:"old_password" form:"old_password" binding:"required"` -} - -type ChangeNicknameReq struct { - Nickname string `json:"nickname" form:"nickname" binding:"required"` -} - -type ChangeAvatarReq struct { - Avatar string `json:"avatar" form:"avatar" binding:"required"` -} - -type ChangeUserStatusReq struct { - ID int64 `json:"id" form:"id" binding:"required"` - Status int `json:"status" form:"status" binding:"required"` -} - -type RequestingFriendReq struct { - UserId int64 `json:"user_id" binding:"required"` - Greetings string `json:"greetings" binding:"required"` -} - -type AddFriendReq struct { - UserId int64 `json:"user_id" binding:"required"` -} - -type RejectFriendReq struct { - UserId int64 `json:"user_id" binding:"required"` -} - -type DeleteFriendReq struct { - UserId int64 `json:"user_id"` -} - -type UserProfileResp 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"` -} - -const ( - _LoginErrKey = "PaoPaoUserLoginErr" - _MaxLoginErrTimes = 10 -) - -// DoLogin 用户认证 -func DoLogin(ctx *gin.Context, param *AuthRequest) (*core.User, error) { - user, err := ds.GetUserByUsername(param.Username) - if err != nil { - return nil, errcode.UnauthorizedAuthNotExist - } - - if user.Model != nil && user.ID > 0 { - if errTimes, err := redisClient.Get(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID)).Result(); err == nil { - if convert.StrTo(errTimes).MustInt() >= _MaxLoginErrTimes { - return nil, errcode.TooManyLoginError - } - } - - // 对比密码是否正确 - if ValidPassword(user.Password, param.Password, user.Salt) { - - if user.Status == core.UserStatusClosed { - return nil, errcode.UserHasBeenBanned - } - - // 清空登录计数 - redisClient.Del(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID)) - return user, nil - } - - // 登录错误计数 - _, err = redisClient.Incr(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID)).Result() - if err == nil { - redisClient.Expire(ctx, fmt.Sprintf("%s:%d", _LoginErrKey, user.ID), time.Hour).Result() - } - - return nil, errcode.UnauthorizedAuthFailed - } - - return nil, errcode.UnauthorizedAuthNotExist -} - -// ValidPassword 检查密码是否一致 -func ValidPassword(dbPassword, password, salt string) bool { - return strings.Compare(dbPassword, utils.EncodeMD5(utils.EncodeMD5(password)+salt)) == 0 -} - -// CheckStatus 检测用户权限 -func CheckStatus(user *core.User) bool { - return user.Status == core.UserStatusNormal -} - -// ValidUsername 验证用户 -func ValidUsername(username string) error { - // 检测用户是否合规 - if utf8.RuneCountInString(username) < 3 || utf8.RuneCountInString(username) > 12 { - return errcode.UsernameLengthLimit - } - - if !regexp.MustCompile(`^[a-zA-Z0-9]+$`).MatchString(username) { - return errcode.UsernameCharLimit - } - - // 重复检查 - user, _ := ds.GetUserByUsername(username) - - if user.Model != nil && user.ID > 0 { - return errcode.UsernameHasExisted - } - - return nil -} - -// CheckPassword 密码检查 -func CheckPassword(password string) error { - // 检测用户是否合规 - if utf8.RuneCountInString(password) < 6 || utf8.RuneCountInString(password) > 16 { - return errcode.PasswordLengthLimit - } - - return nil -} - -// CheckPhoneCaptcha 验证手机验证码 -func CheckPhoneCaptcha(phone, captcha string) *errcode.Error { - // 如果禁止phone verify 则允许通过任意验证码 - if DisablePhoneVerify { - return nil - } - - c, err := ds.GetLatestPhoneCaptcha(phone) - if err != nil { - return errcode.ErrorPhoneCaptcha - } - - if c.Captcha != captcha { - return errcode.ErrorPhoneCaptcha - } - - if c.ExpiredOn < time.Now().Unix() { - return errcode.ErrorPhoneCaptcha - } - - if c.UseTimes >= _MaxCaptchaTimes { - return errcode.MaxPhoneCaptchaUseTimes - } - - // 更新检测次数 - ds.UsePhoneCaptcha(c) - - return nil -} - -// CheckPhoneExist 检测手机号是否存在 -func CheckPhoneExist(uid int64, phone string) bool { - u, err := ds.GetUserByPhone(phone) - if err != nil { - return false - } - - if u.Model == nil || u.ID == 0 { - return false - } - - if u.ID == uid { - return false - } - - return true -} - -// EncryptPasswordAndSalt 密码加密&生成salt -func EncryptPasswordAndSalt(password string) (string, string) { - salt := uuid.Must(uuid.NewV4()).String()[:8] - password = utils.EncodeMD5(utils.EncodeMD5(password) + salt) - - return password, salt -} - -// Register 用户注册 -func Register(username, password string) (*core.User, error) { - password, salt := EncryptPasswordAndSalt(password) - - user := &core.User{ - Nickname: username, - Username: username, - Password: password, - Avatar: GetRandomAvatar(), - Salt: salt, - Status: core.UserStatusNormal, - } - - user, err := ds.CreateUser(user) - if err != nil { - return nil, err - } - - return user, nil -} - -func RequestingFriend(user *core.User, param *RequestingFriendReq) error { - if _, err := ds.GetUserByID(param.UserId); err != nil { - return errcode.NotExistFriendId - } - return ds.RequestingFriend(user.ID, param.UserId, param.Greetings) -} - -func AddFriend(user *core.User, param *AddFriendReq) error { - if _, err := ds.GetUserByID(param.UserId); err != nil { - return errcode.NotExistFriendId - } - return ds.AddFriend(user.ID, param.UserId) -} - -func RejectFriend(user *core.User, param *RejectFriendReq) error { - if _, err := ds.GetUserByID(param.UserId); err != nil { - return errcode.NotExistFriendId - } - return ds.RejectFriend(user.ID, param.UserId) -} - -func DeleteFriend(user *core.User, param *DeleteFriendReq) error { - if _, err := ds.GetUserByID(param.UserId); err != nil { - return errcode.NotExistFriendId - } - return ds.DeleteFriend(user.ID, param.UserId) -} - -func GetContacts(user *core.User, offset int, limit int) (*core.ContactList, error) { - return ds.GetContacts(user.ID, offset, limit) -} - -// GetUserInfo 获取用户信息 -func GetUserInfo(param *AuthRequest) (*core.User, error) { - user, err := ds.GetUserByUsername(param.Username) - - if err != nil { - return nil, err - } - - if user.Model != nil && user.ID > 0 { - return user, nil - } - - return nil, errcode.UnauthorizedAuthNotExist -} - -func GetUserByID(id int64) (*core.User, error) { - user, err := ds.GetUserByID(id) - - if err != nil { - return nil, err - } - - if user.Model != nil && user.ID > 0 { - return user, nil - } - - return nil, errcode.NoExistUsername -} - -func GetUserByUsername(user *core.User, username string) (*UserProfileResp, error) { - other, err := ds.GetUserByUsername(username) - if err != nil { - return nil, err - } - - var resp *UserProfileResp - if other.Model != nil && other.ID > 0 { - resp = &UserProfileResp{ - ID: other.ID, - Nickname: other.Nickname, - Username: other.Username, - Status: other.Status, - Avatar: other.Avatar, - IsAdmin: other.IsAdmin, - IsFriend: !(user == nil || user.ID == other.ID), - } - } else { - return nil, errcode.NoExistUsername - } - - if user != nil && user.ID != other.ID { - resp.IsFriend = ds.IsFriend(user.ID, other.ID) - } - return resp, nil -} - -// UpdateUserInfo 更新用户信息 -func UpdateUserInfo(user *core.User) *errcode.Error { - if err := ds.UpdateUser(user); err != nil { - return errcode.ServerError - } - return nil -} - -func ChangeUserAvatar(user *core.User, avatar string) (err *errcode.Error) { - defer func() { - if err != nil { - deleteOssObjects([]string{avatar}) - } - }() - - if err := ds.CheckAttachment(avatar); err != nil { - return errcode.InvalidParams - } - - if err := oss.PersistObject(oss.ObjectKey(avatar)); err != nil { - logrus.Errorf("service.ChangeUserAvatar persist object failed: %s", err) - return errcode.ServerError - } - - user.Avatar = avatar - err = UpdateUserInfo(user) - return -} - -// GetUserCollections 获取用户收藏列表 -func GetUserCollections(userID int64, offset, limit int) ([]*core.PostFormated, int64, error) { - collections, err := ds.GetUserPostCollections(userID, offset, limit) - if err != nil { - return nil, 0, err - } - totalRows, err := ds.GetUserPostCollectionCount(userID) - if err != nil { - return nil, 0, err - } - var posts []*core.Post - for _, collection := range collections { - posts = append(posts, collection.Post) - } - postsFormated, err := ds.MergePosts(posts) - if err != nil { - return nil, 0, err - } - - return postsFormated, totalRows, nil -} - -// GetUserStars 获取用户点赞列表 -func GetUserStars(userID int64, offset, limit int) ([]*core.PostFormated, int64, error) { - stars, err := ds.GetUserPostStars(userID, offset, limit) - if err != nil { - return nil, 0, err - } - totalRows, err := ds.GetUserPostStarCount(userID) - if err != nil { - return nil, 0, err - } - - var posts []*core.Post - for _, star := range stars { - posts = append(posts, star.Post) - } - postsFormated, err := ds.MergePosts(posts) - if err != nil { - return nil, 0, err - } - - return postsFormated, totalRows, nil -} - -// GetUserWalletBills 获取用户账单列表 -func GetUserWalletBills(userID int64, offset, limit int) ([]*core.WalletStatement, int64, error) { - bills, err := ds.GetUserWalletBills(userID, offset, limit) - if err != nil { - return nil, 0, err - } - totalRows, err := ds.GetUserWalletBillCount(userID) - if err != nil { - return nil, 0, err - } - - return bills, totalRows, nil -} - -// SendPhoneCaptcha 发送短信验证码 -func SendPhoneCaptcha(ctx *gin.Context, phone string) error { - - err := ds.SendPhoneCaptcha(phone) - if err != nil { - return err - } - - // 写入计数缓存 - redisClient.Incr(ctx, "PaoPaoSmsCaptcha:"+phone).Result() - - currentTime := time.Now() - endTime := time.Date(currentTime.Year(), currentTime.Month(), currentTime.Day(), 23, 59, 59, 0, currentTime.Location()) - - redisClient.Expire(ctx, "PaoPaoSmsCaptcha:"+phone, endTime.Sub(currentTime)) - - return nil -} - -// GetSuggestUsers 根据关键词获取用户推荐 -func GetSuggestUsers(keyword string) ([]string, error) { - users, err := ds.GetUsersByKeyword(keyword) - if err != nil { - return nil, err - } - - usernames := []string{} - for _, user := range users { - usernames = append(usernames, user.Username) - } - - return usernames, nil -} - -// GetSuggestTags 根据关键词获取标签推荐 -func GetSuggestTags(keyword string) ([]string, error) { - tags, err := ds.TagsByKeyword(keyword) - if err != nil { - return nil, err - } - - ts := []string{} - for _, t := range tags { - ts = append(ts, t.Tag) - } - - return ts, nil -} - -func IsFriend(userId, friendId int64) bool { - return ds.IsFriend(userId, friendId) -} - -// checkPermision 检查是否拥有者或管理员 -func checkPermision(user *core.User, targetUserId int64) *errcode.Error { - if user == nil || (user.ID != targetUserId && !user.IsAdmin) { - return errcode.NoPermission - } - return nil -} diff --git a/internal/servants/web/broker/wallet.go b/internal/servants/web/broker/wallet.go deleted file mode 100644 index fc4392a2..00000000 --- a/internal/servants/web/broker/wallet.go +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2022 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 broker - -import ( - "time" - - "github.com/rocboss/paopao-ce/internal/core" - - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/pkg/errcode" -) - -type RechargeReq struct { - Amount int64 `json:"amount" form:"amount" binding:"required"` -} - -func GetRechargeByID(id int64) (*core.WalletRecharge, error) { - return ds.GetRechargeByID(id) -} - -func CreateRecharge(userID, amount int64) (*core.WalletRecharge, error) { - return ds.CreateRecharge(userID, amount) -} - -func FinishRecharge(ctx *gin.Context, id int64, tradeNo string) error { - if ok, _ := redisClient.SetNX(ctx, "PaoPaoRecharge:"+tradeNo, 1, time.Second*5).Result(); ok { - recharge, err := ds.GetRechargeByID(id) - if err != nil { - return err - } - - if recharge.TradeStatus != "TRADE_SUCCESS" { - - // 标记为已付款 - err := ds.HandleRechargeSuccess(recharge, tradeNo) - defer redisClient.Del(ctx, "PaoPaoRecharge:"+tradeNo) - - if err != nil { - return err - } - } - - } - - return nil -} - -func BuyPostAttachment(post *core.Post, user *core.User) error { - if user.Balance < post.AttachmentPrice { - return errcode.InsuffientDownloadMoney - } - - // 执行购买 - return ds.HandlePostAttachmentBought(post, user) -} diff --git a/internal/servants/web/routers/api/api.go b/internal/servants/web/routers/api/api.go deleted file mode 100644 index 1578728e..00000000 --- a/internal/servants/web/routers/api/api.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2022 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 api - -import ( - "github.com/alimy/cfg" - "github.com/redis/go-redis/v9" - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/dao" - "github.com/sirupsen/logrus" - "github.com/smartwalle/alipay/v3" -) - -var ( - redisClient *redis.Client - alipayClient *alipay.Client - objectStorage core.ObjectStorageService -) - -func Initialize() { - redisClient = conf.MustRedis() - objectStorage = dao.ObjectStorageService() - - if cfg.If("Alipay") { - alipayClient = mustAlipayClient() - } -} - -func mustAlipayClient() *alipay.Client { - s := conf.AlipaySetting - // 将 key 的验证调整到初始化阶段 - client, err := alipay.New(s.AppID, s.PrivateKey, s.InProduction) - if err != nil { - logrus.Fatalf("alipay.New err: %s", err) - } - // 加载应用公钥证书 - if err = client.LoadAppPublicCertFromFile(s.AppPublicCertFile); err != nil { - logrus.Fatalf("client.LoadAppPublicCertFromFile err: %s\n", err) - } - // 加载支付宝根证书 - if err = client.LoadAliPayRootCertFromFile(s.RootCertFile); err != nil { - logrus.Fatalf("client.LoadAliPayRootCertFromFile err: %s\n", err) - } - // 加载支付宝公钥证书 - if err = client.LoadAliPayPublicCertFromFile(s.PublicCertFile); err != nil { - logrus.Fatalf("client.LoadAliPayPublicCertFromFile err: %s\n", err) - } - return client -} diff --git a/internal/servants/web/routers/api/attachment.go b/internal/servants/web/routers/api/attachment.go deleted file mode 100644 index 4e5636ea..00000000 --- a/internal/servants/web/routers/api/attachment.go +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2022 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 api - -import ( - "image" - - "github.com/disintegration/imaging" - "github.com/gin-gonic/gin" - "github.com/gofrs/uuid" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/core/cs" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/sirupsen/logrus" -) - -var uploadAttachmentTypeMap = map[string]cs.AttachmentType{ - "public/image": cs.AttachmentTypeImage, - "public/avatar": cs.AttachmentTypeImage, - "public/video": cs.AttachmentTypeVideo, - "attachment": cs.AttachmentTypeOther, -} - -func GeneratePath(s string) string { - n := len(s) - if n <= 2 { - return s - } - - return GeneratePath(s[:n-2]) + "/" + s[n-2:] -} - -func GetFileExt(s string) (string, error) { - switch s { - case "image/png": - return ".png", nil - case "image/jpg": - return ".jpg", nil - case "image/jpeg": - return ".jpeg", nil - case "image/gif": - return ".gif", nil - case "video/mp4": - return ".mp4", nil - case "video/quicktime": - return ".mov", nil - case "application/zip": - return ".zip", nil - default: - return "", errcode.FileInvalidExt.WithDetails("仅允许 png/jpg/gif/mp4/mov/zip 类型") - } -} -func GetImageSize(img image.Rectangle) (int, int) { - b := img.Bounds() - width := b.Max.X - height := b.Max.Y - return width, height -} - -func fileCheck(uploadType string, size int64) error { - if uploadType != "public/video" && - uploadType != "public/image" && - uploadType != "public/avatar" && - uploadType != "attachment" { - return errcode.InvalidParams - } - - if size > 1024*1024*100 { - return errcode.FileInvalidSize.WithDetails("最大允许100MB") - } - - return nil -} - -func UploadAttachment(c *gin.Context) { - response := app.NewResponse(c) - - uploadType := c.Request.FormValue("type") - file, fileHeader, err := c.Request.FormFile("file") - if err != nil { - logrus.Errorf("api.UploadAttachment err: %v", err) - response.ToErrorResponse(errcode.FileUploadFailed) - return - } - defer file.Close() - - if err = fileCheck(uploadType, fileHeader.Size); err != nil { - cErr, _ := err.(*errcode.Error) - response.ToErrorResponse(cErr) - return - } - - contentType := fileHeader.Header.Get("Content-Type") - fileExt, err := GetFileExt(fileHeader.Header.Get("Content-Type")) - if err != nil { - logrus.Errorf("GetFileExt err: %v", err) - response.ToErrorResponse(err.(*errcode.Error)) - return - } - - // 生成随机路径 - randomPath := uuid.Must(uuid.NewV4()).String() - ossSavePath := uploadType + "/" + GeneratePath(randomPath[:8]) + "/" + randomPath[9:] + fileExt - - objectUrl, err := objectStorage.PutObject(ossSavePath, file, fileHeader.Size, contentType, false) - if err != nil { - logrus.Errorf("putObject err: %v", err) - response.ToErrorResponse(errcode.FileUploadFailed) - return - } - - // 构造附件Model - attachment := &cs.Attachment{ - FileSize: fileHeader.Size, - Content: objectUrl, - } - - if userID, exists := c.Get("UID"); exists { - attachment.UserID = userID.(int64) - } - - attachment.Type = uploadAttachmentTypeMap[uploadType] - if attachment.Type == cs.AttachmentTypeImage { - var src image.Image - src, err = imaging.Decode(file) - if err == nil { - attachment.ImgWidth, attachment.ImgHeight = GetImageSize(src.Bounds()) - } - } - - attachment.ID, err = broker.CreateAttachment(attachment) - if err != nil { - logrus.Errorf("service.CreateAttachment err: %v", err) - response.ToErrorResponse(errcode.FileUploadFailed) - } - - response.ToResponse(attachment) -} - -func DownloadAttachmentPrecheck(c *gin.Context) { - response := app.NewResponse(c) - - contentID := convert.StrTo(c.Query("id")).MustInt64() - // 加载content - content, err := broker.GetPostContentByID(contentID) - if err != nil { - logrus.Errorf("service.GetPostContentByID err: %v", err) - response.ToErrorResponse(errcode.InvalidDownloadReq) - } - user, _ := c.Get("USER") - if content.Type == core.ContentTypeChargeAttachment { - // 加载post - post, err := broker.GetPost(content.PostID) - if err != nil { - logrus.Errorf("service.GetPost err: %v", err) - response.ToResponse(gin.H{ - "paid": false, - }) - return - } - - // 发布者或管理员免费下载 - if post.UserID == user.(*core.User).ID || user.(*core.User).IsAdmin { - response.ToResponse(gin.H{ - "paid": true, - }) - return - } - - // 检测是否有购买记录 - response.ToResponse(gin.H{ - "paid": broker.CheckPostAttachmentIsPaid(post.ID, user.(*core.User).ID), - }) - return - } - response.ToResponse(gin.H{ - "paid": false, - }) -} - -func DownloadAttachment(c *gin.Context) { - response := app.NewResponse(c) - - contentID := convert.StrTo(c.Query("id")).MustInt64() - - // 加载content - content, err := broker.GetPostContentByID(contentID) - if err != nil { - logrus.Errorf("service.GetPostContentByID err: %v", err) - response.ToErrorResponse(errcode.InvalidDownloadReq) - } - - // 收费附件 - if content.Type == core.ContentTypeChargeAttachment { - user, _ := c.Get("USER") - - // 加载post - post, err := broker.GetPost(content.PostID) - if err != nil { - logrus.Errorf("service.GetPost err: %v", err) - response.ToResponse(gin.H{ - "paid": false, - }) - return - } - - paidFlag := false - - // 发布者或管理员免费下载 - if post.UserID == user.(*core.User).ID || user.(*core.User).IsAdmin { - paidFlag = true - } - - // 检测是否有购买记录 - if broker.CheckPostAttachmentIsPaid(post.ID, user.(*core.User).ID) { - paidFlag = true - } - - if !paidFlag { - // 未购买,则尝试购买 - err := broker.BuyPostAttachment(&core.Post{ - Model: &core.Model{ - ID: post.ID, - }, - UserID: post.UserID, - AttachmentPrice: post.AttachmentPrice, - }, user.(*core.User)) - if err != nil { - logrus.Errorf("service.BuyPostAttachment err: %v", err) - if err == errcode.InsuffientDownloadMoney { - - response.ToErrorResponse(errcode.InsuffientDownloadMoney) - } else { - - response.ToErrorResponse(errcode.DownloadExecFail) - } - return - } - } - } - - objectKey := objectStorage.ObjectKey(content.Content) - signedURL, err := objectStorage.SignURL(objectKey, 60) - if err != nil { - logrus.Errorf("client.SignURL err: %v", err) - response.ToErrorResponse(errcode.DownloadReqError) - return - } - response.ToResponse(gin.H{ - "signed_url": signedURL, - }) -} diff --git a/internal/servants/web/routers/api/comment.go b/internal/servants/web/routers/api/comment.go deleted file mode 100644 index 0779eabb..00000000 --- a/internal/servants/web/routers/api/comment.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2022 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 api - -import ( - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/sirupsen/logrus" -) - -func GetPostComments(c *gin.Context) { - postID := convert.StrTo(c.Query("id")).MustInt64() - response := app.NewResponse(c) - - contents, totalRows, err := broker.GetPostComments(postID, "id ASC", 0, 0) - - if err != nil { - logrus.Errorf("service.GetPostComments err: %v\n", err) - response.ToErrorResponse(errcode.GetCommentsFailed) - return - } - - response.ToResponseList(contents, totalRows) -} - -func CreatePostComment(c *gin.Context) { - param := broker.CommentCreationReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - comment, err := broker.CreatePostComment(c, userID.(int64), param) - - if err != nil { - if err == errcode.MaxCommentCount { - response.ToErrorResponse(errcode.MaxCommentCount) - } else { - logrus.Errorf("service.CreatePostComment err: %v\n", err) - response.ToErrorResponse(errcode.CreateCommentFailed) - } - return - } - - response.ToResponse(comment) -} - -func DeletePostComment(c *gin.Context) { - param := broker.CommentDelReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - user, _ := c.Get("USER") - - comment, err := broker.GetPostComment(param.ID) - if err != nil { - logrus.Errorf("service.GetPostComment err: %v\n", err) - response.ToErrorResponse(errcode.GetCommentFailed) - return - } - - if user.(*core.User).ID != comment.UserID && !user.(*core.User).IsAdmin { - response.ToErrorResponse(errcode.NoPermission) - return - } - - // 执行删除 - err = broker.DeletePostComment(comment) - if err != nil { - logrus.Errorf("service.DeletePostComment err: %v\n", err) - response.ToErrorResponse(errcode.DeleteCommentFailed) - return - } - - response.ToResponse(nil) -} - -func CreatePostCommentReply(c *gin.Context) { - param := broker.CommentReplyCreationReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - user, _ := c.Get("USER") - - comment, err := broker.CreatePostCommentReply(c, param.CommentID, param.Content, user.(*core.User).ID, param.AtUserID) - if err != nil { - logrus.Errorf("service.CreatePostCommentReply err: %v\n", err) - response.ToErrorResponse(errcode.CreateReplyFailed) - return - } - - response.ToResponse(comment) -} - -func DeletePostCommentReply(c *gin.Context) { - param := broker.ReplyDelReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, _ := c.Get("USER") - - reply, err := broker.GetPostCommentReply(param.ID) - if err != nil { - logrus.Errorf("service.GetPostCommentReply err: %v\n", err) - response.ToErrorResponse(errcode.GetReplyFailed) - return - } - - if user.(*core.User).ID != reply.UserID && !user.(*core.User).IsAdmin { - response.ToErrorResponse(errcode.NoPermission) - return - } - - // 执行删除 - err = broker.DeletePostCommentReply(reply) - if err != nil { - logrus.Errorf("service.DeletePostCommentReply err: %v\n", err) - response.ToErrorResponse(errcode.DeleteCommentFailed) - return - } - - response.ToResponse(nil) -} diff --git a/internal/servants/web/routers/api/home.go b/internal/servants/web/routers/api/home.go deleted file mode 100644 index 74523992..00000000 --- a/internal/servants/web/routers/api/home.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2022 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 api - -import ( - "bytes" - "encoding/base64" - "image/color" - "image/png" - "time" - - "github.com/afocus/captcha" - "github.com/gin-gonic/gin" - "github.com/gofrs/uuid" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/servants/web/assets" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/debug" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/rocboss/paopao-ce/pkg/utils" - "github.com/sirupsen/logrus" -) - -const MAX_PHONE_CAPTCHA = 10 - -func Version(c *gin.Context) { - response := app.NewResponse(c) - response.ToResponse(gin.H{ - "BuildInfo": debug.ReadBuildInfo(), - }) -} - -func SyncSearchIndex(c *gin.Context) { - response := app.NewResponse(c) - - user, _ := c.Get("USER") - - if user.(*core.User).IsAdmin { - go broker.PushPostsToSearch(c) - } - - response.ToResponse(nil) -} - -func GetCaptcha(c *gin.Context) { - cap := captcha.New() - - if err := cap.AddFontFromBytes(assets.ComicBytes); err != nil { - panic(err.Error()) - } - - cap.SetSize(160, 64) - cap.SetDisturbance(captcha.MEDIUM) - cap.SetFrontColor(color.RGBA{0, 0, 0, 255}) - cap.SetBkgColor(color.RGBA{218, 240, 228, 255}) - img, password := cap.Create(6, captcha.NUM) - emptyBuff := bytes.NewBuffer(nil) - _ = png.Encode(emptyBuff, img) - - key := utils.EncodeMD5(uuid.Must(uuid.NewV4()).String()) - - // 五分钟有效期 - redisClient.SetEx(c, "PaoPaoCaptcha:"+key, password, time.Minute*5) - - response := app.NewResponse(c) - response.ToResponse(gin.H{ - "id": key, - "b64s": "data:image/png;base64," + base64.StdEncoding.EncodeToString(emptyBuff.Bytes()), - }) -} - -func PostCaptcha(c *gin.Context) { - param := broker.PhoneCaptchaReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - // 验证图片验证码 - if res, err := redisClient.Get(c.Request.Context(), "PaoPaoCaptcha:"+param.ImgCaptchaID).Result(); err != nil || res != param.ImgCaptcha { - response.ToErrorResponse(errcode.ErrorCaptchaPassword) - return - } - redisClient.Del(c.Request.Context(), "PaoPaoCaptcha:"+param.ImgCaptchaID).Result() - - // 今日频次限制 - if res, _ := redisClient.Get(c.Request.Context(), "PaoPaoSmsCaptcha:"+param.Phone).Result(); convert.StrTo(res).MustInt() >= MAX_PHONE_CAPTCHA { - response.ToErrorResponse(errcode.TooManyPhoneCaptchaSend) - return - } - - err := broker.SendPhoneCaptcha(c, param.Phone) - if err != nil { - logrus.Errorf("app.SendPhoneCaptcha errs: %v", errs) - response.ToErrorResponse(errcode.GetPhoneCaptchaError) - return - } - - response.ToResponse(nil) -} diff --git a/internal/servants/web/routers/api/message.go b/internal/servants/web/routers/api/message.go deleted file mode 100644 index 9cc72ffd..00000000 --- a/internal/servants/web/routers/api/message.go +++ /dev/null @@ -1,105 +0,0 @@ -// Copyright 2022 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 api - -import ( - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/sirupsen/logrus" -) - -func GetUnreadMsgCount(c *gin.Context) { - response := app.NewResponse(c) - - user := &core.User{} - if u, exists := c.Get("USER"); exists { - user = u.(*core.User) - } - - count, _ := broker.GetUnreadCount(user.ID) - - response.ToResponse(gin.H{ - "count": count, - }) -} - -func GetMessages(c *gin.Context) { - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - messages, totalRows, err := broker.GetMessages(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) - - if err != nil { - logrus.Errorf("service.GetMessages err: %v\n", err) - response.ToErrorResponse(errcode.GetMessagesFailed) - return - } - - response.ToResponseList(messages, totalRows) -} - -func ReadMessage(c *gin.Context) { - param := broker.ReadMessageReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - err := broker.ReadMessage(param.ID, userID.(int64)) - if err != nil { - logrus.Errorf("service.ReadMessage err: %v\n", err) - response.ToErrorResponse(errcode.ReadMessageFailed) - return - } - - response.ToResponse(nil) -} - -func SendUserWhisper(c *gin.Context) { - param := broker.WhisperReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - - // 不允许发送私信给自己 - if userID.(int64) == param.UserID { - response.ToErrorResponse(errcode.NoWhisperToSelf) - return - } - - _, err := broker.CreateWhisper(c, &core.Message{ - SenderUserID: userID.(int64), - ReceiverUserID: param.UserID, - Type: core.MsgTypeWhisper, - Brief: "给你发送新私信了", - Content: param.Content, - }) - - if err != nil { - logrus.Errorf("service.CreateWhisper err: %v\n", err) - - if err == errcode.TooManyWhisperNum { - response.ToErrorResponse(errcode.TooManyWhisperNum) - } else { - response.ToErrorResponse(errcode.SendWhisperFailed) - } - return - } - - response.ToResponse(nil) -} diff --git a/internal/servants/web/routers/api/post.go b/internal/servants/web/routers/api/post.go deleted file mode 100644 index 0dc923b0..00000000 --- a/internal/servants/web/routers/api/post.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2022 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 api - -import ( - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/sirupsen/logrus" -) - -func GetPostList(c *gin.Context) { - response := app.NewResponse(c) - - q := &core.QueryReq{ - Query: c.Query("query"), - Type: "search", - } - if c.Query("type") == "tag" { - q.Type = "tag" - } - - user, _ := userFrom(c) - offset, limit := app.GetPageOffset(c) - if q.Query == "" && q.Type == "search" { - resp, err := broker.GetIndexPosts(user, offset, limit) - if err != nil { - logrus.Errorf("service.GetPostList err: %v\n", err) - response.ToErrorResponse(errcode.GetPostsFailed) - return - } - - response.ToResponseList(resp.Tweets, resp.Total) - } else { - posts, totalRows, err := broker.GetPostListFromSearch(user, q, offset, limit) - - if err != nil { - logrus.Errorf("service.GetPostListFromSearch err: %v\n", err) - response.ToErrorResponse(errcode.GetPostsFailed) - return - } - response.ToResponseList(posts, totalRows) - } -} - -func GetPost(c *gin.Context) { - postID := convert.StrTo(c.Query("id")).MustInt64() - response := app.NewResponse(c) - - postFormated, err := broker.GetPost(postID) - - if err != nil { - logrus.Errorf("service.GetPost err: %v\n", err) - response.ToErrorResponse(errcode.GetPostFailed) - return - } - - response.ToResponse(postFormated) -} - -func CreatePost(c *gin.Context) { - param := broker.PostCreationReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - post, err := broker.CreatePost(c, userID.(int64), param) - - if err != nil { - logrus.Errorf("service.CreatePost err: %v\n", err) - response.ToErrorResponse(errcode.CreatePostFailed) - return - } - - response.ToResponse(post) -} - -func DeletePost(c *gin.Context) { - param := broker.PostDelReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, exist := userFrom(c) - if !exist { - response.ToErrorResponse(errcode.NoPermission) - return - } - - err := broker.DeletePost(user, param.ID) - if err != nil { - logrus.Errorf("service.DeletePost err: %v\n", err) - response.ToErrorResponse(err) - return - } - - response.ToResponse(nil) -} - -func GetPostStar(c *gin.Context) { - postID := convert.StrTo(c.Query("id")).MustInt64() - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - - _, err := broker.GetPostStar(postID, userID.(int64)) - if err != nil { - response.ToResponse(gin.H{ - "status": false, - }) - - return - } - - response.ToResponse(gin.H{ - "status": true, - }) -} - -func PostStar(c *gin.Context) { - param := broker.PostStarReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - - status := false - star, err := broker.GetPostStar(param.ID, userID.(int64)) - if err != nil { - // 创建Star - _, err = broker.CreatePostStar(param.ID, userID.(int64)) - status = true - } else { - // 取消Star - err = broker.DeletePostStar(star) - } - - if err != nil { - response.ToErrorResponse(errcode.NoPermission) - return - } - - response.ToResponse(gin.H{ - "status": status, - }) -} - -func GetPostCollection(c *gin.Context) { - postID := convert.StrTo(c.Query("id")).MustInt64() - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - - _, err := broker.GetPostCollection(postID, userID.(int64)) - if err != nil { - response.ToResponse(gin.H{ - "status": false, - }) - - return - } - - response.ToResponse(gin.H{ - "status": true, - }) -} - -func PostCollection(c *gin.Context) { - param := broker.PostCollectionReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - userID, _ := c.Get("UID") - - status := false - collection, err := broker.GetPostCollection(param.ID, userID.(int64)) - if err != nil { - // 创建collection - _, err = broker.CreatePostCollection(param.ID, userID.(int64)) - status = true - } else { - // 取消Star - err = broker.DeletePostCollection(collection) - } - - if err != nil { - response.ToErrorResponse(errcode.NoPermission) - return - } - - response.ToResponse(gin.H{ - "status": status, - }) -} - -func LockPost(c *gin.Context) { - param := broker.PostLockReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, _ := c.Get("USER") - - // 获取Post - postFormated, err := broker.GetPost(param.ID) - if err != nil { - logrus.Errorf("service.GetPost err: %v\n", err) - response.ToErrorResponse(errcode.GetPostFailed) - return - } - - if postFormated.UserID != user.(*core.User).ID && !user.(*core.User).IsAdmin { - response.ToErrorResponse(errcode.NoPermission) - return - } - err = broker.LockPost(param.ID) - if err != nil { - logrus.Errorf("service.LockPost err: %v\n", err) - response.ToErrorResponse(errcode.LockPostFailed) - return - } - - response.ToResponse(gin.H{ - "lock_status": 1 - postFormated.IsLock, - }) -} - -func StickPost(c *gin.Context) { - param := broker.PostStickReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, _ := c.Get("USER") - - // 获取Post - postFormated, err := broker.GetPost(param.ID) - if err != nil { - logrus.Errorf("service.GetPost err: %v\n", err) - response.ToErrorResponse(errcode.GetPostFailed) - return - } - - if !user.(*core.User).IsAdmin { - response.ToErrorResponse(errcode.NoPermission) - return - } - err = broker.StickPost(param.ID) - if err != nil { - logrus.Errorf("service.StickPost err: %v\n", err) - response.ToErrorResponse(errcode.LockPostFailed) - return - } - - response.ToResponse(gin.H{ - "top_status": 1 - postFormated.IsTop, - }) -} - -func VisiblePost(c *gin.Context) { - param := broker.PostVisibilityReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, _ := userFrom(c) - if err := broker.VisiblePost(user, param.ID, param.Visibility); err != nil { - logrus.Errorf("service.VisiblePost err: %v\n", err) - response.ToErrorResponse(err) - return - } - - response.ToResponse(gin.H{ - "visibility": param.Visibility, - }) -} - -func GetPostTags(c *gin.Context) { - param := broker.PostTagsReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - tags, err := broker.GetPostTags(¶m) - if err != nil { - logrus.Errorf("service.GetPostTags err: %v\n", err) - response.ToErrorResponse(errcode.GetPostTagsFailed) - return - - } - - response.ToResponse(gin.H{ - "topics": tags, - }) -} diff --git a/internal/servants/web/routers/api/user.go b/internal/servants/web/routers/api/user.go deleted file mode 100644 index 89aa6615..00000000 --- a/internal/servants/web/routers/api/user.go +++ /dev/null @@ -1,650 +0,0 @@ -// Copyright 2022 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 api - -import ( - "fmt" - "net/http" - "unicode/utf8" - - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/core" - "github.com/rocboss/paopao-ce/internal/servants/web/broker" - "github.com/rocboss/paopao-ce/pkg/app" - "github.com/rocboss/paopao-ce/pkg/convert" - "github.com/rocboss/paopao-ce/pkg/errcode" - "github.com/sirupsen/logrus" - "github.com/smartwalle/alipay/v3" -) - -// Login 用户登录 -func Login(c *gin.Context) { - param := broker.AuthRequest{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, err := broker.DoLogin(c, ¶m) - if err != nil { - logrus.Errorf("service.DoLogin err: %v", err) - response.ToErrorResponse(err.(*errcode.Error)) - return - } - - token, err := app.GenerateToken(user) - if err != nil { - logrus.Errorf("app.GenerateToken err: %v", err) - response.ToErrorResponse(errcode.UnauthorizedTokenGenerate) - return - } - - response.ToResponse(gin.H{ - "token": token, - }) -} - -// Register 用户注册 -func Register(c *gin.Context) { - - param := broker.RegisterRequest{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - // 用户名检查 - err := broker.ValidUsername(param.Username) - if err != nil { - logrus.Errorf("service.Register err: %v", err) - response.ToErrorResponse(err.(*errcode.Error)) - return - } - - // 密码检查 - err = broker.CheckPassword(param.Password) - if err != nil { - logrus.Errorf("service.Register err: %v", err) - response.ToErrorResponse(err.(*errcode.Error)) - return - } - - user, err := broker.Register( - param.Username, - param.Password, - ) - - if err != nil { - logrus.Errorf("service.Register err: %v", err) - response.ToErrorResponse(errcode.UserRegisterFailed) - return - } - - response.ToResponse(gin.H{ - "id": user.ID, - "username": user.Username, - }) -} - -func RequestingFriend(c *gin.Context) { - param := broker.RequestingFriendReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, ok := userFrom(c) - if !ok { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - } - if user.ID == param.UserId { - response.ToErrorResponse(errcode.NoRequestingFriendToSelf) - return - } - - if err := broker.RequestingFriend(user, ¶m); err != nil { - logrus.Errorf("service.RequestingFriend err: %v", err) - response.ToErrorResponse(errcode.SendRequestingFriendFailed) - return - } - - response.ToResponse(nil) -} - -func AddFriend(c *gin.Context) { - param := broker.AddFriendReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - user, ok := userFrom(c) - if !ok { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - } - if user.ID == param.UserId { - response.ToErrorResponse(errcode.NoActionToSelf) - return - } - - if err := broker.AddFriend(user, ¶m); err != nil { - logrus.Errorf("service.AddFriend err: %v", err) - response.ToErrorResponse(errcode.AddFriendFailed) - return - } - - response.ToResponse(nil) -} - -func RejectFriend(c *gin.Context) { - param := broker.RejectFriendReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - user, ok := userFrom(c) - if !ok { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - } - if user.ID == param.UserId { - response.ToErrorResponse(errcode.NoActionToSelf) - return - } - - if err := broker.RejectFriend(user, ¶m); err != nil { - logrus.Errorf("service.RejectFriend err: %v", err) - response.ToErrorResponse(errcode.RejectFriendFailed) - return - } - - response.ToResponse(nil) -} - -func DeleteFriend(c *gin.Context) { - param := broker.DeleteFriendReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - user, ok := userFrom(c) - if !ok { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - } - if user.ID == param.UserId { - response.ToErrorResponse(errcode.NoActionToSelf) - return - } - - if err := broker.DeleteFriend(user, ¶m); err != nil { - logrus.Errorf("service.DeleteFriend err: %v", err) - response.ToErrorResponse(errcode.DeleteFriendFailed) - return - } - - response.ToResponse(nil) -} - -func GetContacts(c *gin.Context) { - response := app.NewResponse(c) - user, ok := userFrom(c) - if !ok { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - } - - offset, limit := app.GetPageOffset(c) - resp, err := broker.GetContacts(user, offset, limit) - if err != nil { - logrus.Errorf("service.DeleteFriend err: %v", err) - response.ToErrorResponse(errcode.GetContactsFailed) - return - } - response.ToResponseList(resp.Contacts, resp.Total) -} - -// 获取用户基本信息 -func GetUserInfo(c *gin.Context) { - param := broker.AuthRequest{} - response := app.NewResponse(c) - - if username, exists := c.Get("USERNAME"); exists { - param.Username = username.(string) - } - - user, err := broker.GetUserInfo(¶m) - - if err != nil { - response.ToErrorResponse(errcode.UnauthorizedAuthNotExist) - return - } - phone := "" - if user.Phone != "" && len(user.Phone) == 11 { - phone = user.Phone[0:3] + "****" + user.Phone[7:] - } - - response.ToResponse(gin.H{ - "id": user.ID, - "nickname": user.Nickname, - "username": user.Username, - "status": user.Status, - "avatar": user.Avatar, - "balance": user.Balance, - "phone": phone, - "is_admin": user.IsAdmin, - }) -} - -// 修改密码 -func ChangeUserPassword(c *gin.Context) { - param := broker.ChangePasswordReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user := &core.User{} - if u, exists := c.Get("USER"); exists { - user = u.(*core.User) - } - - // 密码检查 - err := broker.CheckPassword(param.Password) - if err != nil { - logrus.Errorf("service.Register err: %v", err) - response.ToErrorResponse(err.(*errcode.Error)) - return - } - - // 旧密码校验 - if !broker.ValidPassword(user.Password, param.OldPassword, user.Salt) { - response.ToErrorResponse(errcode.ErrorOldPassword) - return - } - - // 更新入库 - user.Password, user.Salt = broker.EncryptPasswordAndSalt(param.Password) - broker.UpdateUserInfo(user) - - response.ToResponse(nil) -} - -// 修改昵称 -func ChangeNickname(c *gin.Context) { - param := broker.ChangeNicknameReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user := &core.User{} - if u, exists := c.Get("USER"); exists { - user = u.(*core.User) - } - - if utf8.RuneCountInString(param.Nickname) < 2 || utf8.RuneCountInString(param.Nickname) > 12 { - response.ToErrorResponse(errcode.NicknameLengthLimit) - return - } - - // 执行绑定 - user.Nickname = param.Nickname - broker.UpdateUserInfo(user) - - response.ToResponse(nil) -} - -// 修改头像 -func ChangeAvatar(c *gin.Context) { - param := broker.ChangeAvatarReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user, exist := userFrom(c) - if !exist { - response.ToErrorResponse(errcode.UnauthorizedTokenError) - return - } - - if err := broker.ChangeUserAvatar(user, param.Avatar); err != nil { - response.ToErrorResponse(err) - return - } - response.ToResponse(nil) -} - -// 用户绑定手机号 -func BindUserPhone(c *gin.Context) { - param := broker.UserPhoneBindReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - user := &core.User{} - if u, exists := c.Get("USER"); exists { - user = u.(*core.User) - } - - // 手机重复性检查 - if broker.CheckPhoneExist(user.ID, param.Phone) { - response.ToErrorResponse(errcode.ExistedUserPhone) - return - } - - if err := broker.CheckPhoneCaptcha(param.Phone, param.Captcha); err != nil { - logrus.Errorf("service.CheckPhoneCaptcha err: %v\n", err) - response.ToErrorResponse(err) - return - } - - // 执行绑定 - user.Phone = param.Phone - if err := broker.UpdateUserInfo(user); err != nil { - response.ToErrorResponse(err) - return - } - - response.ToResponse(nil) -} - -// 修改用户状态 -func ChangeUserStatus(c *gin.Context) { - param := broker.ChangeUserStatusReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - if param.Status != core.UserStatusNormal && param.Status != core.UserStatusClosed { - response.ToErrorResponse(errcode.InvalidParams) - return - } - - user, err := broker.GetUserByID(param.ID) - if err != nil { - logrus.Errorf("service.GetUserByID err: %v\n", err) - response.ToErrorResponse(errcode.NoExistUsername) - return - } - - // 执行更新 - user.Status = param.Status - broker.UpdateUserInfo(user) - - response.ToResponse(nil) -} - -func GetUserProfile(c *gin.Context) { - response := app.NewResponse(c) - username := c.Query("username") - user, _ := userFrom(c) - - resp, err := broker.GetUserByUsername(user, username) - if err != nil { - logrus.Errorf("service.GetUserByUsername err: %v\n", err) - response.ToErrorResponse(errcode.NoExistUsername) - return - } - - response.ToResponse(resp) -} - -func GetUserPosts(c *gin.Context) { - response := app.NewResponse(c) - username := c.Query("username") - user, exists := userFrom(c) - - other, err := broker.GetUserByUsername(user, username) - if err != nil { - logrus.Errorf("service.GetUserByUsername err: %v\n", err) - response.ToErrorResponse(errcode.NoExistUsername) - return - } - - visibilities := []core.PostVisibleT{core.PostVisitPublic} - if exists { - if user.ID == other.ID || user.IsAdmin { - visibilities = append(visibilities, core.PostVisitPrivate, core.PostVisitFriend) - } else if other.IsFriend { - visibilities = append(visibilities, core.PostVisitFriend) - } - } - conditions := core.ConditionsT{ - "user_id": other.ID, - "visibility IN ?": visibilities, - "ORDER": "latest_replied_on DESC", - } - - _, posts, err := broker.GetPostList(&broker.PostListReq{ - Conditions: &conditions, - Offset: (app.GetPage(c) - 1) * app.GetPageSize(c), - Limit: app.GetPageSize(c), - }) - if err != nil { - logrus.Errorf("service.GetPostList err: %v\n", err) - response.ToErrorResponse(errcode.GetPostsFailed) - return - } - totalRows, _ := broker.GetPostCount(&conditions) - - response.ToResponseList(posts, totalRows) -} - -func GetUserCollections(c *gin.Context) { - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - posts, totalRows, err := broker.GetUserCollections(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) - - if err != nil { - logrus.Errorf("service.GetUserCollections err: %v\n", err) - response.ToErrorResponse(errcode.GetCollectionsFailed) - return - } - - response.ToResponseList(posts, totalRows) -} - -func GetUserStars(c *gin.Context) { - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - posts, totalRows, err := broker.GetUserStars(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) - if err != nil { - logrus.Errorf("service.GetUserStars err: %v\n", err) - response.ToErrorResponse(errcode.GetCollectionsFailed) - return - } - - response.ToResponseList(posts, totalRows) -} - -func GetSuggestUsers(c *gin.Context) { - keyword := c.Query("k") - response := app.NewResponse(c) - - usernames, err := broker.GetSuggestUsers(keyword) - if err != nil { - logrus.Errorf("service.GetSuggestUsers err: %v\n", err) - response.ToErrorResponse(errcode.GetCollectionsFailed) - return - } - - response.ToResponse(gin.H{ - "suggest": usernames, - }) -} - -func GetSuggestTags(c *gin.Context) { - keyword := c.Query("k") - response := app.NewResponse(c) - - tags, err := broker.GetSuggestTags(keyword) - if err != nil { - logrus.Errorf("service.GetSuggestTags err: %v\n", err) - response.ToErrorResponse(errcode.GetCollectionsFailed) - return - } - - response.ToResponse(gin.H{ - "suggest": tags, - }) -} - -func GetUserRechargeLink(c *gin.Context) { - param := broker.RechargeReq{} - response := app.NewResponse(c) - valid, errs := app.BindAndValid(c, ¶m) - if !valid { - logrus.Errorf("app.BindAndValid errs: %v", errs) - response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...)) - return - } - - // 下单 - userID, _ := c.Get("UID") - recharge, err := broker.CreateRecharge(userID.(int64), param.Amount) - if err != nil { - logrus.Errorf("service.CreateRecharge err: %v\n", err) - response.ToErrorResponse(errcode.RechargeReqFail) - return - } - - p := alipay.TradePreCreate{} - p.OutTradeNo = fmt.Sprintf("%d", recharge.ID) - p.Subject = "PaoPao用户钱包充值" - p.TotalAmount = fmt.Sprintf("%.2f", float64(recharge.Amount)/100.0) - p.NotifyURL = "https://" + c.Request.Host + "/v1/alipay/notify" - - rsp, err := alipayClient.TradePreCreate(p) - if err != nil { - logrus.Errorf("client.TradePreCreate err: %v\n", err) - response.ToErrorResponse(errcode.RechargeReqFail) - return - } - if rsp.Content.Code != alipay.CodeSuccess { - response.ToErrorResponse(errcode.RechargeReqFail) - return - } - - response.ToResponse(gin.H{ - "id": recharge.ID, - "pay": rsp.Content.QRCode, - }) -} - -func GetUserRechargeResult(c *gin.Context) { - response := app.NewResponse(c) - - id := c.Query("id") - userID, _ := c.Get("UID") - - recharge, err := broker.GetRechargeByID(convert.StrTo(id).MustInt64()) - if err != nil { - response.ToErrorResponse(errcode.GetRechargeFailed) - return - } - - if recharge.UserID != userID.(int64) { - response.ToErrorResponse(errcode.GetRechargeFailed) - return - } - - response.ToResponse(gin.H{ - "id": recharge.ID, - "status": recharge.TradeStatus, - }) -} - -func AlipayNotify(c *gin.Context) { - response := app.NewResponse(c) - c.Request.ParseForm() - - _, err := alipayClient.GetTradeNotification(c.Request) - if err != nil { - logrus.Errorf("alipayClient.GetTradeNotification err: %v\n", err) - logrus.Infoln(c.Request.Form) - response.ToErrorResponse(errcode.RechargeNotifyError) - return - } - - id := c.Request.Form.Get("out_trade_no") - tradeNo := c.Request.Form.Get("trade_no") - tradeStatus := c.Request.Form.Get("trade_status") - if tradeStatus == "TRADE_SUCCESS" { - // 交易支付成功 - err = broker.FinishRecharge(c, convert.StrTo(id).MustInt64(), tradeNo) - if err != nil { - logrus.Errorf("service.FinishRecharge err: %v\n", err) - response.ToErrorResponse(errcode.RechargeNotifyError) - return - } - } - response.Ctx.String(http.StatusOK, "success") -} - -func GetUserWalletBills(c *gin.Context) { - response := app.NewResponse(c) - - userID, _ := c.Get("UID") - bills, totalRows, err := broker.GetUserWalletBills(userID.(int64), (app.GetPage(c)-1)*app.GetPageSize(c), app.GetPageSize(c)) - - if err != nil { - logrus.Errorf("service.GetUserWalletBills err: %v\n", err) - response.ToErrorResponse(errcode.GetCollectionsFailed) - return - } - - response.ToResponseList(bills, totalRows) -} - -func userFrom(c *gin.Context) (*core.User, bool) { - if u, exists := c.Get("USER"); exists { - user, ok := u.(*core.User) - return user, ok - } - return nil, false -} - -func userIdFrom(c *gin.Context) (int64, bool) { - if u, exists := c.Get("UID"); exists { - uid, ok := u.(int64) - return uid, ok - } - return -1, false -} diff --git a/internal/servants/web/routers/docs.go b/internal/servants/web/routers/docs.go deleted file mode 100644 index bf5e0488..00000000 --- a/internal/servants/web/routers/docs.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022 ROC. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build !docs -// +build !docs - -package routers - -import ( - "github.com/gin-gonic/gin" -) - -// registerDocs stub function for register docs asset route -func registerDocs(e *gin.Engine) { - // empty -} diff --git a/internal/servants/web/routers/docs_embed.go b/internal/servants/web/routers/docs_embed.go deleted file mode 100644 index 20754e1a..00000000 --- a/internal/servants/web/routers/docs_embed.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2022 ROC. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -//go:build docs -// +build docs - -package routers - -import ( - "github.com/alimy/cfg" - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/docs/openapi" -) - -// registerDocs register docs asset route -func registerDocs(e *gin.Engine) { - cfg.Be("Docs:OpenAPI", func() { - e.StaticFS("/docs/openapi", openapi.NewFileSystem()) - }) -} diff --git a/internal/servants/web/routers/router.go b/internal/servants/web/routers/router.go deleted file mode 100644 index 70247c6a..00000000 --- a/internal/servants/web/routers/router.go +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright 2022 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 routers - -import ( - "net/http" - "path/filepath" - - "github.com/alimy/cfg" - "github.com/gin-contrib/cors" - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/internal/servants/chain" - "github.com/rocboss/paopao-ce/internal/servants/web/routers/api" - "github.com/sirupsen/logrus" -) - -func NewRouter() *gin.Engine { - e := gin.New() - e.HandleMethodNotAllowed = true - e.Use(gin.Logger()) - e.Use(gin.Recovery()) - - // 跨域配置 - corsConfig := cors.DefaultConfig() - corsConfig.AllowAllOrigins = true - corsConfig.AddAllowHeaders("Authorization") - e.Use(cors.New(corsConfig)) - - // 按需注册 docs、静态资源、LocalOSS 路由 - { - registerDocs(e) - registerStatick(e) - - cfg.Be("LocalOSS", func() { - routeLocalOSS(e) - }) - } - - // v1 group api - r := e.Group("/v1") - - // 获取version - r.GET("/", api.Version) - - // 用户登录 - r.POST("/auth/login", api.Login) - - // 用户注册 - r.POST("/auth/register", api.Register) - - // 获取验证码 - r.GET("/captcha", api.GetCaptcha) - - // 发送验证码 - r.POST("/captcha", api.PostCaptcha) - - // 无鉴权路由组 - noAuthApi := r.Group("/") - { - // 获取动态详情 - noAuthApi.GET("/post", api.GetPost) - - // 获取动态评论 - noAuthApi.GET("/post/comments", api.GetPostComments) - - // 获取话题列表 - noAuthApi.GET("/tags", api.GetPostTags) - } - - // 宽松鉴权路由组 - looseApi := r.Group("/").Use(chain.JwtLoose()) - { - // 获取广场流 - looseApi.GET("/posts", api.GetPostList) - - // 获取用户动态列表 - looseApi.GET("/user/posts", api.GetUserPosts) - - // 获取用户基本信息 - looseApi.GET("/user/profile", api.GetUserProfile) - } - - // 鉴权路由组 - authApi := r.Group("/").Use(chain.JWT()) - privApi := r.Group("/").Use(chain.JWT()).Use(chain.Priv()) - adminApi := r.Group("/").Use(chain.JWT()).Use(chain.Admin()) - - // 核心路由注册 - routeCore(authApi, privApi, adminApi) - - // 支付宝路由注册 - cfg.Be("Alipay", func() { - routeAlipay(r, authApi) - }) - - // Relationship相关路由注册 - routeRelationship(authApi) - - // 默认404 - e.NoRoute(func(c *gin.Context) { - c.JSON(http.StatusNotFound, gin.H{ - "code": 404, - "msg": "Not Found", - }) - }) - - // 默认405 - e.NoMethod(func(c *gin.Context) { - c.JSON(http.StatusMethodNotAllowed, gin.H{ - "code": 405, - "msg": "Method Not Allowed", - }) - }) - - return e -} - -func RegisterRoute(e *gin.Engine) { - // 按需注册 Web前端静态资源 - cfg.Be("Frontend:EmbedWeb", func() { - registerStatick(e) - }) - - // 按需注册 LocalOSS 路由 - cfg.Be("LocalOSS", func() { - routeLocalOSS(e) - }) - - // v1 group api - r := e.Group("/v1") - - // 获取version - r.GET("/", api.Version) - - // 用户登录 - r.POST("/auth/login", api.Login) - - // 用户注册 - r.POST("/auth/register", api.Register) - - // 获取验证码 - r.GET("/captcha", api.GetCaptcha) - - // 发送验证码 - r.POST("/captcha", api.PostCaptcha) - - // 无鉴权路由组 - noAuthApi := r.Group("/") - { - // 获取动态详情 - noAuthApi.GET("/post", api.GetPost) - - // 获取动态评论 - noAuthApi.GET("/post/comments", api.GetPostComments) - - // 获取话题列表 - noAuthApi.GET("/tags", api.GetPostTags) - } - - // 宽松鉴权路由组 - looseApi := r.Group("/").Use(chain.JwtLoose()) - { - // 获取广场流 - looseApi.GET("/posts", api.GetPostList) - - // 获取用户动态列表 - looseApi.GET("/user/posts", api.GetUserPosts) - - // 获取用户基本信息 - looseApi.GET("/user/profile", api.GetUserProfile) - } - - // 鉴权路由组 - authApi := r.Group("/").Use(chain.JWT()) - privApi := r.Group("/").Use(chain.JWT()).Use(chain.Priv()) - adminApi := r.Group("/").Use(chain.JWT()).Use(chain.Admin()) - - // 核心路由注册 - routeCore(authApi, privApi, adminApi) - - // 支付宝路由注册 - cfg.Be("Alipay", func() { - routeAlipay(r, authApi) - }) - - // Relationship相关路由注册 - routeRelationship(authApi) -} - -func routeCore(authApi gin.IRoutes, privApi gin.IRoutes, adminApi gin.IRoutes) { - // 同步索引 - authApi.GET("/sync/index", api.SyncSearchIndex) - - // 获取当前用户信息 - authApi.GET("/user/info", api.GetUserInfo) - - // 获取当前用户未读消息数量 - authApi.GET("/user/msgcount/unread", api.GetUnreadMsgCount) - - // 获取消息列表 - authApi.GET("/user/messages", api.GetMessages) - - // 标记消息已读 - authApi.POST("/user/message/read", api.ReadMessage) - - // 发送用户私信 - authApi.POST("/user/whisper", api.SendUserWhisper) - - // 获取用户收藏列表 - authApi.GET("/user/collections", api.GetUserCollections) - - // 获取用户点赞列表 - authApi.GET("/user/stars", api.GetUserStars) - - // 绑定用户手机号 - authApi.POST("/user/phone", api.BindUserPhone) - - // 修改密码 - authApi.POST("/user/password", api.ChangeUserPassword) - - // 修改昵称 - authApi.POST("/user/nickname", api.ChangeNickname) - - // 修改头像 - authApi.POST("/user/avatar", api.ChangeAvatar) - - // 检索用户 - authApi.GET("/suggest/users", api.GetSuggestUsers) - - // 检索标签 - authApi.GET("/suggest/tags", api.GetSuggestTags) - - // 上传资源 - privApi.POST("/attachment", api.UploadAttachment) - - // 下载资源预检 - privApi.GET("/attachment/precheck", api.DownloadAttachmentPrecheck) - - // 下载资源 - privApi.GET("/attachment", api.DownloadAttachment) - - // 发布动态 - privApi.POST("/post", api.CreatePost) - - // 删除动态 - privApi.DELETE("/post", api.DeletePost) - - // 获取动态点赞状态 - authApi.GET("/post/star", api.GetPostStar) - - // 动态点赞操作 - privApi.POST("/post/star", api.PostStar) - - // 获取动态收藏状态 - authApi.GET("/post/collection", api.GetPostCollection) - - // 动态收藏操作 - privApi.POST("/post/collection", api.PostCollection) - - // 锁定动态 - privApi.POST("/post/lock", api.LockPost) - - // 置顶动态 - privApi.POST("/post/stick", api.StickPost) - - // 修改动态可见度 - privApi.POST("/post/visibility", api.VisiblePost) - - // 发布动态评论 - privApi.POST("/post/comment", api.CreatePostComment) - - // 删除动态评论 - privApi.DELETE("/post/comment", api.DeletePostComment) - - // 发布评论回复 - privApi.POST("/post/comment/reply", api.CreatePostCommentReply) - - // 删除评论回复 - privApi.DELETE("/post/comment/reply", api.DeletePostCommentReply) - - // 管理·禁言/解封用户 - adminApi.POST("/admin/user/status", api.ChangeUserStatus) -} - -// routeLocalOSS register LocalOSS route if needed -func routeLocalOSS(e *gin.Engine) { - savePath, err := filepath.Abs(conf.LocalOSSSetting.SavePath) - if err != nil { - logrus.Fatalf("get localOSS save path err: %v", err) - } - e.Static("/oss", savePath) - - logrus.Infof("register LocalOSS route in /oss on save path: %s", savePath) -} - -// routeAlipay register Alipay feature releated route if needed -func routeAlipay(public gin.IRoutes, authApi gin.IRoutes) { - // 支付宝回调 - public.POST("/alipay/notify", api.AlipayNotify) - - // 用户充值 - authApi.POST("/user/recharge", api.GetUserRechargeLink) - - // 获取钱包余额 - authApi.GET("/user/recharge", api.GetUserRechargeResult) - - // 获取用户账单 - authApi.GET("/user/wallet/bills", api.GetUserWalletBills) -} - -// routeRelationship register Relationship releated routes -func routeRelationship(authApi gin.IRoutes) { - cfg.In(cfg.Actions{ - "Friendship": func() { - routeFriendship(authApi) - }, - "Followship": func() { - routeFollowship(authApi) - }, - }, func() { - // 暂时默认使用好友模式 - // TODO: 后期提供一种无关系模式(既不是好友模式也不是关注者模式)作为默认的关系模式 - routeFriendship(authApi) - }) -} - -// routeFriendship register Friendship feature releated routes -func routeFriendship(authApi gin.IRoutes) { - // 请求添加朋友 - authApi.POST("/friend/requesting", api.RequestingFriend) - - // 同意添加好友 - authApi.POST("/friend/add", api.AddFriend) - - // 拒绝添加好友 - authApi.POST("/friend/reject", api.RejectFriend) - - // 删除好友 - authApi.POST("/friend/delete", api.DeleteFriend) - - // 获取好友列表 - authApi.GET("/user/contacts", api.GetContacts) -} - -// routeFollowship register Followship feature releated routes -func routeFollowship(authApi gin.IRoutes) { - // TODO: register followship routes -} diff --git a/internal/servants/web/routers/statick.go b/internal/servants/web/routers/statick.go deleted file mode 100644 index c1f2d1cb..00000000 --- a/internal/servants/web/routers/statick.go +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2022 ROC. All rights reserved. -// 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 - -package routers - -import ( - "github.com/gin-gonic/gin" -) - -// registerStatick stub function for register static asset route -func registerStatick(e *gin.Engine) { - // empty -} diff --git a/internal/servants/web/routers/statick_embed.go b/internal/servants/web/routers/statick_embed.go deleted file mode 100644 index dfc729f5..00000000 --- a/internal/servants/web/routers/statick_embed.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022 ROC. All rights reserved. -// 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 - -package routers - -import ( - "net/http" - - "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/web" -) - -// registerStatick register static assets route -func registerStatick(e *gin.Engine) { - routeStatic(e, "/", "/index.html", "/favicon.ico", "/assets/*filepath") -} - -func routeStatic(e *gin.Engine, paths ...string) { - staticHandler := http.FileServer(web.NewFileSystem()) - handler := func(c *gin.Context) { - staticHandler.ServeHTTP(c.Writer, c.Request) - } - for _, path := range paths { - e.GET(path, handler) - e.HEAD(path, handler) - } -} diff --git a/internal/servants/web/web.go b/internal/servants/web/web.go index e5ffe196..04b6aa78 100644 --- a/internal/servants/web/web.go +++ b/internal/servants/web/web.go @@ -26,11 +26,7 @@ var ( func RouteWeb(e *gin.Engine) { lazyInitial() oss := dao.ObjectStorageService() - ds := &base.DaoServant{ - Redis: conf.MustRedis(), - Ds: dao.DataService(), - Ts: dao.TweetSearchService(), - } + ds := base.NewDaoServant() // aways register servants api.RegisterAdminServant(e, newAdminSrv(ds), newAdminBinding(), newAdminRender()) api.RegisterCoreServant(e, newCoreSrv(ds, oss), newCoreBinding(), newCoreRender()) diff --git a/internal/service/service.go b/internal/service/service.go index 7af409f3..18c36951 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -72,9 +72,6 @@ func newService() (ss []Service) { "Docs": func() { ss = append(ss, newDocsService()) }, - "Deprecated:OldWeb": func() { - ss = append(ss, newOldWebService()) - }, }) return } diff --git a/internal/service/web_old.go b/internal/service/web_old.go deleted file mode 100644 index 9b3fbe9a..00000000 --- a/internal/service/web_old.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2022 ROC. All rights reserved. -// Use of this source code is governed by a MIT style -// license that can be found in the LICENSE file. - -package service - -import ( - "fmt" - "net/http" - - "github.com/Masterminds/semver/v3" - "github.com/fatih/color" - "github.com/rocboss/paopao-ce/internal/conf" - "github.com/rocboss/paopao-ce/internal/servants/web/routers" -) - -var ( - _ Service = (*oldWebService)(nil) -) - -type oldWebService struct { - *baseHttpService -} - -func (s *oldWebService) Name() string { - return "OldWebService" -} - -func (s *oldWebService) Version() *semver.Version { - return semver.MustParse("v0.1.0") -} - -func (s *oldWebService) OnInit() error { - s.registerRoute(s, routers.RegisterRoute) - return nil -} - -func (s *oldWebService) String() string { - return fmt.Sprintf("listen on %s\n", color.GreenString("http://%s:%s", conf.ServerSetting.HttpIp, conf.ServerSetting.HttpPort)) -} - -func newOldWebService() Service { - addr := conf.ServerSetting.HttpIp + ":" + conf.ServerSetting.HttpPort - server := httpServers.from(addr, func() *httpServer { - engine := newWebEngine() - return &httpServer{ - baseServer: newBaseServe(), - e: engine, - server: &http.Server{ - Addr: addr, - Handler: engine, - ReadTimeout: conf.ServerSetting.GetReadTimeout(), - WriteTimeout: conf.ServerSetting.GetWriteTimeout(), - MaxHeaderBytes: 1 << 20, - }, - } - }) - return &oldWebService{ - baseHttpService: &baseHttpService{ - server: server, - }, - } -} diff --git a/pkg/app/app.go b/pkg/app/app.go index a98a49a2..2d5c11c5 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -9,7 +9,7 @@ import ( "os" "github.com/gin-gonic/gin" - "github.com/rocboss/paopao-ce/pkg/errcode" + "github.com/rocboss/paopao-ce/pkg/xerror" ) type Response struct { @@ -56,12 +56,12 @@ func (r *Response) ToResponseList(list any, totalRows int64) { }) } -func (r *Response) ToErrorResponse(err *errcode.Error) { - response := gin.H{"code": err.Code(), "msg": err.Msg()} +func (r *Response) ToErrorResponse(err *xerror.Error) { + response := gin.H{"code": err.StatusCode(), "msg": err.Msg()} details := err.Details() if len(details) > 0 { response["details"] = details } - r.Ctx.JSON(err.StatusCode(), response) + r.Ctx.JSON(xerror.HttpStatusCode(err), response) } diff --git a/pkg/errcode/common_code.go b/pkg/errcode/common_code.go deleted file mode 100644 index 1f15f8c5..00000000 --- a/pkg/errcode/common_code.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2022 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 errcode - -var ( - Success = NewError(0, "成功") - ServerError = NewError(10000, "服务内部错误") - InvalidParams = NewError(10001, "入参错误") - NotFound = NewError(10002, "找不到") - UnauthorizedAuthNotExist = NewError(10003, "账户不存在") - UnauthorizedAuthFailed = NewError(10004, "账户密码错误") - UnauthorizedTokenError = NewError(10005, "鉴权失败,Token 错误或丢失") - UnauthorizedTokenTimeout = NewError(10006, "鉴权失败,Token 超时") - UnauthorizedTokenGenerate = NewError(10007, "鉴权失败,Token 生成失败") - TooManyRequests = NewError(10008, "请求过多") - - GatewayMethodsLimit = NewError(10109, "网关仅接受GET或POST请求") - GatewayLostSign = NewError(10110, "网关请求缺少签名") - GatewayLostAppKey = NewError(10111, "网关请求缺少APP KEY") - GatewayAppKeyInvalid = NewError(10112, "网关请求无效APP KEY") - GatewayAppKeyClosed = NewError(10113, "网关请求APP KEY已停用") - GatewayParamSignError = NewError(10114, "网关请求参数签名错误") - GatewayTooManyRequests = NewError(10115, "网关请求频次超限") - - FileUploadFailed = NewError(10200, "文件上传失败") - FileInvalidExt = NewError(10201, "文件类型不合法") - FileInvalidSize = NewError(10202, "文件大小超限") -) diff --git a/pkg/errcode/errcode.go b/pkg/errcode/errcode.go deleted file mode 100644 index 5143aabb..00000000 --- a/pkg/errcode/errcode.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2022 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 errcode - -import ( - "fmt" - "net/http" -) - -type Error struct { - code int - msg string - details []string -} - -var codes = map[int]string{} - -func NewError(code int, msg string) *Error { - if _, ok := codes[code]; ok { - panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code)) - } - codes[code] = msg - return &Error{code: code, msg: msg} -} - -func (e *Error) Error() string { - return fmt.Sprintf("错误码: %d, 错误信息: %s", e.Code(), e.Msg()) -} - -func (e *Error) Code() int { - return e.code -} - -func (e *Error) Msg() string { - return e.msg -} - -func (e *Error) Msgf(args []any) string { - return fmt.Sprintf(e.msg, args...) -} - -func (e *Error) Details() []string { - return e.details -} - -func (e *Error) WithDetails(details ...string) *Error { - newError := *e - newError.details = []string{} - newError.details = append(newError.details, details...) - - return &newError -} - -func (e *Error) StatusCode() int { - switch e.Code() { - case Success.Code(): - return http.StatusOK - case ServerError.Code(): - return http.StatusInternalServerError - case InvalidParams.Code(): - return http.StatusBadRequest - case UnauthorizedAuthNotExist.Code(): - fallthrough - case UnauthorizedAuthFailed.Code(): - fallthrough - case UnauthorizedTokenError.Code(): - fallthrough - case UnauthorizedTokenGenerate.Code(): - fallthrough - case UnauthorizedTokenTimeout.Code(): - return http.StatusUnauthorized - case TooManyRequests.Code(): - return http.StatusTooManyRequests - } - - return http.StatusInternalServerError -} diff --git a/pkg/errcode/module_code.go b/pkg/errcode/module_code.go deleted file mode 100644 index 9543624b..00000000 --- a/pkg/errcode/module_code.go +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2022 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 errcode - -var ( - UsernameHasExisted = NewError(20001, "用户名已存在") - UsernameLengthLimit = NewError(20002, "用户名长度3~12") - UsernameCharLimit = NewError(20003, "用户名只能包含字母、数字") - PasswordLengthLimit = NewError(20004, "密码长度6~16") - UserRegisterFailed = NewError(20005, "用户注册失败") - UserHasBeenBanned = NewError(20006, "该账户已被封停") - NoPermission = NewError(20007, "无权限执行该请求") - UserHasBindOTP = NewError(20008, "当前用户已绑定二次验证") - UserOTPInvalid = NewError(20009, "二次验证码验证失败") - UserNoBindOTP = NewError(20010, "当前用户未绑定二次验证") - ErrorOldPassword = NewError(20011, "当前用户密码验证失败") - ErrorCaptchaPassword = NewError(20012, "图形验证码验证失败") - AccountNoPhoneBind = NewError(20013, "拒绝操作: 账户未绑定手机号") - TooManyLoginError = NewError(20014, "登录失败次数过多,请稍后再试") - GetPhoneCaptchaError = NewError(20015, "短信验证码获取失败") - TooManyPhoneCaptchaSend = NewError(20016, "短信验证码获取次数已达今日上限") - ExistedUserPhone = NewError(20017, "该手机号已被绑定") - ErrorPhoneCaptcha = NewError(20018, "手机验证码不正确") - MaxPhoneCaptchaUseTimes = NewError(20019, "手机验证码已达最大使用次数") - NicknameLengthLimit = NewError(20020, "昵称长度2~12") - NoExistUsername = NewError(20021, "用户不存在") - NoAdminPermission = NewError(20022, "无管理权限") - - GetPostsFailed = NewError(30001, "获取动态列表失败") - CreatePostFailed = NewError(30002, "动态发布失败") - GetPostFailed = NewError(30003, "获取动态详情失败") - DeletePostFailed = NewError(30004, "动态删除失败") - LockPostFailed = NewError(30005, "动态锁定失败") - GetPostTagsFailed = NewError(30006, "获取话题列表失败") - InvalidDownloadReq = NewError(30007, "附件下载请求不合法") - DownloadReqError = NewError(30008, "附件下载请求失败") - InsuffientDownloadMoney = NewError(30009, "附件下载失败:账户资金不足") - DownloadExecFail = NewError(30010, "附件下载失败:扣费失败") - StickPostFailed = NewError(30011, "动态置顶失败") - VisblePostFailed = NewError(30012, "更新可见性失败") - - GetCommentsFailed = NewError(40001, "获取评论列表失败") - CreateCommentFailed = NewError(40002, "评论发布失败") - GetCommentFailed = NewError(40003, "获取评论详情失败") - DeleteCommentFailed = NewError(40004, "评论删除失败") - CreateReplyFailed = NewError(40005, "评论回复失败") - GetReplyFailed = NewError(40006, "获取评论详情失败") - MaxCommentCount = NewError(40007, "评论数已达最大限制") - - GetMessagesFailed = NewError(50001, "获取消息列表失败") - ReadMessageFailed = NewError(50002, "标记消息已读失败") - SendWhisperFailed = NewError(50003, "私信发送失败") - NoWhisperToSelf = NewError(50004, "不允许给自己发送私信") - TooManyWhisperNum = NewError(50005, "今日私信次数已达上限") - - GetCollectionsFailed = NewError(60001, "获取收藏列表失败") - GetStarsFailed = NewError(60002, "获取点赞列表失败") - - RechargeReqFail = NewError(70001, "充值请求失败") - RechargeNotifyError = NewError(70002, "充值回调失败") - GetRechargeFailed = NewError(70003, "充值详情获取失败") - - NoRequestingFriendToSelf = NewError(80001, "不允许添加自己为好友") - NotExistFriendId = NewError(80002, "好友id不存在") - SendRequestingFriendFailed = NewError(80003, "申请添加朋友请求发送失败") - AddFriendFailed = NewError(80004, "添加好友失败") - RejectFriendFailed = NewError(80005, "拒绝好友失败") - DeleteFriendFailed = NewError(80006, "删除好友失败") - GetContactsFailed = NewError(80007, "获取联系人列表失败") - NoActionToSelf = NewError(80008, "不允许对自己操作") -) diff --git a/pkg/xerror/xerror.go b/pkg/xerror/xerror.go index 0fc7878c..5bdcb115 100644 --- a/pkg/xerror/xerror.go +++ b/pkg/xerror/xerror.go @@ -15,7 +15,7 @@ import ( var ( _ mir.Error = (*Error)(nil) - codes = map[int]string{} + // codes = map[int]string{} ) type Error struct { @@ -47,10 +47,10 @@ func (v ValidErrors) Errors() []string { } func NewError(code int, msg string) *Error { - if _, ok := codes[code]; ok { - panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code)) - } - codes[code] = msg + // if _, ok := codes[code]; ok { + // panic(fmt.Sprintf("错误码 %d 已经存在,请更换一个", code)) + // } + // codes[code] = msg return &Error{code: code, msg: msg} }