mirror of https://github.com/rocboss/paopao-ce
Merge pull request #148 from alimy/pr-optimize-core-service-logic
optimize core service implement logicpull/149/head
commit
65f8bd626e
@ -0,0 +1,11 @@
|
|||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// VersionInfo 版本信息
|
||||||
|
type VersionInfo interface {
|
||||||
|
Name() string
|
||||||
|
Version() *semver.Version
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *dataServant) CreateAttachment(attachment *model.Attachment) (*model.Attachment, error) {
|
|
||||||
return attachment.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *attachmentCheckServant) CheckAttachment(uri string) error {
|
|
||||||
if strings.Index(uri, s.domain) != 0 {
|
|
||||||
return fmt.Errorf("附件非本站资源")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSimpleAuthorizationManageService() *simpleAuthorizationManageService {
|
|
||||||
return &simpleAuthorizationManageService{
|
|
||||||
db: conf.DBEngine,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleAuthorizationManageService) IsAllow(user *model.User, action *core.Action) bool {
|
|
||||||
// user is activation if had bind phone
|
|
||||||
isActivation := (len(user.Phone) != 0)
|
|
||||||
isFriend := s.isFriend(action.UserId)
|
|
||||||
// TODO: just use defaut act authorization chek rule now
|
|
||||||
return action.Act.IsAllow(user, action.UserId, isFriend, isActivation)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFriendFilter _userId保留未来使用
|
|
||||||
func (s *simpleAuthorizationManageService) GetFriendFilter(_userId int64) core.FriendFilter {
|
|
||||||
// TODO: just return an empty friend fileter now
|
|
||||||
return core.FriendFilter{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleAuthorizationManageService) GetFriendIds(_userId int64) []int64 {
|
|
||||||
// TODO: just retrun empty now
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleAuthorizationManageService) isFriend(_userId int64) bool {
|
|
||||||
// friend with all world now
|
|
||||||
return true
|
|
||||||
}
|
|
@ -0,0 +1,86 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/allegro/bigcache/v3"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewBigCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) {
|
||||||
|
s := conf.BigCacheIndexSetting
|
||||||
|
|
||||||
|
config := bigcache.DefaultConfig(s.ExpireInSecond)
|
||||||
|
config.Shards = s.MaxIndexPage
|
||||||
|
config.Verbose = s.Verbose
|
||||||
|
config.MaxEntrySize = 10000
|
||||||
|
config.Logger = logrus.StandardLogger()
|
||||||
|
cache, err := bigcache.NewBigCache(config)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("initial bigCahceIndex failure by err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheIndex := &bigCacheIndexServant{
|
||||||
|
ips: indexPosts,
|
||||||
|
cache: cache,
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexActionCh capacity custom configure by conf.yaml need in [10, 10000]
|
||||||
|
// or re-compile source to adjust min/max capacity
|
||||||
|
capacity := conf.CacheIndexSetting.MaxUpdateQPS
|
||||||
|
if capacity < 10 {
|
||||||
|
capacity = 10
|
||||||
|
} else if capacity > 10000 {
|
||||||
|
capacity = 10000
|
||||||
|
}
|
||||||
|
cacheIndex.indexActionCh = make(chan core.IndexActionT, capacity)
|
||||||
|
cacheIndex.cachePostsCh = make(chan *postsEntry, capacity)
|
||||||
|
|
||||||
|
// 启动索引更新器
|
||||||
|
go cacheIndex.startIndexPosts()
|
||||||
|
|
||||||
|
return cacheIndex, cacheIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimpleCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) {
|
||||||
|
s := conf.SimpleCacheIndexSetting
|
||||||
|
cacheIndex := &simpleCacheIndexServant{
|
||||||
|
ips: indexPosts,
|
||||||
|
maxIndexSize: s.MaxIndexSize,
|
||||||
|
indexPosts: nil,
|
||||||
|
checkTick: time.NewTicker(s.CheckTickDuration), // check whether need update index every 1 minute
|
||||||
|
expireIndexTick: time.NewTicker(time.Second),
|
||||||
|
}
|
||||||
|
|
||||||
|
// force expire index every ExpireTickDuration second
|
||||||
|
if s.ExpireTickDuration != 0 {
|
||||||
|
cacheIndex.expireIndexTick.Reset(s.CheckTickDuration)
|
||||||
|
} else {
|
||||||
|
cacheIndex.expireIndexTick.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexActionCh capacity custom configure by conf.yaml need in [10, 10000]
|
||||||
|
// or re-compile source to adjust min/max capacity
|
||||||
|
capacity := conf.CacheIndexSetting.MaxUpdateQPS
|
||||||
|
if capacity < 10 {
|
||||||
|
capacity = 10
|
||||||
|
} else if capacity > 10000 {
|
||||||
|
capacity = 10000
|
||||||
|
}
|
||||||
|
cacheIndex.indexActionCh = make(chan core.IndexActionT, capacity)
|
||||||
|
|
||||||
|
// start index posts
|
||||||
|
cacheIndex.atomicIndex.Store(cacheIndex.indexPosts)
|
||||||
|
go cacheIndex.startIndexPosts()
|
||||||
|
|
||||||
|
return cacheIndex, cacheIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoneCacheIndexService(indexPosts core.IndexPostsService) (core.CacheIndexService, core.VersionInfo) {
|
||||||
|
obj := &noneCacheIndexServant{
|
||||||
|
ips: indexPosts,
|
||||||
|
}
|
||||||
|
return obj, obj
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model/rest"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.CacheIndexService = (*noneCacheIndexServant)(nil)
|
||||||
|
_ core.VersionInfo = (*noneCacheIndexServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type noneCacheIndexServant struct {
|
||||||
|
ips core.IndexPostsService
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noneCacheIndexServant) IndexPosts(user *model.User, offset int, limit int) (*rest.IndexTweetsResp, error) {
|
||||||
|
return s.ips.IndexPosts(user, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noneCacheIndexServant) SendAction(act core.IndexActionT) {
|
||||||
|
// empty
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noneCacheIndexServant) Name() string {
|
||||||
|
return "NoneCacheIndex"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *noneCacheIndexServant) Version() *semver.Version {
|
||||||
|
return semver.MustParse("v0.1.0")
|
||||||
|
}
|
@ -0,0 +1,106 @@
|
|||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model/rest"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.CacheIndexService = (*simpleCacheIndexServant)(nil)
|
||||||
|
_ core.VersionInfo = (*simpleCacheIndexServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type simpleCacheIndexServant struct {
|
||||||
|
ips core.IndexPostsService
|
||||||
|
|
||||||
|
indexActionCh chan core.IndexActionT
|
||||||
|
indexPosts *rest.IndexTweetsResp
|
||||||
|
atomicIndex atomic.Value
|
||||||
|
maxIndexSize int
|
||||||
|
checkTick *time.Ticker
|
||||||
|
expireIndexTick *time.Ticker
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleCacheIndexServant) IndexPosts(user *model.User, offset int, limit int) (*rest.IndexTweetsResp, error) {
|
||||||
|
cacheResp := s.atomicIndex.Load().(*rest.IndexTweetsResp)
|
||||||
|
end := offset + limit
|
||||||
|
if cacheResp != nil {
|
||||||
|
size := len(cacheResp.Tweets)
|
||||||
|
logrus.Debugf("simpleCacheIndexServant.IndexPosts get index posts from cache posts: %d offset:%d limit:%d start:%d, end:%d", size, offset, limit, offset, end)
|
||||||
|
if size >= end {
|
||||||
|
return &rest.IndexTweetsResp{
|
||||||
|
Tweets: cacheResp.Tweets[offset:end],
|
||||||
|
Total: cacheResp.Total,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugln("simpleCacheIndexServant.IndexPosts get index posts from database")
|
||||||
|
return s.ips.IndexPosts(user, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleCacheIndexServant) SendAction(act core.IndexActionT) {
|
||||||
|
select {
|
||||||
|
case s.indexActionCh <- act:
|
||||||
|
logrus.Debugf("simpleCacheIndexServant.SendAction send indexAction by chan: %s", act)
|
||||||
|
default:
|
||||||
|
go func(ch chan<- core.IndexActionT, act core.IndexActionT) {
|
||||||
|
logrus.Debugf("simpleCacheIndexServant.SendAction send indexAction by goroutine: %s", act)
|
||||||
|
ch <- act
|
||||||
|
}(s.indexActionCh, act)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleCacheIndexServant) startIndexPosts() {
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-s.checkTick.C:
|
||||||
|
if s.indexPosts == nil {
|
||||||
|
logrus.Debugf("index posts by checkTick")
|
||||||
|
if s.indexPosts, err = s.ips.IndexPosts(nil, 0, s.maxIndexSize); err == nil {
|
||||||
|
s.atomicIndex.Store(s.indexPosts)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("get index posts err: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case <-s.expireIndexTick.C:
|
||||||
|
logrus.Debugf("expire index posts by expireIndexTick")
|
||||||
|
if s.indexPosts != nil {
|
||||||
|
s.indexPosts = nil
|
||||||
|
s.atomicIndex.Store(s.indexPosts)
|
||||||
|
}
|
||||||
|
case action := <-s.indexActionCh:
|
||||||
|
switch action {
|
||||||
|
// TODO: 这里列出来是因为后续可能会精细化处理每种情况
|
||||||
|
case core.IdxActCreatePost,
|
||||||
|
core.IdxActUpdatePost,
|
||||||
|
core.IdxActDeletePost,
|
||||||
|
core.IdxActStickPost,
|
||||||
|
core.IdxActVisiblePost:
|
||||||
|
// prevent many update post in least time
|
||||||
|
if s.indexPosts != nil {
|
||||||
|
logrus.Debugf("remove index posts by action %s", action)
|
||||||
|
s.indexPosts = nil
|
||||||
|
s.atomicIndex.Store(s.indexPosts)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// nop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleCacheIndexServant) Name() string {
|
||||||
|
return "SimpleCacheIndex"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *simpleCacheIndexServant) Version() *semver.Version {
|
||||||
|
return semver.MustParse("v0.1.0")
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/allegro/bigcache/v3"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ core.CacheIndexService = (*simpleCacheIndexServant)(nil)
|
|
||||||
_ core.CacheIndexService = (*bigCacheIndexServant)(nil)
|
|
||||||
_ core.CacheIndexService = (*noneCacheIndexServant)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type postsEntry struct {
|
|
||||||
key string
|
|
||||||
posts []*model.PostFormated
|
|
||||||
}
|
|
||||||
|
|
||||||
type indexPostsFunc func(*model.User, int, int) ([]*model.PostFormated, error)
|
|
||||||
|
|
||||||
type bigCacheIndexServant struct {
|
|
||||||
getIndexPosts indexPostsFunc
|
|
||||||
indexActionCh chan core.IndexActionT
|
|
||||||
cachePostsCh chan *postsEntry
|
|
||||||
cache *bigcache.BigCache
|
|
||||||
lastCacheResetTime time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
type simpleCacheIndexServant struct {
|
|
||||||
getIndexPosts indexPostsFunc
|
|
||||||
indexActionCh chan core.IndexActionT
|
|
||||||
indexPosts []*model.PostFormated
|
|
||||||
atomicIndex atomic.Value
|
|
||||||
maxIndexSize int
|
|
||||||
checkTick *time.Ticker
|
|
||||||
expireIndexTick *time.Ticker
|
|
||||||
}
|
|
||||||
|
|
||||||
type noneCacheIndexServant struct {
|
|
||||||
getIndexPosts indexPostsFunc
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newNoneCacheIndexServant(getIndexPosts indexPostsFunc) (*noneCacheIndexServant, versionInfo) {
|
|
||||||
obj := &noneCacheIndexServant{
|
|
||||||
getIndexPosts: getIndexPosts,
|
|
||||||
}
|
|
||||||
return obj, obj
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *noneCacheIndexServant) IndexPosts(user *model.User, offset int, limit int) ([]*model.PostFormated, error) {
|
|
||||||
return s.getIndexPosts(user, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *noneCacheIndexServant) SendAction(act core.IndexActionT) {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *noneCacheIndexServant) name() string {
|
|
||||||
return "NoneCacheIndex"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *noneCacheIndexServant) version() *semver.Version {
|
|
||||||
return semver.MustParse("v0.1.0")
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newSimpleCacheIndexServant(getIndexPosts indexPostsFunc) (*simpleCacheIndexServant, versionInfo) {
|
|
||||||
s := conf.SimpleCacheIndexSetting
|
|
||||||
cacheIndex := &simpleCacheIndexServant{
|
|
||||||
getIndexPosts: getIndexPosts,
|
|
||||||
maxIndexSize: s.MaxIndexSize,
|
|
||||||
indexPosts: make([]*model.PostFormated, 0),
|
|
||||||
checkTick: time.NewTicker(s.CheckTickDuration), // check whether need update index every 1 minute
|
|
||||||
expireIndexTick: time.NewTicker(time.Second),
|
|
||||||
}
|
|
||||||
|
|
||||||
// force expire index every ExpireTickDuration second
|
|
||||||
if s.ExpireTickDuration != 0 {
|
|
||||||
cacheIndex.expireIndexTick.Reset(s.CheckTickDuration)
|
|
||||||
} else {
|
|
||||||
cacheIndex.expireIndexTick.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
// indexActionCh capacity custom configure by conf.yaml need in [10, 10000]
|
|
||||||
// or re-compile source to adjust min/max capacity
|
|
||||||
capacity := conf.CacheIndexSetting.MaxUpdateQPS
|
|
||||||
if capacity < 10 {
|
|
||||||
capacity = 10
|
|
||||||
} else if capacity > 10000 {
|
|
||||||
capacity = 10000
|
|
||||||
}
|
|
||||||
cacheIndex.indexActionCh = make(chan core.IndexActionT, capacity)
|
|
||||||
|
|
||||||
// start index posts
|
|
||||||
cacheIndex.atomicIndex.Store(cacheIndex.indexPosts)
|
|
||||||
go cacheIndex.startIndexPosts()
|
|
||||||
|
|
||||||
return cacheIndex, cacheIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleCacheIndexServant) IndexPosts(user *model.User, offset int, limit int) ([]*model.PostFormated, error) {
|
|
||||||
posts := s.atomicIndex.Load().([]*model.PostFormated)
|
|
||||||
end := offset + limit
|
|
||||||
size := len(posts)
|
|
||||||
logrus.Debugf("get index posts from posts: %d offset:%d limit:%d start:%d, end:%d", size, offset, limit, offset, end)
|
|
||||||
if size >= end {
|
|
||||||
return posts[offset:end], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
logrus.Debugln("simpleCacheIndexServant.IndexPosts get index posts from database")
|
|
||||||
return s.getIndexPosts(user, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleCacheIndexServant) SendAction(act core.IndexActionT) {
|
|
||||||
select {
|
|
||||||
case s.indexActionCh <- act:
|
|
||||||
logrus.Debugf("send indexAction by chan: %s", act)
|
|
||||||
default:
|
|
||||||
go func(ch chan<- core.IndexActionT, act core.IndexActionT) {
|
|
||||||
logrus.Debugf("send indexAction by goroutine: %s", act)
|
|
||||||
ch <- act
|
|
||||||
}(s.indexActionCh, act)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleCacheIndexServant) startIndexPosts() {
|
|
||||||
var err error
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-s.checkTick.C:
|
|
||||||
if len(s.indexPosts) == 0 {
|
|
||||||
logrus.Debugf("index posts by checkTick")
|
|
||||||
if s.indexPosts, err = s.getIndexPosts(nil, 0, s.maxIndexSize); err == nil {
|
|
||||||
s.atomicIndex.Store(s.indexPosts)
|
|
||||||
} else {
|
|
||||||
logrus.Errorf("get index posts err: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case <-s.expireIndexTick.C:
|
|
||||||
logrus.Debugf("expire index posts by expireIndexTick")
|
|
||||||
if len(s.indexPosts) != 0 {
|
|
||||||
s.indexPosts = nil
|
|
||||||
s.atomicIndex.Store(s.indexPosts)
|
|
||||||
}
|
|
||||||
case action := <-s.indexActionCh:
|
|
||||||
switch action {
|
|
||||||
// TODO: 这里列出来是因为后续可能会精细化处理每种情况
|
|
||||||
case core.IdxActCreatePost,
|
|
||||||
core.IdxActUpdatePost,
|
|
||||||
core.IdxActDeletePost,
|
|
||||||
core.IdxActStickPost,
|
|
||||||
core.IdxActVisiblePost:
|
|
||||||
// prevent many update post in least time
|
|
||||||
if len(s.indexPosts) != 0 {
|
|
||||||
logrus.Debugf("remove index posts by action %s", action)
|
|
||||||
s.indexPosts = nil
|
|
||||||
s.atomicIndex.Store(s.indexPosts)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// nop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleCacheIndexServant) name() string {
|
|
||||||
return "SimpleCacheIndex"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *simpleCacheIndexServant) version() *semver.Version {
|
|
||||||
return semver.MustParse("v0.1.0")
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import "github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
|
|
||||||
func (d *dataServant) GetComments(conditions *model.ConditionsT, offset, limit int) ([]*model.Comment, error) {
|
|
||||||
return (&model.Comment{}).List(d.engine, conditions, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetCommentByID(id int64) (*model.Comment, error) {
|
|
||||||
comment := &model.Comment{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return comment.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeleteComment(comment *model.Comment) error {
|
|
||||||
return comment.Delete(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetCommentCount(conditions *model.ConditionsT) (int64, error) {
|
|
||||||
return (&model.Comment{}).Count(d.engine, conditions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) CreateComment(comment *model.Comment) (*model.Comment, error) {
|
|
||||||
return comment.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) CreateCommentReply(reply *model.CommentReply) (*model.CommentReply, error) {
|
|
||||||
return reply.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetCommentReplyByID(id int64) (*model.CommentReply, error) {
|
|
||||||
reply := &model.CommentReply{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return reply.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeleteCommentReply(reply *model.CommentReply) error {
|
|
||||||
return reply.Delete(d.engine)
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import "github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
|
|
||||||
func (d *dataServant) GetCommentContentsByIDs(ids []int64) ([]*model.CommentContent, error) {
|
|
||||||
commentContent := &model.CommentContent{}
|
|
||||||
return commentContent.List(d.engine, &model.ConditionsT{
|
|
||||||
"comment_id IN ?": ids,
|
|
||||||
}, 0, 0)
|
|
||||||
}
|
|
||||||
func (d *dataServant) GetCommentRepliesByID(ids []int64) ([]*model.CommentReplyFormated, error) {
|
|
||||||
CommentReply := &model.CommentReply{}
|
|
||||||
replies, err := CommentReply.List(d.engine, &model.ConditionsT{
|
|
||||||
"comment_id IN ?": ids,
|
|
||||||
}, 0, 0)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userIds := []int64{}
|
|
||||||
for _, reply := range replies {
|
|
||||||
userIds = append(userIds, reply.UserID, reply.AtUserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := d.GetUsersByIDs(userIds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
repliesFormated := []*model.CommentReplyFormated{}
|
|
||||||
for _, reply := range replies {
|
|
||||||
replyFormated := reply.Format()
|
|
||||||
for _, user := range users {
|
|
||||||
if reply.UserID == user.ID {
|
|
||||||
replyFormated.User = user.Format()
|
|
||||||
}
|
|
||||||
if reply.AtUserID == user.ID {
|
|
||||||
replyFormated.AtUser = user.Format()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
repliesFormated = append(repliesFormated, replyFormated)
|
|
||||||
}
|
|
||||||
|
|
||||||
return repliesFormated, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) CreateCommentContent(content *model.CommentContent) (*model.CommentContent, error) {
|
|
||||||
return content.Create(d.engine)
|
|
||||||
}
|
|
@ -0,0 +1,38 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.AuthorizationManageService = (*authorizationManageServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type authorizationManageServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *authorizationManageServant) IsAllow(user *model.User, action *core.Action) bool {
|
||||||
|
// user is activation if had bind phone
|
||||||
|
isActivation := (len(user.Phone) != 0)
|
||||||
|
isFriend := s.isFriend(user.ID, action.UserId)
|
||||||
|
// TODO: just use defaut act authorization chek rule now
|
||||||
|
return action.Act.IsAllow(user, action.UserId, isFriend, isActivation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *authorizationManageServant) BeFriendFilter(userId int64) core.FriendFilter {
|
||||||
|
// just empty now
|
||||||
|
return core.FriendFilter{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *authorizationManageServant) BeFriendIds(userId int64) ([]int64, error) {
|
||||||
|
// just empty now
|
||||||
|
return []int64{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *authorizationManageServant) isFriend(userId int64, friendId int64) bool {
|
||||||
|
// just true now
|
||||||
|
return true
|
||||||
|
}
|
@ -0,0 +1,122 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.CommentService = (*commentServant)(nil)
|
||||||
|
_ core.CommentManageService = (*commentManageServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type commentServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type commentManageServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCommentService(db *gorm.DB) core.CommentService {
|
||||||
|
return &commentServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newCommentManageService(db *gorm.DB) core.CommentManageService {
|
||||||
|
return &commentManageServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetComments(conditions *model.ConditionsT, offset, limit int) ([]*model.Comment, error) {
|
||||||
|
return (&model.Comment{}).List(s.db, conditions, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetCommentByID(id int64) (*model.Comment, error) {
|
||||||
|
comment := &model.Comment{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return comment.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetCommentReplyByID(id int64) (*model.CommentReply, error) {
|
||||||
|
reply := &model.CommentReply{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return reply.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetCommentCount(conditions *model.ConditionsT) (int64, error) {
|
||||||
|
return (&model.Comment{}).Count(s.db, conditions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetCommentContentsByIDs(ids []int64) ([]*model.CommentContent, error) {
|
||||||
|
commentContent := &model.CommentContent{}
|
||||||
|
return commentContent.List(s.db, &model.ConditionsT{
|
||||||
|
"comment_id IN ?": ids,
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentServant) GetCommentRepliesByID(ids []int64) ([]*model.CommentReplyFormated, error) {
|
||||||
|
CommentReply := &model.CommentReply{}
|
||||||
|
replies, err := CommentReply.List(s.db, &model.ConditionsT{
|
||||||
|
"comment_id IN ?": ids,
|
||||||
|
}, 0, 0)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userIds := []int64{}
|
||||||
|
for _, reply := range replies {
|
||||||
|
userIds = append(userIds, reply.UserID, reply.AtUserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := getUsersByIDs(s.db, userIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
repliesFormated := []*model.CommentReplyFormated{}
|
||||||
|
for _, reply := range replies {
|
||||||
|
replyFormated := reply.Format()
|
||||||
|
for _, user := range users {
|
||||||
|
if reply.UserID == user.ID {
|
||||||
|
replyFormated.User = user.Format()
|
||||||
|
}
|
||||||
|
if reply.AtUserID == user.ID {
|
||||||
|
replyFormated.AtUser = user.Format()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repliesFormated = append(repliesFormated, replyFormated)
|
||||||
|
}
|
||||||
|
|
||||||
|
return repliesFormated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentManageServant) DeleteComment(comment *model.Comment) error {
|
||||||
|
return comment.Delete(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentManageServant) CreateComment(comment *model.Comment) (*model.Comment, error) {
|
||||||
|
return comment.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentManageServant) CreateCommentReply(reply *model.CommentReply) (*model.CommentReply, error) {
|
||||||
|
return reply.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentManageServant) DeleteCommentReply(reply *model.CommentReply) error {
|
||||||
|
return reply.Delete(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *commentManageServant) CreateCommentContent(content *model.CommentContent) (*model.CommentContent, error) {
|
||||||
|
return content.Create(s.db)
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
// Core service implement base gorm+mysql/postgresql/sqlite3.
|
||||||
|
// Jinzhu is the primary developer of gorm so use his name as
|
||||||
|
// pakcage name as a saluter.
|
||||||
|
|
||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/dao/cache"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/dao/security"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.DataService = (*dataServant)(nil)
|
||||||
|
_ core.VersionInfo = (*dataServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type dataServant struct {
|
||||||
|
core.IndexPostsService
|
||||||
|
core.WalletService
|
||||||
|
core.MessageService
|
||||||
|
core.TopicService
|
||||||
|
core.TweetService
|
||||||
|
core.TweetManageService
|
||||||
|
core.TweetHelpService
|
||||||
|
core.CommentService
|
||||||
|
core.CommentManageService
|
||||||
|
core.UserManageService
|
||||||
|
core.SecurityService
|
||||||
|
core.AttachmentCheckService
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDataService() (core.DataService, core.VersionInfo) {
|
||||||
|
// initialize CacheIndex if needed
|
||||||
|
var (
|
||||||
|
c core.CacheIndexService
|
||||||
|
v core.VersionInfo
|
||||||
|
)
|
||||||
|
db := conf.DBEngine
|
||||||
|
|
||||||
|
i := newIndexPostsService(db)
|
||||||
|
if conf.CfgIf("SimpleCacheIndex") {
|
||||||
|
i = newSimpleIndexPostsService(db)
|
||||||
|
c, v = cache.NewSimpleCacheIndexService(i)
|
||||||
|
} else if conf.CfgIf("BigCacheIndex") {
|
||||||
|
c, v = cache.NewBigCacheIndexService(i)
|
||||||
|
} else {
|
||||||
|
c, v = cache.NewNoneCacheIndexService(i)
|
||||||
|
}
|
||||||
|
logrus.Infof("use %s as cache index service by version: %s", v.Name(), v.Version())
|
||||||
|
|
||||||
|
ds := &dataServant{
|
||||||
|
IndexPostsService: c,
|
||||||
|
WalletService: newWalletService(db),
|
||||||
|
MessageService: newMessageService(db),
|
||||||
|
TopicService: newTopicService(db),
|
||||||
|
TweetService: newTweetService(db),
|
||||||
|
TweetManageService: newTweetManageService(db, c),
|
||||||
|
TweetHelpService: newTweetHelpService(db),
|
||||||
|
CommentService: newCommentService(db),
|
||||||
|
CommentManageService: newCommentManageService(db),
|
||||||
|
UserManageService: newUserManageService(db),
|
||||||
|
SecurityService: newSecurityService(db),
|
||||||
|
AttachmentCheckService: security.NewAttachmentCheckService(),
|
||||||
|
}
|
||||||
|
return ds, ds
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthorizationManageService() core.AuthorizationManageService {
|
||||||
|
return &authorizationManageServant{
|
||||||
|
db: conf.DBEngine,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dataServant) Name() string {
|
||||||
|
return "Gorm"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *dataServant) Version() *semver.Version {
|
||||||
|
return semver.MustParse("v0.1.0")
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.MessageService = (*messageServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type messageServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMessageService(db *gorm.DB) core.MessageService {
|
||||||
|
return &messageServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) CreateMessage(msg *model.Message) (*model.Message, error) {
|
||||||
|
return msg.Create(d.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) GetUnreadCount(userID int64) (int64, error) {
|
||||||
|
return (&model.Message{}).Count(d.db, &model.ConditionsT{
|
||||||
|
"receiver_user_id": userID,
|
||||||
|
"is_read": model.MsgStatusUnread,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) GetMessageByID(id int64) (*model.Message, error) {
|
||||||
|
return (&model.Message{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}).Get(d.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) ReadMessage(message *model.Message) error {
|
||||||
|
message.IsRead = 1
|
||||||
|
return message.Update(d.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) GetMessages(conditions *model.ConditionsT, offset, limit int) ([]*model.MessageFormated, error) {
|
||||||
|
messages, err := (&model.Message{}).List(d.db, conditions, offset, limit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
mfs := []*model.MessageFormated{}
|
||||||
|
for _, message := range messages {
|
||||||
|
mf := message.Format()
|
||||||
|
mfs = append(mfs, mf)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mfs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *messageServant) GetMessageCount(conditions *model.ConditionsT) (int64, error) {
|
||||||
|
return (&model.Message{}).Count(d.db, conditions)
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"github.com/rocboss/paopao-ce/pkg/json"
|
||||||
|
"gopkg.in/resty.v1"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.SecurityService = (*securityServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type securityServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSecurityService(db *gorm.DB) core.SecurityService {
|
||||||
|
return &securityServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type juhePhoneCaptchaRsp struct {
|
||||||
|
ErrorCode int `json:"error_code"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最新短信验证码
|
||||||
|
func (s *securityServant) GetLatestPhoneCaptcha(phone string) (*model.Captcha, error) {
|
||||||
|
return (&model.Captcha{
|
||||||
|
Phone: phone,
|
||||||
|
}).Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新短信验证码
|
||||||
|
func (s *securityServant) UsePhoneCaptcha(captcha *model.Captcha) error {
|
||||||
|
captcha.UseTimes++
|
||||||
|
return captcha.Update(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送短信验证码
|
||||||
|
func (s *securityServant) SendPhoneCaptcha(phone string) error {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
captcha := rand.Intn(900000) + 100000
|
||||||
|
m := 5
|
||||||
|
|
||||||
|
client := resty.New()
|
||||||
|
client.DisableWarn = true
|
||||||
|
resp, err := client.R().
|
||||||
|
SetFormData(map[string]string{
|
||||||
|
"mobile": phone,
|
||||||
|
"tpl_id": conf.SmsJuheSetting.TplID,
|
||||||
|
"tpl_value": fmt.Sprintf(conf.SmsJuheSetting.TplVal, captcha, m),
|
||||||
|
"key": conf.SmsJuheSetting.Key,
|
||||||
|
}).Post(conf.SmsJuheSetting.Gateway)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode() != http.StatusOK {
|
||||||
|
return errors.New(resp.Status())
|
||||||
|
}
|
||||||
|
|
||||||
|
result := &juhePhoneCaptchaRsp{}
|
||||||
|
err = json.Unmarshal(resp.Body(), result)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if result.ErrorCode != 0 {
|
||||||
|
return errors.New(result.Reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 写入表
|
||||||
|
captchaModel := &model.Captcha{
|
||||||
|
Phone: phone,
|
||||||
|
Captcha: strconv.Itoa(captcha),
|
||||||
|
ExpiredOn: time.Now().Add(time.Minute * time.Duration(m)).Unix(),
|
||||||
|
}
|
||||||
|
captchaModel.Create(s.db)
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.TopicService = (*topicServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type topicServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTopicService(db *gorm.DB) core.TopicService {
|
||||||
|
return &topicServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *topicServant) CreateTag(tag *model.Tag) (*model.Tag, error) {
|
||||||
|
return createTag(s.db, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *topicServant) DeleteTag(tag *model.Tag) error {
|
||||||
|
return deleteTag(s.db, tag)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *topicServant) GetTags(conditions *model.ConditionsT, offset, limit int) ([]*model.Tag, error) {
|
||||||
|
return (&model.Tag{}).List(s.db, conditions, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *topicServant) GetTagsByKeyword(keyword string) ([]*model.Tag, error) {
|
||||||
|
tag := &model.Tag{}
|
||||||
|
|
||||||
|
keyword = "%" + strings.Trim(keyword, " ") + "%"
|
||||||
|
if keyword == "%%" {
|
||||||
|
return tag.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": "quote_num DESC",
|
||||||
|
}, 0, 6)
|
||||||
|
} else {
|
||||||
|
return tag.List(s.db, &model.ConditionsT{
|
||||||
|
"tag LIKE ?": keyword,
|
||||||
|
"ORDER": "quote_num DESC",
|
||||||
|
}, 0, 6)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,361 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.TweetService = (*tweetServant)(nil)
|
||||||
|
_ core.TweetManageService = (*tweetManageServant)(nil)
|
||||||
|
_ core.TweetHelpService = (*tweetHelpServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type tweetServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type tweetManageServant struct {
|
||||||
|
cacheIndex core.CacheIndexService
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
type tweetHelpServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTweetService(db *gorm.DB) core.TweetService {
|
||||||
|
return &tweetServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTweetManageService(db *gorm.DB, cacheIndex core.CacheIndexService) core.TweetManageService {
|
||||||
|
return &tweetManageServant{
|
||||||
|
cacheIndex: cacheIndex,
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTweetHelpService(db *gorm.DB) core.TweetHelpService {
|
||||||
|
return &tweetHelpServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergePosts post数据整合
|
||||||
|
func (s *tweetHelpServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) {
|
||||||
|
postIds := make([]int64, 0, len(posts))
|
||||||
|
userIds := make([]int64, 0, len(posts))
|
||||||
|
for _, post := range posts {
|
||||||
|
postIds = append(postIds, post.ID)
|
||||||
|
userIds = append(userIds, post.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
postContents, err := s.getPostContentsByIDs(postIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := s.getUsersByIDs(userIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap := make(map[int64]*model.UserFormated, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
userMap[user.ID] = user.Format()
|
||||||
|
}
|
||||||
|
|
||||||
|
contentMap := make(map[int64][]*model.PostContentFormated, len(postContents))
|
||||||
|
for _, content := range postContents {
|
||||||
|
contentMap[content.PostID] = append(contentMap[content.PostID], content.Format())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据整合
|
||||||
|
postsFormated := make([]*model.PostFormated, 0, len(posts))
|
||||||
|
for _, post := range posts {
|
||||||
|
postFormated := post.Format()
|
||||||
|
postFormated.User = userMap[post.UserID]
|
||||||
|
postFormated.Contents = contentMap[post.ID]
|
||||||
|
postsFormated = append(postsFormated, postFormated)
|
||||||
|
}
|
||||||
|
return postsFormated, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevampPosts post数据整形修复
|
||||||
|
func (s *tweetHelpServant) RevampPosts(posts []*model.PostFormated) ([]*model.PostFormated, error) {
|
||||||
|
postIds := make([]int64, 0, len(posts))
|
||||||
|
userIds := make([]int64, 0, len(posts))
|
||||||
|
for _, post := range posts {
|
||||||
|
postIds = append(postIds, post.ID)
|
||||||
|
userIds = append(userIds, post.UserID)
|
||||||
|
}
|
||||||
|
|
||||||
|
postContents, err := s.getPostContentsByIDs(postIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users, err := s.getUsersByIDs(userIds)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
userMap := make(map[int64]*model.UserFormated, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
userMap[user.ID] = user.Format()
|
||||||
|
}
|
||||||
|
|
||||||
|
contentMap := make(map[int64][]*model.PostContentFormated, len(postContents))
|
||||||
|
for _, content := range postContents {
|
||||||
|
contentMap[content.PostID] = append(contentMap[content.PostID], content.Format())
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据整合
|
||||||
|
for _, post := range posts {
|
||||||
|
post.User = userMap[post.UserID]
|
||||||
|
post.Contents = contentMap[post.ID]
|
||||||
|
}
|
||||||
|
return posts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetHelpServant) getPostContentsByIDs(ids []int64) ([]*model.PostContent, error) {
|
||||||
|
return (&model.PostContent{}).List(s.db, &model.ConditionsT{
|
||||||
|
"post_id IN ?": ids,
|
||||||
|
"ORDER": "sort ASC",
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetHelpServant) getUsersByIDs(ids []int64) ([]*model.User, error) {
|
||||||
|
user := &model.User{}
|
||||||
|
|
||||||
|
return user.List(s.db, &model.ConditionsT{
|
||||||
|
"id IN ?": ids,
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) CreatePostCollection(postID, userID int64) (*model.PostCollection, error) {
|
||||||
|
collection := &model.PostCollection{
|
||||||
|
PostID: postID,
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) DeletePostCollection(p *model.PostCollection) error {
|
||||||
|
return p.Delete(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) CreatePostContent(content *model.PostContent) (*model.PostContent, error) {
|
||||||
|
return content.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) CreateAttachment(attachment *model.Attachment) (*model.Attachment, error) {
|
||||||
|
return attachment.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) CreatePost(post *model.Post) (*model.Post, error) {
|
||||||
|
post.LatestRepliedOn = time.Now().Unix()
|
||||||
|
p, err := post.Create(s.db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
s.cacheIndex.SendAction(core.IdxActCreatePost)
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) DeletePost(post *model.Post) error {
|
||||||
|
if err := post.Delete(s.db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.cacheIndex.SendAction(core.IdxActDeletePost)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) LockPost(post *model.Post) error {
|
||||||
|
post.IsLock = 1 - post.IsLock
|
||||||
|
return post.Update(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) StickPost(post *model.Post) error {
|
||||||
|
post.IsTop = 1 - post.IsTop
|
||||||
|
if err := post.Update(s.db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.cacheIndex.SendAction(core.IdxActStickPost)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) VisiblePost(post *model.Post, visibility model.PostVisibleT) error {
|
||||||
|
oldVisibility := post.Visibility
|
||||||
|
post.Visibility = visibility
|
||||||
|
// TODO: 这个判断是否可以不要呢
|
||||||
|
if oldVisibility == visibility {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// 私密推文 特殊处理
|
||||||
|
if visibility == model.PostVisitPrivate {
|
||||||
|
// 强制取消置顶
|
||||||
|
// TODO: 置顶推文用户是否有权设置成私密? 后续完善
|
||||||
|
post.IsTop = 0
|
||||||
|
}
|
||||||
|
db := s.db.Begin()
|
||||||
|
err := post.Update(db)
|
||||||
|
if err != nil {
|
||||||
|
db.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// tag处理
|
||||||
|
tags := strings.Split(post.Tags, ",")
|
||||||
|
for _, t := range tags {
|
||||||
|
tag := &model.Tag{
|
||||||
|
Tag: t,
|
||||||
|
}
|
||||||
|
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
|
||||||
|
if oldVisibility == model.PostVisitPrivate {
|
||||||
|
// 从私密转为非私密才需要重新创建tag
|
||||||
|
createTag(db, tag)
|
||||||
|
} else if visibility == model.PostVisitPrivate {
|
||||||
|
// 从非私密转为私密才需要删除tag
|
||||||
|
deleteTag(db, tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db.Commit()
|
||||||
|
s.cacheIndex.SendAction(core.IdxActVisiblePost)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) UpdatePost(post *model.Post) error {
|
||||||
|
if err := post.Update(s.db); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.cacheIndex.SendAction(core.IdxActUpdatePost)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) CreatePostStar(postID, userID int64) (*model.PostStar, error) {
|
||||||
|
star := &model.PostStar{
|
||||||
|
PostID: postID,
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return star.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetManageServant) DeletePostStar(p *model.PostStar) error {
|
||||||
|
return p.Delete(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPostByID(id int64) (*model.Post, error) {
|
||||||
|
post := &model.Post{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return post.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPosts(conditions *model.ConditionsT, offset, limit int) ([]*model.Post, error) {
|
||||||
|
return (&model.Post{}).List(s.db, conditions, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPostCount(conditions *model.ConditionsT) (int64, error) {
|
||||||
|
return (&model.Post{}).Count(s.db, conditions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostStar(postID, userID int64) (*model.PostStar, error) {
|
||||||
|
star := &model.PostStar{
|
||||||
|
PostID: postID,
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return star.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostStars(userID int64, offset, limit int) ([]*model.PostStar, error) {
|
||||||
|
star := &model.PostStar{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return star.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": s.db.NamingStrategy.TableName("PostStar") + ".id DESC",
|
||||||
|
}, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostStarCount(userID int64) (int64, error) {
|
||||||
|
star := &model.PostStar{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return star.Count(s.db, &model.ConditionsT{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostCollection(postID, userID int64) (*model.PostCollection, error) {
|
||||||
|
star := &model.PostCollection{
|
||||||
|
PostID: postID,
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return star.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostCollections(userID int64, offset, limit int) ([]*model.PostCollection, error) {
|
||||||
|
collection := &model.PostCollection{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": s.db.NamingStrategy.TableName("PostCollection") + ".id DESC",
|
||||||
|
}, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserPostCollectionCount(userID int64) (int64, error) {
|
||||||
|
collection := &model.PostCollection{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return collection.Count(s.db, &model.ConditionsT{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserWalletBills(userID int64, offset, limit int) ([]*model.WalletStatement, error) {
|
||||||
|
statement := &model.WalletStatement{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return statement.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": "id DESC",
|
||||||
|
}, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetUserWalletBillCount(userID int64) (int64, error) {
|
||||||
|
statement := &model.WalletStatement{
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
return statement.Count(s.db, &model.ConditionsT{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPostAttatchmentBill(postID, userID int64) (*model.PostAttachmentBill, error) {
|
||||||
|
bill := &model.PostAttachmentBill{
|
||||||
|
PostID: postID,
|
||||||
|
UserID: userID,
|
||||||
|
}
|
||||||
|
|
||||||
|
return bill.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPostContentsByIDs(ids []int64) ([]*model.PostContent, error) {
|
||||||
|
return (&model.PostContent{}).List(s.db, &model.ConditionsT{
|
||||||
|
"post_id IN ?": ids,
|
||||||
|
"ORDER": "sort ASC",
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *tweetServant) GetPostContentByID(id int64) (*model.PostContent, error) {
|
||||||
|
return (&model.PostContent{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}).Get(s.db)
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ core.UserManageService = (*userManageServant)(nil)
|
||||||
|
)
|
||||||
|
|
||||||
|
type userManageServant struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUserManageService(db *gorm.DB) core.UserManageService {
|
||||||
|
return &userManageServant{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetUserByID(id int64) (*model.User, error) {
|
||||||
|
user := &model.User{
|
||||||
|
Model: &model.Model{
|
||||||
|
ID: id,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return user.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetUserByUsername(username string) (*model.User, error) {
|
||||||
|
user := &model.User{
|
||||||
|
Username: username,
|
||||||
|
}
|
||||||
|
return user.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetUserByPhone(phone string) (*model.User, error) {
|
||||||
|
user := &model.User{
|
||||||
|
Phone: phone,
|
||||||
|
}
|
||||||
|
return user.Get(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetUsersByIDs(ids []int64) ([]*model.User, error) {
|
||||||
|
user := &model.User{}
|
||||||
|
return user.List(s.db, &model.ConditionsT{
|
||||||
|
"id IN ?": ids,
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetUsersByKeyword(keyword string) ([]*model.User, error) {
|
||||||
|
user := &model.User{}
|
||||||
|
keyword = strings.Trim(keyword, " ") + "%"
|
||||||
|
if keyword == "%" {
|
||||||
|
return user.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": "id ASC",
|
||||||
|
}, 0, 6)
|
||||||
|
} else {
|
||||||
|
return user.List(s.db, &model.ConditionsT{
|
||||||
|
"username LIKE ?": keyword,
|
||||||
|
}, 0, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) GetTagsByKeyword(keyword string) ([]*model.Tag, error) {
|
||||||
|
tag := &model.Tag{}
|
||||||
|
keyword = "%" + strings.Trim(keyword, " ") + "%"
|
||||||
|
if keyword == "%%" {
|
||||||
|
return tag.List(s.db, &model.ConditionsT{
|
||||||
|
"ORDER": "quote_num DESC",
|
||||||
|
}, 0, 6)
|
||||||
|
} else {
|
||||||
|
return tag.List(s.db, &model.ConditionsT{
|
||||||
|
"tag LIKE ?": keyword,
|
||||||
|
"ORDER": "quote_num DESC",
|
||||||
|
}, 0, 6)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) CreateUser(user *model.User) (*model.User, error) {
|
||||||
|
return user.Create(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) UpdateUser(user *model.User) error {
|
||||||
|
return user.Update(s.db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *userManageServant) IsFriend(userId int64, friendId int64) bool {
|
||||||
|
// just true now
|
||||||
|
return true
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package jinzhu
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createTag(db *gorm.DB, tag *model.Tag) (*model.Tag, error) {
|
||||||
|
t, err := tag.Get(db)
|
||||||
|
if err != nil {
|
||||||
|
tag.QuoteNum = 1
|
||||||
|
return tag.Create(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新
|
||||||
|
t.QuoteNum++
|
||||||
|
err = t.Update(db)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteTag(db *gorm.DB, tag *model.Tag) error {
|
||||||
|
tag, err := tag.Get(db)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tag.QuoteNum--
|
||||||
|
return tag.Update(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据IDs获取用户列表
|
||||||
|
func getUsersByIDs(db *gorm.DB, ids []int64) ([]*model.User, error) {
|
||||||
|
user := &model.User{}
|
||||||
|
return user.List(db, &model.ConditionsT{
|
||||||
|
"id IN ?": ids,
|
||||||
|
}, 0, 0)
|
||||||
|
}
|
@ -1,46 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import "github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
|
|
||||||
func (d *dataServant) CreateMessage(msg *model.Message) (*model.Message, error) {
|
|
||||||
return msg.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUnreadCount(userID int64) (int64, error) {
|
|
||||||
return (&model.Message{}).Count(d.engine, &model.ConditionsT{
|
|
||||||
"receiver_user_id": userID,
|
|
||||||
"is_read": model.MSG_UNREAD,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetMessageByID(id int64) (*model.Message, error) {
|
|
||||||
return (&model.Message{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}).Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) ReadMessage(message *model.Message) error {
|
|
||||||
message.IsRead = 1
|
|
||||||
return message.Update(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetMessages(conditions *model.ConditionsT, offset, limit int) ([]*model.MessageFormated, error) {
|
|
||||||
messages, err := (&model.Message{}).List(d.engine, conditions, offset, limit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
mfs := []*model.MessageFormated{}
|
|
||||||
for _, message := range messages {
|
|
||||||
mf := message.Format()
|
|
||||||
mfs = append(mfs, mf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mfs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetMessageCount(conditions *model.ConditionsT) (int64, error) {
|
|
||||||
return (&model.Message{}).Count(d.engine, conditions)
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ core.ObjectStorageService = (*aliossServant)(nil)
|
|
||||||
_ core.ObjectStorageService = (*minioServant)(nil)
|
|
||||||
_ core.ObjectStorageService = (*s3Servant)(nil)
|
|
||||||
_ core.ObjectStorageService = (*localossServant)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type localossServant struct {
|
|
||||||
savePath string
|
|
||||||
domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
type aliossServant struct {
|
|
||||||
bucket *oss.Bucket
|
|
||||||
domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
type minioServant struct {
|
|
||||||
client *minio.Client
|
|
||||||
bucket string
|
|
||||||
domain string
|
|
||||||
}
|
|
||||||
|
|
||||||
type s3Servant = minioServant
|
|
||||||
|
|
||||||
func NewObjectStorageService() (oss core.ObjectStorageService) {
|
|
||||||
var v versionInfo
|
|
||||||
if conf.CfgIf("AliOSS") {
|
|
||||||
oss, v = newAliossServent()
|
|
||||||
} else if conf.CfgIf("MinIO") {
|
|
||||||
oss, v = newMinioServeant()
|
|
||||||
} else if conf.CfgIf("S3") {
|
|
||||||
oss, v = newS3Servent()
|
|
||||||
logrus.Infof("use S3 as object storage by version %s", v.version())
|
|
||||||
return
|
|
||||||
} else if conf.CfgIf("LocalOSS") {
|
|
||||||
oss, v = newLocalossServent()
|
|
||||||
} else {
|
|
||||||
// default use AliOSS as object storage service
|
|
||||||
oss, v = newAliossServent()
|
|
||||||
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())
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
func newS3Servent() (*s3Servant, versionInfo) {
|
|
||||||
// Initialize s3 client object use minio-go.
|
|
||||||
client, err := minio.New(conf.S3Setting.Endpoint, &minio.Options{
|
|
||||||
Creds: credentials.NewStaticV4(conf.S3Setting.AccessKey, conf.S3Setting.SecretKey, ""),
|
|
||||||
Secure: conf.S3Setting.Secure,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
logrus.Fatalf("s3.New err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := &s3Servant{
|
|
||||||
client: client,
|
|
||||||
bucket: conf.MinIOSetting.Bucket,
|
|
||||||
domain: getOssDomain(),
|
|
||||||
}
|
|
||||||
return obj, obj
|
|
||||||
}
|
|
@ -1,286 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *dataServant) CreatePost(post *model.Post) (*model.Post, error) {
|
|
||||||
post.LatestRepliedOn = time.Now().Unix()
|
|
||||||
p, err := post.Create(d.engine)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
d.SendAction(core.IdxActCreatePost)
|
|
||||||
return p, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeletePost(post *model.Post) error {
|
|
||||||
if err := post.Delete(d.engine); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.SendAction(core.IdxActDeletePost)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) LockPost(post *model.Post) error {
|
|
||||||
post.IsLock = 1 - post.IsLock
|
|
||||||
return post.Update(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) StickPost(post *model.Post) error {
|
|
||||||
post.IsTop = 1 - post.IsTop
|
|
||||||
if err := post.Update(d.engine); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.SendAction(core.IdxActStickPost)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) VisiblePost(post *model.Post, visibility model.PostVisibleT) error {
|
|
||||||
oldVisibility := post.Visibility
|
|
||||||
post.Visibility = visibility
|
|
||||||
// TODO: 这个判断是否可以不要呢
|
|
||||||
if oldVisibility == visibility {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// 私密推文 特殊处理
|
|
||||||
if visibility == model.PostVisitPrivate {
|
|
||||||
// 强制取消置顶
|
|
||||||
// TODO: 置顶推文用户是否有权设置成私密? 后续完善
|
|
||||||
post.IsTop = 0
|
|
||||||
}
|
|
||||||
db := d.engine.Begin()
|
|
||||||
err := post.Update(db)
|
|
||||||
if err != nil {
|
|
||||||
db.Rollback()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// tag处理
|
|
||||||
tags := strings.Split(post.Tags, ",")
|
|
||||||
for _, t := range tags {
|
|
||||||
tag := &model.Tag{
|
|
||||||
Tag: t,
|
|
||||||
}
|
|
||||||
// TODO: 暂时宽松不处理错误,这里或许可以有优化,后续完善
|
|
||||||
if oldVisibility == model.PostVisitPrivate {
|
|
||||||
// 从私密转为非私密才需要重新创建tag
|
|
||||||
d.createTag(db, tag)
|
|
||||||
} else if visibility == model.PostVisitPrivate {
|
|
||||||
// 从非私密转为私密才需要删除tag
|
|
||||||
d.deleteTag(db, tag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.Commit()
|
|
||||||
d.SendAction(core.IdxActVisiblePost)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPostByID(id int64) (*model.Post, error) {
|
|
||||||
post := &model.Post{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return post.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPosts(conditions *model.ConditionsT, offset, limit int) ([]*model.Post, error) {
|
|
||||||
return (&model.Post{}).List(d.engine, conditions, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPostCount(conditions *model.ConditionsT) (int64, error) {
|
|
||||||
return (&model.Post{}).Count(d.engine, conditions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) UpdatePost(post *model.Post) error {
|
|
||||||
if err := post.Update(d.engine); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
d.SendAction(core.IdxActUpdatePost)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostStar(postID, userID int64) (*model.PostStar, error) {
|
|
||||||
star := &model.PostStar{
|
|
||||||
PostID: postID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return star.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostStars(userID int64, offset, limit int) ([]*model.PostStar, error) {
|
|
||||||
star := &model.PostStar{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return star.List(d.engine, &model.ConditionsT{
|
|
||||||
"ORDER": d.engine.NamingStrategy.TableName("PostStar") + ".id DESC",
|
|
||||||
}, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostStarCount(userID int64) (int64, error) {
|
|
||||||
star := &model.PostStar{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
return star.Count(d.engine, &model.ConditionsT{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) CreatePostStar(postID, userID int64) (*model.PostStar, error) {
|
|
||||||
star := &model.PostStar{
|
|
||||||
PostID: postID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return star.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeletePostStar(p *model.PostStar) error {
|
|
||||||
return p.Delete(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostCollection(postID, userID int64) (*model.PostCollection, error) {
|
|
||||||
star := &model.PostCollection{
|
|
||||||
PostID: postID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return star.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostCollections(userID int64, offset, limit int) ([]*model.PostCollection, error) {
|
|
||||||
collection := &model.PostCollection{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection.List(d.engine, &model.ConditionsT{
|
|
||||||
"ORDER": d.engine.NamingStrategy.TableName("PostCollection") + ".id DESC",
|
|
||||||
}, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserPostCollectionCount(userID int64) (int64, error) {
|
|
||||||
collection := &model.PostCollection{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
return collection.Count(d.engine, &model.ConditionsT{})
|
|
||||||
}
|
|
||||||
func (d *dataServant) GetUserWalletBills(userID int64, offset, limit int) ([]*model.WalletStatement, error) {
|
|
||||||
statement := &model.WalletStatement{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return statement.List(d.engine, &model.ConditionsT{
|
|
||||||
"ORDER": "id DESC",
|
|
||||||
}, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetUserWalletBillCount(userID int64) (int64, error) {
|
|
||||||
statement := &model.WalletStatement{
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
return statement.Count(d.engine, &model.ConditionsT{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) CreatePostCollection(postID, userID int64) (*model.PostCollection, error) {
|
|
||||||
collection := &model.PostCollection{
|
|
||||||
PostID: postID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return collection.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeletePostCollection(p *model.PostCollection) error {
|
|
||||||
return p.Delete(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPostAttatchmentBill(postID, userID int64) (*model.PostAttachmentBill, error) {
|
|
||||||
bill := &model.PostAttachmentBill{
|
|
||||||
PostID: postID,
|
|
||||||
UserID: userID,
|
|
||||||
}
|
|
||||||
|
|
||||||
return bill.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MergePosts post数据整合
|
|
||||||
func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) {
|
|
||||||
postIds := make([]int64, 0, len(posts))
|
|
||||||
userIds := make([]int64, 0, len(posts))
|
|
||||||
for _, post := range posts {
|
|
||||||
postIds = append(postIds, post.ID)
|
|
||||||
userIds = append(userIds, post.UserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
postContents, err := d.GetPostContentsByIDs(postIds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := d.GetUsersByIDs(userIds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userMap := make(map[int64]*model.UserFormated, len(users))
|
|
||||||
for _, user := range users {
|
|
||||||
userMap[user.ID] = user.Format()
|
|
||||||
}
|
|
||||||
|
|
||||||
contentMap := make(map[int64][]*model.PostContentFormated, len(postContents))
|
|
||||||
for _, content := range postContents {
|
|
||||||
contentMap[content.PostID] = append(contentMap[content.PostID], content.Format())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据整合
|
|
||||||
postsFormated := make([]*model.PostFormated, 0, len(posts))
|
|
||||||
for _, post := range posts {
|
|
||||||
postFormated := post.Format()
|
|
||||||
postFormated.User = userMap[post.UserID]
|
|
||||||
postFormated.Contents = contentMap[post.ID]
|
|
||||||
postsFormated = append(postsFormated, postFormated)
|
|
||||||
}
|
|
||||||
return postsFormated, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RevampPosts post数据整形修复
|
|
||||||
func (d *dataServant) RevampPosts(posts []*model.PostFormated) ([]*model.PostFormated, error) {
|
|
||||||
postIds := make([]int64, 0, len(posts))
|
|
||||||
userIds := make([]int64, 0, len(posts))
|
|
||||||
for _, post := range posts {
|
|
||||||
postIds = append(postIds, post.ID)
|
|
||||||
userIds = append(userIds, post.UserID)
|
|
||||||
}
|
|
||||||
|
|
||||||
postContents, err := d.GetPostContentsByIDs(postIds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
users, err := d.GetUsersByIDs(userIds)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userMap := make(map[int64]*model.UserFormated, len(users))
|
|
||||||
for _, user := range users {
|
|
||||||
userMap[user.ID] = user.Format()
|
|
||||||
}
|
|
||||||
|
|
||||||
contentMap := make(map[int64][]*model.PostContentFormated, len(postContents))
|
|
||||||
for _, content := range postContents {
|
|
||||||
contentMap[content.PostID] = append(contentMap[content.PostID], content.Format())
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据整合
|
|
||||||
for _, post := range posts {
|
|
||||||
post.User = userMap[post.UserID]
|
|
||||||
post.Contents = contentMap[post.ID]
|
|
||||||
}
|
|
||||||
return posts, nil
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import "github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
|
|
||||||
func (d *dataServant) CreatePostContent(content *model.PostContent) (*model.PostContent, error) {
|
|
||||||
return content.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPostContentsByIDs(ids []int64) ([]*model.PostContent, error) {
|
|
||||||
return (&model.PostContent{}).List(d.engine, &model.ConditionsT{
|
|
||||||
"post_id IN ?": ids,
|
|
||||||
"ORDER": "sort ASC",
|
|
||||||
}, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetPostContentByID(id int64) (*model.PostContent, error) {
|
|
||||||
return (&model.PostContent{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}).Get(d.engine)
|
|
||||||
}
|
|
@ -0,0 +1,19 @@
|
|||||||
|
// Core service implement base sqlx+mysql. All sub-service
|
||||||
|
// will declare here and provide initial function.
|
||||||
|
|
||||||
|
package sakila
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDataService() (core.DataService, core.VersionInfo) {
|
||||||
|
logrus.Fatal("not support now")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthorizationManageService() core.AuthorizationManageService {
|
||||||
|
logrus.Fatal("not support now")
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,86 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/meilisearch/meilisearch-go"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
|
||||||
"github.com/rocboss/paopao-ce/pkg/zinc"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ core.TweetSearchService = (*zincTweetSearchServant)(nil)
|
|
||||||
_ core.TweetSearchService = (*bridgeTweetSearchServant)(nil)
|
|
||||||
)
|
|
||||||
|
|
||||||
type documents struct {
|
|
||||||
primaryKey []string
|
|
||||||
docItems core.DocItems
|
|
||||||
identifiers []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type bridgeTweetSearchServant struct {
|
|
||||||
ts core.TweetSearchService
|
|
||||||
updateDocsCh chan *documents
|
|
||||||
}
|
|
||||||
|
|
||||||
type tweetSearchFilter struct {
|
|
||||||
ams core.AuthorizationManageService
|
|
||||||
}
|
|
||||||
|
|
||||||
type zincTweetSearchServant struct {
|
|
||||||
tweetSearchFilter
|
|
||||||
|
|
||||||
indexName string
|
|
||||||
client *zinc.ZincClient
|
|
||||||
publicFilter string
|
|
||||||
privateFilter string
|
|
||||||
friendFilter string
|
|
||||||
}
|
|
||||||
|
|
||||||
type meiliTweetSearchServant struct {
|
|
||||||
tweetSearchFilter
|
|
||||||
|
|
||||||
client *meilisearch.Client
|
|
||||||
index *meilisearch.Index
|
|
||||||
publicFilter string
|
|
||||||
privateFilter string
|
|
||||||
friendFilter string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTweetSearchService() core.TweetSearchService {
|
|
||||||
bts := &bridgeTweetSearchServant{}
|
|
||||||
|
|
||||||
capacity := conf.TweetSearchSetting.MaxUpdateQPS
|
|
||||||
if capacity < 10 {
|
|
||||||
capacity = 10
|
|
||||||
} else if capacity > 10000 {
|
|
||||||
capacity = 10000
|
|
||||||
}
|
|
||||||
bts.updateDocsCh = make(chan *documents, capacity)
|
|
||||||
|
|
||||||
var v versionInfo
|
|
||||||
if conf.CfgIf("Zinc") {
|
|
||||||
bts.ts, v = newZincTweetSearchServant()
|
|
||||||
} else if conf.CfgIf("Meili") {
|
|
||||||
bts.ts, v = newMeiliTweetSearchServant()
|
|
||||||
} else {
|
|
||||||
// default use Zinc as tweet search service
|
|
||||||
bts.ts, v = newZincTweetSearchServant()
|
|
||||||
}
|
|
||||||
logrus.Infof("use %s as tweet search serice by version %s", v.name(), v.version())
|
|
||||||
|
|
||||||
numWorker := conf.TweetSearchSetting.MinWorker
|
|
||||||
if numWorker < 5 {
|
|
||||||
numWorker = 5
|
|
||||||
} else if numWorker > 1000 {
|
|
||||||
numWorker = 1000
|
|
||||||
}
|
|
||||||
logrus.Debugf("use %d backend worker to update documents to search engine", numWorker)
|
|
||||||
// 启动文档更新器
|
|
||||||
for ; numWorker > 0; numWorker-- {
|
|
||||||
go bts.startUpdateDocs()
|
|
||||||
}
|
|
||||||
|
|
||||||
return bts
|
|
||||||
}
|
|
@ -1,58 +1,36 @@
|
|||||||
package dao
|
package search
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/meilisearch/meilisearch-go"
|
"github.com/meilisearch/meilisearch-go"
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/core"
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
"github.com/rocboss/paopao-ce/pkg/json"
|
"github.com/rocboss/paopao-ce/pkg/json"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMeiliTweetSearchServant() (*meiliTweetSearchServant, versionInfo) {
|
var (
|
||||||
s := conf.MeiliSetting
|
_ core.TweetSearchService = (*meiliTweetSearchServant)(nil)
|
||||||
client := meilisearch.NewClient(meilisearch.ClientConfig{
|
_ core.VersionInfo = (*meiliTweetSearchServant)(nil)
|
||||||
Host: s.Endpoint(),
|
)
|
||||||
APIKey: s.ApiKey,
|
|
||||||
})
|
type meiliTweetSearchServant struct {
|
||||||
|
tweetSearchFilter
|
||||||
if _, err := client.Index(s.Index).FetchInfo(); err != nil {
|
|
||||||
logrus.Debugf("create index because fetch index info error: %v", err)
|
client *meilisearch.Client
|
||||||
client.CreateIndex(&meilisearch.IndexConfig{
|
index *meilisearch.Index
|
||||||
Uid: s.Index,
|
publicFilter string
|
||||||
PrimaryKey: "id",
|
privateFilter string
|
||||||
})
|
friendFilter string
|
||||||
searchableAttributes := []string{"content", "tags"}
|
|
||||||
sortableAttributes := []string{"is_top", "latest_replied_on"}
|
|
||||||
filterableAttributes := []string{"tags", "visibility", "user_id"}
|
|
||||||
|
|
||||||
index := client.Index(s.Index)
|
|
||||||
index.UpdateSearchableAttributes(&searchableAttributes)
|
|
||||||
index.UpdateSortableAttributes(&sortableAttributes)
|
|
||||||
index.UpdateFilterableAttributes(&filterableAttributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
obj := &meiliTweetSearchServant{
|
|
||||||
tweetSearchFilter: tweetSearchFilter{
|
|
||||||
ams: newAuthorizationManageService(),
|
|
||||||
},
|
|
||||||
client: client,
|
|
||||||
index: client.Index(s.Index),
|
|
||||||
publicFilter: fmt.Sprintf("visibility=%d", model.PostVisitPublic),
|
|
||||||
privateFilter: fmt.Sprintf("visibility=%d AND user_id=", model.PostVisitPrivate),
|
|
||||||
friendFilter: fmt.Sprintf("visibility=%d", model.PostVisitFriend),
|
|
||||||
}
|
|
||||||
return obj, obj
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *meiliTweetSearchServant) name() string {
|
func (s *meiliTweetSearchServant) Name() string {
|
||||||
return "Meili"
|
return "Meili"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *meiliTweetSearchServant) version() *semver.Version {
|
func (s *meiliTweetSearchServant) Version() *semver.Version {
|
||||||
return semver.MustParse("v0.2.0")
|
return semver.MustParse("v0.2.0")
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,92 @@
|
|||||||
|
package search
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/meilisearch/meilisearch-go"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
"github.com/rocboss/paopao-ce/pkg/zinc"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMeiliTweetSearchService(ams core.AuthorizationManageService) (core.TweetSearchService, core.VersionInfo) {
|
||||||
|
s := conf.MeiliSetting
|
||||||
|
client := meilisearch.NewClient(meilisearch.ClientConfig{
|
||||||
|
Host: s.Endpoint(),
|
||||||
|
APIKey: s.ApiKey,
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := client.Index(s.Index).FetchInfo(); err != nil {
|
||||||
|
logrus.Debugf("create index because fetch index info error: %v", err)
|
||||||
|
client.CreateIndex(&meilisearch.IndexConfig{
|
||||||
|
Uid: s.Index,
|
||||||
|
PrimaryKey: "id",
|
||||||
|
})
|
||||||
|
searchableAttributes := []string{"content", "tags"}
|
||||||
|
sortableAttributes := []string{"is_top", "latest_replied_on"}
|
||||||
|
filterableAttributes := []string{"tags", "visibility", "user_id"}
|
||||||
|
|
||||||
|
index := client.Index(s.Index)
|
||||||
|
index.UpdateSearchableAttributes(&searchableAttributes)
|
||||||
|
index.UpdateSortableAttributes(&sortableAttributes)
|
||||||
|
index.UpdateFilterableAttributes(&filterableAttributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
mts := &meiliTweetSearchServant{
|
||||||
|
tweetSearchFilter: tweetSearchFilter{
|
||||||
|
ams: ams,
|
||||||
|
},
|
||||||
|
client: client,
|
||||||
|
index: client.Index(s.Index),
|
||||||
|
publicFilter: fmt.Sprintf("visibility=%d", model.PostVisitPublic),
|
||||||
|
privateFilter: fmt.Sprintf("visibility=%d AND user_id=", model.PostVisitPrivate),
|
||||||
|
friendFilter: fmt.Sprintf("visibility=%d", model.PostVisitFriend),
|
||||||
|
}
|
||||||
|
return mts, mts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZincTweetSearchService(ams core.AuthorizationManageService) (core.TweetSearchService, core.VersionInfo) {
|
||||||
|
s := conf.ZincSetting
|
||||||
|
zts := &zincTweetSearchServant{
|
||||||
|
tweetSearchFilter: tweetSearchFilter{
|
||||||
|
ams: ams,
|
||||||
|
},
|
||||||
|
indexName: s.Index,
|
||||||
|
client: zinc.NewClient(s),
|
||||||
|
publicFilter: fmt.Sprintf("visibility:%d", model.PostVisitPublic),
|
||||||
|
privateFilter: fmt.Sprintf("visibility:%d AND user_id:%%d", model.PostVisitPrivate),
|
||||||
|
friendFilter: fmt.Sprintf("visibility:%d", model.PostVisitFriend),
|
||||||
|
}
|
||||||
|
zts.createIndex()
|
||||||
|
|
||||||
|
return zts, zts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBridgeTweetSearchService(ts core.TweetSearchService) core.TweetSearchService {
|
||||||
|
capacity := conf.TweetSearchSetting.MaxUpdateQPS
|
||||||
|
if capacity < 10 {
|
||||||
|
capacity = 10
|
||||||
|
} else if capacity > 10000 {
|
||||||
|
capacity = 10000
|
||||||
|
}
|
||||||
|
bts := &bridgeTweetSearchServant{
|
||||||
|
ts: ts,
|
||||||
|
updateDocsCh: make(chan *documents, capacity),
|
||||||
|
}
|
||||||
|
|
||||||
|
numWorker := conf.TweetSearchSetting.MinWorker
|
||||||
|
if numWorker < 5 {
|
||||||
|
numWorker = 5
|
||||||
|
} else if numWorker > 1000 {
|
||||||
|
numWorker = 1000
|
||||||
|
}
|
||||||
|
logrus.Debugf("use %d backend worker to update documents to search engine", numWorker)
|
||||||
|
// 启动文档更新器
|
||||||
|
for ; numWorker > 0; numWorker-- {
|
||||||
|
go bts.startUpdateDocs()
|
||||||
|
}
|
||||||
|
|
||||||
|
return bts
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
package security
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type attachmentCheckServant struct {
|
||||||
|
domain string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *attachmentCheckServant) CheckAttachment(uri string) error {
|
||||||
|
if strings.Index(uri, s.domain) != 0 {
|
||||||
|
return fmt.Errorf("附件非本站资源")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAttachmentCheckService() core.AttachmentCheckService {
|
||||||
|
return &attachmentCheckServant{
|
||||||
|
domain: conf.GetOssDomain(),
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
// Core service implement base sqlx+postgresql. All sub-service
|
||||||
|
// will declare here and provide initial function.
|
||||||
|
|
||||||
|
package slonik
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDataService() (core.DataService, core.VersionInfo) {
|
||||||
|
logrus.Fatal("not support now")
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthorizationManageService() core.AuthorizationManageService {
|
||||||
|
logrus.Fatal("not support now")
|
||||||
|
return nil
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/aliyun/aliyun-oss-go-sdk/oss"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/conf"
|
||||||
|
"github.com/rocboss/paopao-ce/internal/core"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewAliossService() (core.ObjectStorageService, core.VersionInfo) {
|
||||||
|
client, err := oss.New(conf.AliOSSSetting.Endpoint, conf.AliOSSSetting.AccessKeyID, conf.AliOSSSetting.AccessKeySecret)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("alioss.New err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bucket, err := client.Bucket(conf.AliOSSSetting.Bucket)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("client.Bucket err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &aliossServant{
|
||||||
|
bucket: bucket,
|
||||||
|
domain: conf.GetOssDomain(),
|
||||||
|
}
|
||||||
|
return obj, obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLocalossService() (core.ObjectStorageService, core.VersionInfo) {
|
||||||
|
savePath, err := filepath.Abs(conf.LocalOSSSetting.SavePath)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("get localOSS save path err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &localossServant{
|
||||||
|
savePath: savePath + "/" + conf.LocalOSSSetting.Bucket + "/",
|
||||||
|
domain: conf.GetOssDomain(),
|
||||||
|
}
|
||||||
|
return obj, obj
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMinioService() (core.ObjectStorageService, core.VersionInfo) {
|
||||||
|
// Initialize minio client object.
|
||||||
|
client, err := minio.New(conf.MinIOSetting.Endpoint, &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(conf.MinIOSetting.AccessKey, conf.MinIOSetting.SecretKey, ""),
|
||||||
|
Secure: conf.MinIOSetting.Secure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("minio.New err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ms := &minioServant{
|
||||||
|
client: client,
|
||||||
|
bucket: conf.MinIOSetting.Bucket,
|
||||||
|
domain: conf.GetOssDomain(),
|
||||||
|
}
|
||||||
|
return ms, ms
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewS3Service() (core.ObjectStorageService, core.VersionInfo) {
|
||||||
|
// Initialize s3 client object use minio-go.
|
||||||
|
client, err := minio.New(conf.S3Setting.Endpoint, &minio.Options{
|
||||||
|
Creds: credentials.NewStaticV4(conf.S3Setting.AccessKey, conf.S3Setting.SecretKey, ""),
|
||||||
|
Secure: conf.S3Setting.Secure,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logrus.Fatalf("s3.New err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj := &s3Servant{
|
||||||
|
client: client,
|
||||||
|
bucket: conf.MinIOSetting.Bucket,
|
||||||
|
domain: conf.GetOssDomain(),
|
||||||
|
}
|
||||||
|
return obj, obj
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (d *dataServant) CreateTag(tag *model.Tag) (*model.Tag, error) {
|
|
||||||
return d.createTag(d.engine, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) DeleteTag(tag *model.Tag) error {
|
|
||||||
return d.deleteTag(d.engine, tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) GetTags(conditions *model.ConditionsT, offset, limit int) ([]*model.Tag, error) {
|
|
||||||
return (&model.Tag{}).List(d.engine, conditions, offset, limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) createTag(db *gorm.DB, tag *model.Tag) (*model.Tag, error) {
|
|
||||||
t, err := tag.Get(d.engine)
|
|
||||||
if err != nil {
|
|
||||||
tag.QuoteNum = 1
|
|
||||||
return tag.Create(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
t.QuoteNum++
|
|
||||||
err = t.Update(db)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) deleteTag(db *gorm.DB, tag *model.Tag) error {
|
|
||||||
tag, err := tag.Get(db)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
tag.QuoteNum--
|
|
||||||
return tag.Update(db)
|
|
||||||
}
|
|
@ -1,165 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
"github.com/rocboss/paopao-ce/internal/model"
|
|
||||||
"github.com/rocboss/paopao-ce/pkg/json"
|
|
||||||
"gopkg.in/resty.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type JuhePhoneCaptchaRsp struct {
|
|
||||||
ErrorCode int `json:"error_code"`
|
|
||||||
Reason string `json:"reason"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据用户ID获取用户
|
|
||||||
func (d *dataServant) GetUserByID(id int64) (*model.User, error) {
|
|
||||||
user := &model.User{
|
|
||||||
Model: &model.Model{
|
|
||||||
ID: id,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据用户名获取用户
|
|
||||||
func (d *dataServant) GetUserByUsername(username string) (*model.User, error) {
|
|
||||||
user := &model.User{
|
|
||||||
Username: username,
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据手机号获取用户
|
|
||||||
func (d *dataServant) GetUserByPhone(phone string) (*model.User, error) {
|
|
||||||
user := &model.User{
|
|
||||||
Phone: phone,
|
|
||||||
}
|
|
||||||
|
|
||||||
return user.Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据IDs获取用户列表
|
|
||||||
func (d *dataServant) GetUsersByIDs(ids []int64) ([]*model.User, error) {
|
|
||||||
user := &model.User{}
|
|
||||||
|
|
||||||
return user.List(d.engine, &model.ConditionsT{
|
|
||||||
"id IN ?": ids,
|
|
||||||
}, 0, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据关键词模糊获取用户列表
|
|
||||||
func (d *dataServant) GetUsersByKeyword(keyword string) ([]*model.User, error) {
|
|
||||||
user := &model.User{}
|
|
||||||
|
|
||||||
keyword = strings.Trim(keyword, " ") + "%"
|
|
||||||
if keyword == "%" {
|
|
||||||
return user.List(d.engine, &model.ConditionsT{
|
|
||||||
"ORDER": "id ASC",
|
|
||||||
}, 0, 6)
|
|
||||||
} else {
|
|
||||||
return user.List(d.engine, &model.ConditionsT{
|
|
||||||
"username LIKE ?": keyword,
|
|
||||||
}, 0, 6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据关键词模糊获取用户列表
|
|
||||||
func (d *dataServant) GetTagsByKeyword(keyword string) ([]*model.Tag, error) {
|
|
||||||
tag := &model.Tag{}
|
|
||||||
|
|
||||||
keyword = "%" + strings.Trim(keyword, " ") + "%"
|
|
||||||
if keyword == "%%" {
|
|
||||||
return tag.List(d.engine, &model.ConditionsT{
|
|
||||||
"ORDER": "quote_num DESC",
|
|
||||||
}, 0, 6)
|
|
||||||
} else {
|
|
||||||
return tag.List(d.engine, &model.ConditionsT{
|
|
||||||
"tag LIKE ?": keyword,
|
|
||||||
"ORDER": "quote_num DESC",
|
|
||||||
}, 0, 6)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建用户
|
|
||||||
func (d *dataServant) CreateUser(user *model.User) (*model.User, error) {
|
|
||||||
return user.Create(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新用户
|
|
||||||
func (d *dataServant) UpdateUser(user *model.User) error {
|
|
||||||
return user.Update(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取最新短信验证码
|
|
||||||
func (d *dataServant) GetLatestPhoneCaptcha(phone string) (*model.Captcha, error) {
|
|
||||||
return (&model.Captcha{
|
|
||||||
Phone: phone,
|
|
||||||
}).Get(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新短信验证码
|
|
||||||
func (d *dataServant) UsePhoneCaptcha(captcha *model.Captcha) error {
|
|
||||||
captcha.UseTimes++
|
|
||||||
return captcha.Update(d.engine)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送短信验证码
|
|
||||||
func (d *dataServant) SendPhoneCaptcha(phone string) error {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
captcha := rand.Intn(900000) + 100000
|
|
||||||
m := 5
|
|
||||||
|
|
||||||
gateway := "https://v.juhe.cn/sms/send"
|
|
||||||
|
|
||||||
client := resty.New()
|
|
||||||
client.DisableWarn = true
|
|
||||||
resp, err := client.R().
|
|
||||||
SetFormData(map[string]string{
|
|
||||||
"mobile": phone,
|
|
||||||
"tpl_id": conf.SmsJuheSetting.TplID,
|
|
||||||
"tpl_value": fmt.Sprintf(conf.SmsJuheSetting.TplVal, captcha, m),
|
|
||||||
"key": conf.SmsJuheSetting.Key,
|
|
||||||
}).Post(gateway)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resp.StatusCode() != http.StatusOK {
|
|
||||||
return errors.New(resp.Status())
|
|
||||||
}
|
|
||||||
|
|
||||||
result := &JuhePhoneCaptchaRsp{}
|
|
||||||
err = json.Unmarshal(resp.Body(), result)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.ErrorCode != 0 {
|
|
||||||
return errors.New(result.Reason)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 写入表
|
|
||||||
captchaModel := &model.Captcha{
|
|
||||||
Phone: phone,
|
|
||||||
Captcha: strconv.Itoa(captcha),
|
|
||||||
ExpiredOn: time.Now().Add(time.Minute * time.Duration(m)).Unix(),
|
|
||||||
}
|
|
||||||
captchaModel.Create(d.engine)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *dataServant) IsFriend(_userID int64, _friendID int64) bool {
|
|
||||||
// TODO: you are friend in all now
|
|
||||||
return true
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/rocboss/paopao-ce/internal/conf"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getOssDomain() string {
|
|
||||||
uri := "https://"
|
|
||||||
if conf.CfgIf("AliOSS") {
|
|
||||||
return uri + conf.AliOSSSetting.Domain + "/"
|
|
||||||
} else if conf.CfgIf("MinIO") {
|
|
||||||
if !conf.MinIOSetting.Secure {
|
|
||||||
uri = "http://"
|
|
||||||
}
|
|
||||||
return uri + conf.MinIOSetting.Domain + "/" + conf.MinIOSetting.Bucket + "/"
|
|
||||||
} else if conf.CfgIf("S3") {
|
|
||||||
if !conf.S3Setting.Secure {
|
|
||||||
uri = "http://"
|
|
||||||
}
|
|
||||||
// TODO: will not work well need test in real world
|
|
||||||
return uri + conf.S3Setting.Domain + "/" + conf.S3Setting.Bucket + "/"
|
|
||||||
} else if conf.CfgIf("LocalOSS") {
|
|
||||||
if !conf.LocalOSSSetting.Secure {
|
|
||||||
uri = "http://"
|
|
||||||
}
|
|
||||||
return uri + conf.LocalOSSSetting.Domain + "/oss/" + conf.LocalOSSSetting.Bucket + "/"
|
|
||||||
}
|
|
||||||
return uri + conf.AliOSSSetting.Domain + "/"
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package dao
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/Masterminds/semver/v3"
|
|
||||||
)
|
|
||||||
|
|
||||||
// versionInfo 版本信息
|
|
||||||
type versionInfo interface {
|
|
||||||
name() string
|
|
||||||
version() *semver.Version
|
|
||||||
}
|
|
@ -0,0 +1,8 @@
|
|||||||
|
package rest
|
||||||
|
|
||||||
|
import "github.com/rocboss/paopao-ce/internal/model"
|
||||||
|
|
||||||
|
type IndexTweetsResp struct {
|
||||||
|
Tweets []*model.PostFormated
|
||||||
|
Total int64
|
||||||
|
}
|
Loading…
Reference in new issue