Merge pull request #97 from rocboss/feature/post-visibility

support set visibility(public/private/friend) for post
pull/115/head
ROC 3 years ago committed by GitHub
commit 071dfe5746
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -77,8 +77,8 @@ LocalOSS: # 本地文件OSS存储配置
Bucket: paopao Bucket: paopao
Domain: 127.0.0.1:8008 Domain: 127.0.0.1:8008
Database: # Database通用配置 Database: # Database通用配置
LogLevel: 2 LogLevel: error # 日志级别 silent|error|warn|info
TablePrefix: p_ TablePrefix: p_ # 表名前缀
MySQL: # MySQL数据库 MySQL: # MySQL数据库
Username: paopao Username: paopao
Password: paopao Password: paopao
@ -88,7 +88,7 @@ MySQL: # MySQL数据库
ParseTime: True ParseTime: True
MaxIdleConns: 10 MaxIdleConns: 10
MaxOpenConns: 30 MaxOpenConns: 30
Postgres: Postgres: # PostgreSQL数据库
User: paopao User: paopao
Password: paopao Password: paopao
DBName: paopao DBName: paopao

@ -23,10 +23,10 @@ func newDBEngine() (*gorm.DB, error) {
newLogger := logger.New( newLogger := logger.New(
logrus.StandardLogger(), // io writer日志输出的目标前缀和日志包含的内容 logrus.StandardLogger(), // io writer日志输出的目标前缀和日志包含的内容
logger.Config{ logger.Config{
SlowThreshold: time.Second, // 慢 SQL 阈值 SlowThreshold: time.Second, // 慢 SQL 阈值
LogLevel: databaseSetting.LogLevel, // 日志级别 LogLevel: databaseSetting.logLevel(), // 日志级别
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound记录未找到错误 IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound记录未找到错误
Colorful: false, // 禁用彩色打印 Colorful: false, // 禁用彩色打印
}, },
) )

@ -80,7 +80,7 @@ type ZincSettingS struct {
type DatabaseSetingS struct { type DatabaseSetingS struct {
TablePrefix string TablePrefix string
LogLevel logger.LogLevel LogLevel string
} }
type MySQLSettingS struct { type MySQLSettingS struct {
@ -282,3 +282,18 @@ func (s PostgresSettingS) Dsn() string {
} }
return strings.Join(params, " ") return strings.Join(params, " ")
} }
func (s *DatabaseSetingS) logLevel() logger.LogLevel {
switch strings.ToLower(s.LogLevel) {
case "silent":
return logger.Silent
case "error":
return logger.Error
case "warn":
return logger.Warn
case "info":
return logger.Info
default:
return logger.Error
}
}

@ -6,6 +6,7 @@ const (
IdxActUpdatePost IdxActUpdatePost
IdxActDeletePost IdxActDeletePost
IdxActStickPost IdxActStickPost
IdxActVisiblePost
) )
type IndexActionT uint8 type IndexActionT uint8
@ -22,6 +23,8 @@ func (a IndexActionT) String() string {
return "delete post" return "delete post"
case IdxActStickPost: case IdxActStickPost:
return "stick post" return "stick post"
case IdxActVisiblePost:
return "visible post"
default: default:
return "unknow action" return "unknow action"
} }

@ -34,8 +34,10 @@ type DataService interface {
DeletePost(post *model.Post) error DeletePost(post *model.Post) error
LockPost(post *model.Post) error LockPost(post *model.Post) error
StickPost(post *model.Post) error StickPost(post *model.Post) error
VisiblePost(post *model.Post, visibility model.PostVisibleT) error
GetPostByID(id int64) (*model.Post, error) GetPostByID(id int64) (*model.Post, error)
GetPosts(conditions *model.ConditionsT, offset, limit int) ([]*model.Post, error) GetPosts(conditions *model.ConditionsT, offset, limit int) ([]*model.Post, error)
MergePosts(posts []*model.Post) ([]*model.PostFormated, error)
GetPostCount(conditions *model.ConditionsT) (int64, error) GetPostCount(conditions *model.ConditionsT) (int64, error)
UpdatePost(post *model.Post) error UpdatePost(post *model.Post) error
GetUserPostStar(postID, userID int64) (*model.PostStar, error) GetUserPostStar(postID, userID int64) (*model.PostStar, error)
@ -70,4 +72,6 @@ type DataService interface {
GetLatestPhoneCaptcha(phone string) (*model.Captcha, error) GetLatestPhoneCaptcha(phone string) (*model.Captcha, error)
UsePhoneCaptcha(captcha *model.Captcha) error UsePhoneCaptcha(captcha *model.Captcha) error
SendPhoneCaptcha(phone string) error SendPhoneCaptcha(phone string) error
IsFriend(userID int64, friendID int64) bool
} }

@ -5,5 +5,5 @@ import (
) )
type IndexPostsService interface { type IndexPostsService interface {
IndexPosts(offset int, limit int) ([]*model.PostFormated, error) IndexPosts(userId int64, offset int, limit int) ([]*model.PostFormated, error)
} }

@ -1,6 +1,7 @@
package core package core
import ( import (
"github.com/rocboss/paopao-ce/internal/model"
"github.com/rocboss/paopao-ce/pkg/zinc" "github.com/rocboss/paopao-ce/pkg/zinc"
) )
@ -12,8 +13,9 @@ const (
type SearchType string type SearchType string
type QueryT struct { type QueryT struct {
Query string Query string
Type SearchType Visibility []model.PostVisibleT
Type SearchType
} }
// SearchService search service interface that implement base zinc // SearchService search service interface that implement base zinc

@ -14,7 +14,7 @@ var (
errNotExist = errors.New("index posts cache not exist") errNotExist = errors.New("index posts cache not exist")
) )
func newSimpleCacheIndexServant(getIndexPosts func(offset, limit int) ([]*model.PostFormated, error)) *simpleCacheIndexServant { func newSimpleCacheIndexServant(getIndexPosts indexPostsFunc) *simpleCacheIndexServant {
s := conf.SimpleCacheIndexSetting s := conf.SimpleCacheIndexSetting
cacheIndex := &simpleCacheIndexServant{ cacheIndex := &simpleCacheIndexServant{
getIndexPosts: getIndexPosts, getIndexPosts: getIndexPosts,
@ -48,7 +48,7 @@ func newSimpleCacheIndexServant(getIndexPosts func(offset, limit int) ([]*model.
return cacheIndex return cacheIndex
} }
func (s *simpleCacheIndexServant) IndexPosts(offset int, limit int) ([]*model.PostFormated, error) { func (s *simpleCacheIndexServant) IndexPosts(_userId int64, offset int, limit int) ([]*model.PostFormated, error) {
posts := s.atomicIndex.Load().([]*model.PostFormated) posts := s.atomicIndex.Load().([]*model.PostFormated)
end := offset + limit end := offset + limit
size := len(posts) size := len(posts)
@ -78,7 +78,7 @@ func (s *simpleCacheIndexServant) startIndexPosts() {
case <-s.checkTick.C: case <-s.checkTick.C:
if len(s.indexPosts) == 0 { if len(s.indexPosts) == 0 {
logrus.Debugf("index posts by checkTick") logrus.Debugf("index posts by checkTick")
if s.indexPosts, err = s.getIndexPosts(0, s.maxIndexSize); err == nil { if s.indexPosts, err = s.getIndexPosts(0, 0, s.maxIndexSize); err == nil {
s.atomicIndex.Store(s.indexPosts) s.atomicIndex.Store(s.indexPosts)
} else { } else {
logrus.Errorf("get index posts err: %v", err) logrus.Errorf("get index posts err: %v", err)
@ -92,7 +92,12 @@ func (s *simpleCacheIndexServant) startIndexPosts() {
} }
case action := <-s.indexActionCh: case action := <-s.indexActionCh:
switch action { switch action {
case core.IdxActCreatePost, core.IdxActUpdatePost, core.IdxActDeletePost, core.IdxActStickPost: // TODO: 这里列出来是因为后续可能会精细化处理每种情况
case core.IdxActCreatePost,
core.IdxActUpdatePost,
core.IdxActDeletePost,
core.IdxActStickPost,
core.IdxActVisiblePost:
// prevent many update post in least time // prevent many update post in least time
if len(s.indexPosts) != 0 { if len(s.indexPosts) != 0 {
logrus.Debugf("remove index posts by action %s", action) logrus.Debugf("remove index posts by action %s", action)

@ -32,8 +32,9 @@ type dataServant struct {
zinc *zinc.ZincClient zinc *zinc.ZincClient
} }
type indexPostsFunc func(int64, int, int) ([]*model.PostFormated, error)
type simpleCacheIndexServant struct { type simpleCacheIndexServant struct {
getIndexPosts func(offset, limit int) ([]*model.PostFormated, error) getIndexPosts indexPostsFunc
indexActionCh chan core.IndexActionT indexActionCh chan core.IndexActionT
indexPosts []*model.PostFormated indexPosts []*model.PostFormated
atomicIndex atomic.Value atomicIndex atomic.Value

@ -1,6 +1,7 @@
package dao package dao
import ( import (
"strings"
"time" "time"
"github.com/rocboss/paopao-ce/internal/core" "github.com/rocboss/paopao-ce/internal/core"
@ -39,6 +40,46 @@ func (d *dataServant) StickPost(post *model.Post) error {
return nil 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.indexActive(core.IdxActVisiblePost)
return nil
}
func (d *dataServant) GetPostByID(id int64) (*model.Post, error) { func (d *dataServant) GetPostByID(id int64) (*model.Post, error) {
post := &model.Post{ post := &model.Post{
Model: &model.Model{ Model: &model.Model{
@ -79,7 +120,7 @@ func (d *dataServant) GetUserPostStars(userID int64, offset, limit int) ([]*mode
} }
return star.List(d.engine, &model.ConditionsT{ return star.List(d.engine, &model.ConditionsT{
"ORDER": "id DESC", "ORDER": d.engine.NamingStrategy.TableName("PostStar") + ".id DESC",
}, offset, limit) }, offset, limit)
} }
@ -118,7 +159,7 @@ func (d *dataServant) GetUserPostCollections(userID int64, offset, limit int) ([
} }
return collection.List(d.engine, &model.ConditionsT{ return collection.List(d.engine, &model.ConditionsT{
"ORDER": "id DESC", "ORDER": d.engine.NamingStrategy.TableName("PostCollection") + ".id DESC",
}, offset, limit) }, offset, limit)
} }

@ -6,15 +6,15 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
func (d *dataServant) IndexPosts(offset int, limit int) ([]*model.PostFormated, error) { func (d *dataServant) IndexPosts(userId int64, offset int, limit int) ([]*model.PostFormated, error) {
if d.useCacheIndex { if d.useCacheIndex {
if posts, err := d.cacheIndex.IndexPosts(offset, limit); err == nil { if posts, err := d.cacheIndex.IndexPosts(userId, offset, limit); err == nil {
logrus.Debugln("get index posts from cached") logrus.Debugln("get index posts from cached")
return posts, nil return posts, nil
} }
} }
logrus.Debugf("get index posts from database but useCacheIndex: %t", d.useCacheIndex) logrus.Debugf("get index posts from database but useCacheIndex: %t", d.useCacheIndex)
return d.getIndexPosts(offset, limit) return d.getIndexPosts(userId, offset, limit)
} }
func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) { func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) {
@ -56,9 +56,11 @@ func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, er
return postsFormated, nil return postsFormated, nil
} }
func (d *dataServant) getIndexPosts(offset int, limit int) ([]*model.PostFormated, error) { // getIndexPosts _userId保留未来使用
func (d *dataServant) getIndexPosts(_userId int64, offset int, limit int) ([]*model.PostFormated, error) {
posts, err := (&model.Post{}).List(d.engine, &model.ConditionsT{ posts, err := (&model.Post{}).List(d.engine, &model.ConditionsT{
"ORDER": "is_top DESC, latest_replied_on DESC", "visibility IN ?": []model.PostVisibleT{model.PostVisitPublic, model.PostVisitFriend},
"ORDER": "is_top DESC, latest_replied_on DESC",
}, offset, limit) }, offset, limit)
if err != nil { if err != nil {
logrus.Debugf("getIndexPosts err: %v", err) logrus.Debugf("getIndexPosts err: %v", err)

@ -1,17 +1,32 @@
package dao package dao
import "github.com/rocboss/paopao-ce/internal/model" import (
"github.com/rocboss/paopao-ce/internal/model"
"gorm.io/gorm"
)
func (d *dataServant) CreateTag(tag *model.Tag) (*model.Tag, error) { 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) t, err := tag.Get(d.engine)
if err != nil { if err != nil {
tag.QuoteNum = 1 tag.QuoteNum = 1
return tag.Create(d.engine) return tag.Create(db)
} }
// 更新 // 更新
t.QuoteNum++ t.QuoteNum++
err = t.Update(d.engine) err = t.Update(db)
if err != nil { if err != nil {
return nil, err return nil, err
@ -20,16 +35,11 @@ func (d *dataServant) CreateTag(tag *model.Tag) (*model.Tag, error) {
return t, nil return t, nil
} }
func (d *dataServant) DeleteTag(tag *model.Tag) error { func (d *dataServant) deleteTag(db *gorm.DB, tag *model.Tag) error {
tag, err := tag.Get(d.engine) tag, err := tag.Get(db)
if err != nil { if err != nil {
return err return err
} }
tag.QuoteNum-- tag.QuoteNum--
return tag.Update(d.engine) return tag.Update(db)
}
func (d *dataServant) GetTags(conditions *model.ConditionsT, offset, limit int) ([]*model.Tag, error) {
return (&model.Tag{}).List(d.engine, conditions, offset, limit)
} }

@ -158,3 +158,8 @@ func (d *dataServant) SendPhoneCaptcha(phone string) error {
captchaModel.Create(d.engine) captchaModel.Create(d.engine)
return nil return nil
} }
func (d *dataServant) IsFriend(_userID int64, _friendID int64) bool {
// TODO: you are friend in all now
return true
}

@ -74,3 +74,36 @@ func JWT() gin.HandlerFunc {
c.Next() c.Next()
} }
} }
func JwtLoose() gin.HandlerFunc {
return func(c *gin.Context) {
token, exist := c.GetQuery("token")
if !exist {
token = c.GetHeader("Authorization")
// 验证前端传过来的token格式不为空开头为Bearer
if strings.HasPrefix(token, "Bearer ") {
// 验证通过提取有效部分除去Bearer)
token = token[7:]
} else {
c.Next()
}
}
if len(token) > 0 {
if claims, err := app.ParseToken(token); err == nil {
c.Set("UID", claims.UID)
c.Set("USERNAME", claims.Username)
// 加载用户信息
user := &model.User{
Model: &model.Model{
ID: claims.UID,
},
}
user, err := user.Get(conf.DBEngine)
if err == nil && (conf.JWTSetting.Issuer+":"+user.Salt) == claims.Issuer {
c.Set("USER", user)
}
}
}
c.Next()
}
}

@ -7,20 +7,31 @@ import (
"gorm.io/gorm" "gorm.io/gorm"
) )
// PostVisibleT 可访问类型0公开1私密2好友
type PostVisibleT uint8
const (
PostVisitPublic PostVisibleT = iota
PostVisitPrivate
PostVisitFriend
PostVisitInvalid
)
type Post struct { type Post struct {
*Model *Model
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
CommentCount int64 `json:"comment_count"` CommentCount int64 `json:"comment_count"`
CollectionCount int64 `json:"collection_count"` CollectionCount int64 `json:"collection_count"`
UpvoteCount int64 `json:"upvote_count"` UpvoteCount int64 `json:"upvote_count"`
IsTop int `json:"is_top"` Visibility PostVisibleT `json:"visibility"`
IsEssence int `json:"is_essence"` IsTop int `json:"is_top"`
IsLock int `json:"is_lock"` IsEssence int `json:"is_essence"`
LatestRepliedOn int64 `json:"latest_replied_on"` IsLock int `json:"is_lock"`
Tags string `json:"tags"` LatestRepliedOn int64 `json:"latest_replied_on"`
AttachmentPrice int64 `json:"attachment_price"` Tags string `json:"tags"`
IP string `json:"ip"` AttachmentPrice int64 `json:"attachment_price"`
IPLoc string `json:"ip_loc"` IP string `json:"ip"`
IPLoc string `json:"ip_loc"`
} }
type PostFormated struct { type PostFormated struct {
@ -31,6 +42,7 @@ type PostFormated struct {
CommentCount int64 `json:"comment_count"` CommentCount int64 `json:"comment_count"`
CollectionCount int64 `json:"collection_count"` CollectionCount int64 `json:"collection_count"`
UpvoteCount int64 `json:"upvote_count"` UpvoteCount int64 `json:"upvote_count"`
Visibility PostVisibleT `json:"visibility"`
IsTop int `json:"is_top"` IsTop int `json:"is_top"`
IsEssence int `json:"is_essence"` IsEssence int `json:"is_essence"`
IsLock int `json:"is_lock"` IsLock int `json:"is_lock"`
@ -56,6 +68,7 @@ func (p *Post) Format() *PostFormated {
CommentCount: p.CommentCount, CommentCount: p.CommentCount,
CollectionCount: p.CollectionCount, CollectionCount: p.CollectionCount,
UpvoteCount: p.UpvoteCount, UpvoteCount: p.UpvoteCount,
Visibility: p.Visibility,
IsTop: p.IsTop, IsTop: p.IsTop,
IsEssence: p.IsEssence, IsEssence: p.IsEssence,
IsLock: p.IsLock, IsLock: p.IsLock,
@ -144,3 +157,18 @@ func (p *Post) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
func (p *Post) Update(db *gorm.DB) error { func (p *Post) Update(db *gorm.DB) error {
return db.Model(&Post{}).Where("id = ? AND is_del = ?", p.Model.ID, 0).Save(p).Error return db.Model(&Post{}).Where("id = ? AND is_del = ?", p.Model.ID, 0).Save(p).Error
} }
func (p PostVisibleT) String() string {
switch p {
case PostVisitPublic:
return "public"
case PostVisitPrivate:
return "private"
case PostVisitFriend:
return "friend"
case PostVisitInvalid:
return "invalid"
default:
return "unknow"
}
}

@ -8,22 +8,26 @@ import (
type PostCollection struct { type PostCollection struct {
*Model *Model
Post *Post `json:"-"`
PostID int64 `json:"post_id"` PostID int64 `json:"post_id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
} }
func (p *PostCollection) Get(db *gorm.DB) (*PostCollection, error) { func (p *PostCollection) Get(db *gorm.DB) (*PostCollection, error) {
var star PostCollection var star PostCollection
tn := db.NamingStrategy.TableName("PostCollection") + "."
if p.Model != nil && p.ID > 0 { if p.Model != nil && p.ID > 0 {
db = db.Where("id = ? AND is_del = ?", p.ID, 0) db = db.Where(tn+"id = ? AND "+tn+"is_del = ?", p.ID, 0)
} }
if p.PostID > 0 { if p.PostID > 0 {
db = db.Where("post_id = ?", p.PostID) db = db.Where(tn+"post_id = ?", p.PostID)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate).Order("Post.id DESC")
err := db.First(&star).Error err := db.First(&star).Error
if err != nil { if err != nil {
return &star, err return &star, err
@ -33,13 +37,13 @@ func (p *PostCollection) Get(db *gorm.DB) (*PostCollection, error) {
} }
func (p *PostCollection) Create(db *gorm.DB) (*PostCollection, error) { func (p *PostCollection) Create(db *gorm.DB) (*PostCollection, error) {
err := db.Create(&p).Error err := db.Omit("Post").Create(&p).Error
return p, err return p, err
} }
func (p *PostCollection) Delete(db *gorm.DB) error { func (p *PostCollection) Delete(db *gorm.DB) error {
return db.Model(&PostCollection{}).Where("id = ? AND is_del = ?", p.Model.ID, 0).Updates(map[string]interface{}{ return db.Model(&PostCollection{}).Omit("Post").Where("id = ? AND is_del = ?", p.Model.ID, 0).Updates(map[string]interface{}{
"deleted_on": time.Now().Unix(), "deleted_on": time.Now().Unix(),
"is_del": 1, "is_del": 1,
}).Error }).Error
@ -48,22 +52,25 @@ func (p *PostCollection) Delete(db *gorm.DB) error {
func (p *PostCollection) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*PostCollection, error) { func (p *PostCollection) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*PostCollection, error) {
var collections []*PostCollection var collections []*PostCollection
var err error var err error
tn := db.NamingStrategy.TableName("PostCollection") + "."
if offset >= 0 && limit > 0 { if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit) db = db.Offset(offset).Limit(limit)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
for k, v := range *conditions { for k, v := range *conditions {
if k == "ORDER" { if k == "ORDER" {
db = db.Order(v) db = db.Order(v)
} else { } else {
db = db.Where(k, v) db = db.Where(tn+k, v)
} }
} }
if err = db.Where("is_del = ?", 0).Find(&collections).Error; err != nil { db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate).Order("Post.id DESC")
if err = db.Where(tn+"is_del = ?", 0).Find(&collections).Error; err != nil {
return nil, err return nil, err
} }
@ -72,17 +79,21 @@ func (p *PostCollection) List(db *gorm.DB, conditions *ConditionsT, offset, limi
func (p *PostCollection) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) { func (p *PostCollection) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
var count int64 var count int64
tn := db.NamingStrategy.TableName("PostCollection") + "."
if p.PostID > 0 { if p.PostID > 0 {
db = db.Where("post_id = ?", p.PostID) db = db.Where(tn+"post_id = ?", p.PostID)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
for k, v := range *conditions { for k, v := range *conditions {
if k != "ORDER" { if k != "ORDER" {
db = db.Where(k, v) db = db.Where(tn+k, v)
} }
} }
db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate)
if err := db.Model(p).Count(&count).Error; err != nil { if err := db.Model(p).Count(&count).Error; err != nil {
return 0, err return 0, err
} }

@ -8,38 +8,40 @@ import (
type PostStar struct { type PostStar struct {
*Model *Model
Post *Post `json:"-"`
PostID int64 `json:"post_id"` PostID int64 `json:"post_id"`
UserID int64 `json:"user_id"` UserID int64 `json:"user_id"`
} }
func (p *PostStar) Get(db *gorm.DB) (*PostStar, error) { func (p *PostStar) Get(db *gorm.DB) (*PostStar, error) {
var star PostStar var star PostStar
tn := db.NamingStrategy.TableName("PostStar") + "."
if p.Model != nil && p.ID > 0 { if p.Model != nil && p.ID > 0 {
db = db.Where("id = ? AND is_del = ?", p.ID, 0) db = db.Where(tn+"id = ? AND "+tn+"is_del = ?", p.ID, 0)
} }
if p.PostID > 0 { if p.PostID > 0 {
db = db.Where("post_id = ?", p.PostID) db = db.Where(tn+"post_id = ?", p.PostID)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
err := db.First(&star).Error db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate).Order("Post.id DESC")
if err != nil { if err := db.First(&star).Error; err != nil {
return &star, err return nil, err
} }
return &star, nil return &star, nil
} }
func (p *PostStar) Create(db *gorm.DB) (*PostStar, error) { func (p *PostStar) Create(db *gorm.DB) (*PostStar, error) {
err := db.Create(&p).Error err := db.Omit("Post").Create(&p).Error
return p, err return p, err
} }
func (p *PostStar) Delete(db *gorm.DB) error { func (p *PostStar) Delete(db *gorm.DB) error {
return db.Model(&PostStar{}).Where("id = ? AND is_del = ?", p.Model.ID, 0).Updates(map[string]interface{}{ return db.Model(&PostStar{}).Omit("Post").Where("id = ? AND is_del = ?", p.Model.ID, 0).Updates(map[string]interface{}{
"deleted_on": time.Now().Unix(), "deleted_on": time.Now().Unix(),
"is_del": 1, "is_del": 1,
}).Error }).Error
@ -48,44 +50,49 @@ func (p *PostStar) Delete(db *gorm.DB) error {
func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*PostStar, error) { func (p *PostStar) List(db *gorm.DB, conditions *ConditionsT, offset, limit int) ([]*PostStar, error) {
var stars []*PostStar var stars []*PostStar
var err error var err error
tn := db.NamingStrategy.TableName("PostStar") + "."
if offset >= 0 && limit > 0 { if offset >= 0 && limit > 0 {
db = db.Offset(offset).Limit(limit) db = db.Offset(offset).Limit(limit)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
for k, v := range *conditions { for k, v := range *conditions {
if k == "ORDER" { if k == "ORDER" {
db = db.Order(v) db = db.Order(v)
} else { } else {
db = db.Where(k, v) db = db.Where(tn+k, v)
} }
} }
if err = db.Where("is_del = ?", 0).Find(&stars).Error; err != nil { db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate).Order("Post.id DESC")
if err = db.Find(&stars).Error; err != nil {
return nil, err return nil, err
} }
return stars, nil return stars, nil
} }
func (p *PostStar) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) { func (p *PostStar) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) {
var count int64 var count int64
tn := db.NamingStrategy.TableName("PostStar") + "."
if p.PostID > 0 { if p.PostID > 0 {
db = db.Where("post_id = ?", p.PostID) db = db.Where(tn+"post_id = ?", p.PostID)
} }
if p.UserID > 0 { if p.UserID > 0 {
db = db.Where("user_id = ?", p.UserID) db = db.Where(tn+"user_id = ?", p.UserID)
} }
for k, v := range *conditions { for k, v := range *conditions {
if k != "ORDER" { if k != "ORDER" {
db = db.Where(k, v) db = db.Where(tn+k, v)
} }
} }
db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate)
if err := db.Model(p).Count(&count).Error; err != nil { if err := db.Model(p).Count(&count).Error; err != nil {
return 0, err return 0, err
} }
return count, nil return count, nil
} }

@ -31,7 +31,8 @@ func GetPostList(c *gin.Context) {
return return
} }
totalRows, _ := service.GetPostCount(&model.ConditionsT{ totalRows, _ := service.GetPostCount(&model.ConditionsT{
"ORDER": "latest_replied_on DESC", "visibility IN ?": []model.PostVisibleT{model.PostVisitPublic, model.PostVisitFriend},
"ORDER": "latest_replied_on DESC",
}) })
response.ToResponseList(posts, totalRows) response.ToResponseList(posts, totalRows)
@ -155,11 +156,16 @@ func PostStar(c *gin.Context) {
star, err := service.GetPostStar(param.ID, userID.(int64)) star, err := service.GetPostStar(param.ID, userID.(int64))
if err != nil { if err != nil {
// 创建Star // 创建Star
service.CreatePostStar(param.ID, userID.(int64)) _, err = service.CreatePostStar(param.ID, userID.(int64))
status = true status = true
} else { } else {
// 取消Star // 取消Star
service.DeletePostStar(star) err = service.DeletePostStar(star)
}
if err != nil {
response.ToErrorResponse(errcode.NoPermission)
return
} }
response.ToResponse(gin.H{ response.ToResponse(gin.H{
@ -203,11 +209,16 @@ func PostCollection(c *gin.Context) {
collection, err := service.GetPostCollection(param.ID, userID.(int64)) collection, err := service.GetPostCollection(param.ID, userID.(int64))
if err != nil { if err != nil {
// 创建collection // 创建collection
service.CreatePostCollection(param.ID, userID.(int64)) _, err = service.CreatePostCollection(param.ID, userID.(int64))
status = true status = true
} else { } else {
// 取消Star // 取消Star
service.DeletePostCollection(collection) err = service.DeletePostCollection(collection)
}
if err != nil {
response.ToErrorResponse(errcode.NoPermission)
return
} }
response.ToResponse(gin.H{ response.ToResponse(gin.H{
@ -287,6 +298,28 @@ func StickPost(c *gin.Context) {
}) })
} }
func VisiblePost(c *gin.Context) {
param := service.PostVisibilityReq{}
response := app.NewResponse(c)
valid, errs := app.BindAndValid(c, &param)
if !valid {
logrus.Errorf("app.BindAndValid errs: %v", errs)
response.ToErrorResponse(errcode.InvalidParams.WithDetails(errs.Errors()...))
return
}
user, _ := userFrom(c)
if err := service.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) { func GetPostTags(c *gin.Context) {
param := service.PostTagsReq{} param := service.PostTagsReq{}
response := app.NewResponse(c) response := app.NewResponse(c)

@ -313,13 +313,23 @@ func GetUserPosts(c *gin.Context) {
return return
} }
conditions := &model.ConditionsT{ visibilities := []model.PostVisibleT{model.PostVisitPublic}
"user_id": user.ID, if u, exists := c.Get("USER"); exists {
"ORDER": "latest_replied_on DESC", self := u.(*model.User)
if self.ID == user.ID || self.IsAdmin {
visibilities = append(visibilities, model.PostVisitPrivate, model.PostVisitFriend)
} else if service.IsFriend(user.ID, self.ID) {
visibilities = append(visibilities, model.PostVisitFriend)
}
}
conditions := model.ConditionsT{
"user_id": user.ID,
"visibility IN ?": visibilities,
"ORDER": "latest_replied_on DESC",
} }
posts, err := service.GetPostList(&service.PostListReq{ posts, err := service.GetPostList(&service.PostListReq{
Conditions: conditions, Conditions: &conditions,
Offset: (app.GetPage(c) - 1) * app.GetPageSize(c), Offset: (app.GetPage(c) - 1) * app.GetPageSize(c),
Limit: app.GetPageSize(c), Limit: app.GetPageSize(c),
}) })
@ -328,7 +338,7 @@ func GetUserPosts(c *gin.Context) {
response.ToErrorResponse(errcode.GetPostsFailed) response.ToErrorResponse(errcode.GetPostsFailed)
return return
} }
totalRows, _ := service.GetPostCount(conditions) totalRows, _ := service.GetPostCount(&conditions)
response.ToResponseList(posts, totalRows) response.ToResponseList(posts, totalRows)
} }
@ -554,3 +564,12 @@ func GetUserWalletBills(c *gin.Context) {
response.ToResponseList(bills, totalRows) response.ToResponseList(bills, totalRows)
} }
func userFrom(c *gin.Context) (*model.User, bool) {
if u, exists := c.Get("USER"); exists {
user, ok := u.(*model.User)
return user, ok
}
logrus.Debugln("user not exist")
return nil, false
}

@ -69,9 +69,13 @@ func NewRouter() *gin.Engine {
// 获取用户基本信息 // 获取用户基本信息
noAuthApi.GET("/user/profile", api.GetUserProfile) noAuthApi.GET("/user/profile", api.GetUserProfile)
}
// 宽松鉴权路由组
looseApi := r.Group("/").Use(middleware.JwtLoose())
{
// 获取用户动态列表 // 获取用户动态列表
noAuthApi.GET("/user/posts", api.GetUserPosts) looseApi.GET("/user/posts", api.GetUserPosts)
} }
// 鉴权路由组 // 鉴权路由组
@ -163,6 +167,9 @@ func NewRouter() *gin.Engine {
// 置顶动态 // 置顶动态
privApi.POST("/post/stick", api.StickPost) privApi.POST("/post/stick", api.StickPost)
// 修改动态可见度
privApi.POST("/post/visibility", api.VisiblePost)
// 发布动态评论 // 发布动态评论
privApi.POST("/post/comment", api.CreatePostComment) privApi.POST("/post/comment", api.CreatePostComment)

@ -2,6 +2,7 @@ package service
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"math" "math"
"strings" "strings"
@ -11,6 +12,7 @@ import (
"github.com/rocboss/paopao-ce/internal/conf" "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/errcode"
"github.com/rocboss/paopao-ce/pkg/util" "github.com/rocboss/paopao-ce/pkg/util"
"github.com/rocboss/paopao-ce/pkg/zinc" "github.com/rocboss/paopao-ce/pkg/zinc"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -36,6 +38,7 @@ type PostCreationReq struct {
Tags []string `json:"tags" binding:"required"` Tags []string `json:"tags" binding:"required"`
Users []string `json:"users" binding:"required"` Users []string `json:"users" binding:"required"`
AttachmentPrice int64 `json:"attachment_price"` AttachmentPrice int64 `json:"attachment_price"`
Visibility model.PostVisibleT `json:"visibility"`
} }
type PostDelReq struct { type PostDelReq struct {
@ -50,6 +53,11 @@ type PostStickReq struct {
ID int64 `json:"id" binding:"required"` ID int64 `json:"id" binding:"required"`
} }
type PostVisibilityReq struct {
ID int64 `json:"id" binding:"required"`
Visibility model.PostVisibleT `json:"visibility"`
}
type PostStarReq struct { type PostStarReq struct {
ID int64 `json:"id" binding:"required"` ID int64 `json:"id" binding:"required"`
} }
@ -94,8 +102,13 @@ func tagsFrom(originTags []string) []string {
return tags return tags
} }
// CreatePost 创建文章
// TODO: maybe have bug need optimize for use transaction to create post
func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Post, error) { func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Post, error) {
ip := c.ClientIP() ip := c.ClientIP()
if len(ip) == 0 {
ip = "未知"
}
tags := tagsFrom(param.Tags) tags := tagsFrom(param.Tags)
post := &model.Post{ post := &model.Post{
@ -104,21 +117,13 @@ func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Pos
IP: ip, IP: ip,
IPLoc: util.GetIPLoc(ip), IPLoc: util.GetIPLoc(ip),
AttachmentPrice: param.AttachmentPrice, AttachmentPrice: param.AttachmentPrice,
Visibility: param.Visibility,
} }
post, err := ds.CreatePost(post) post, err := ds.CreatePost(post)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 创建标签
for _, t := range tags {
tag := &model.Tag{
UserID: userID,
Tag: t,
}
ds.CreateTag(tag)
}
for _, item := range param.Contents { for _, item := range param.Contents {
if err = item.Check(); err != nil { if err = item.Check(); err != nil {
// 属性非法 // 属性非法
@ -137,27 +142,43 @@ func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Pos
Type: item.Type, Type: item.Type,
Sort: item.Sort, Sort: item.Sort,
} }
ds.CreatePostContent(postContent) if _, err := ds.CreatePostContent(postContent); err != nil {
return nil, err
}
} }
// 推送Search // TODO: 目前非私密文章才能有如下操作,后续再优化
go PushPostToSearch(post) if post.Visibility != model.PostVisitPrivate {
// 创建标签
// 创建用户消息提醒 for _, t := range tags {
for _, u := range param.Users { tag := &model.Tag{
user, err := ds.GetUserByUsername(u) UserID: userID,
if err != nil || user.ID == userID { Tag: t,
continue }
if _, err := ds.CreateTag(tag); err != nil {
return nil, err
}
} }
// 创建用户消息提醒
for _, u := range param.Users {
user, err := ds.GetUserByUsername(u)
if err != nil || user.ID == userID {
continue
}
// 创建消息提醒 // 创建消息提醒
go ds.CreateMessage(&model.Message{ // TODO: 优化消息提醒处理机制
SenderUserID: userID, go ds.CreateMessage(&model.Message{
ReceiverUserID: user.ID, SenderUserID: userID,
Type: model.MESSAGE_POST, ReceiverUserID: user.ID,
Brief: "在新发布的泡泡动态中@了你", Type: model.MESSAGE_POST,
PostID: post.ID, Brief: "在新发布的泡泡动态中@了你",
}) PostID: post.ID,
})
}
// 推送Search
// TODO: 优化推送文章到搜索的处理机制最好使用通道channel传递文章可以省goroutine
go PushPostToSearch(post)
} }
return post, nil return post, nil
@ -212,6 +233,45 @@ func StickPost(id int64) error {
return nil return nil
} }
func VisiblePost(user *model.User, postId int64, visibility model.PostVisibleT) *errcode.Error {
if visibility >= model.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
}
// 相同属性,不需要操作了
oldVisibility := post.Visibility
if oldVisibility == visibility {
logrus.Infof("sample visibility no need operate postId: %d", postId)
return nil
}
if err = ds.VisiblePost(post, visibility); err != nil {
logrus.Warnf("update post failure: %v", err)
return errcode.VisblePostFailed
}
// 搜索处理
if oldVisibility == model.PostVisitPrivate {
// 从私密转为非私密需要push
logrus.Debugf("visible post set to re-public to add search index: %d, visibility: %s", post.ID, visibility)
go PushPostToSearch(post)
} else if visibility == model.PostVisitPrivate {
// 从非私密转为私密需要删除索引
logrus.Debugf("visible post set to private to delete search index: %d, visibility: %s", post.ID, visibility)
go DeleteSearchPost(post)
}
return nil
}
func GetPostStar(postID, userID int64) (*model.PostStar, error) { func GetPostStar(postID, userID int64) (*model.PostStar, error) {
return ds.GetUserPostStar(postID, userID) return ds.GetUserPostStar(postID, userID)
} }
@ -222,6 +282,12 @@ func CreatePostStar(postID, userID int64) (*model.PostStar, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 私密post不可操作
if post.Visibility == model.PostVisitPrivate {
return nil, errors.New("no permision")
}
star, err := ds.CreatePostStar(postID, userID) star, err := ds.CreatePostStar(postID, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -247,6 +313,12 @@ func DeletePostStar(star *model.PostStar) error {
if err != nil { if err != nil {
return err return err
} }
// 私密post不可操作
if post.Visibility == model.PostVisitPrivate {
return errors.New("no permision")
}
// 更新Post点赞数 // 更新Post点赞数
post.UpvoteCount-- post.UpvoteCount--
ds.UpdatePost(post) ds.UpdatePost(post)
@ -267,6 +339,12 @@ func CreatePostCollection(postID, userID int64) (*model.PostCollection, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// 私密post不可操作
if post.Visibility == model.PostVisitPrivate {
return nil, errors.New("no permision")
}
collection, err := ds.CreatePostCollection(postID, userID) collection, err := ds.CreatePostCollection(postID, userID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -292,6 +370,12 @@ func DeletePostCollection(collection *model.PostCollection) error {
if err != nil { if err != nil {
return err return err
} }
// 私密post不可操作
if post.Visibility == model.PostVisitPrivate {
return errors.New("no permision")
}
// 更新Post点赞数 // 更新Post点赞数
post.CollectionCount-- post.CollectionCount--
ds.UpdatePost(post) ds.UpdatePost(post)
@ -337,7 +421,7 @@ func GetPostContentByID(id int64) (*model.PostContent, error) {
} }
func GetIndexPosts(offset int, limit int) ([]*model.PostFormated, error) { func GetIndexPosts(offset int, limit int) ([]*model.PostFormated, error) {
return ds.IndexPosts(offset, limit) return ds.IndexPosts(0, offset, limit)
} }
func GetPostList(req *PostListReq) ([]*model.PostFormated, error) { func GetPostList(req *PostListReq) ([]*model.PostFormated, error) {
@ -423,6 +507,11 @@ func GetPostListFromSearchByQuery(query string, offset, limit int) ([]*model.Pos
} }
func PushPostToSearch(post *model.Post) { func PushPostToSearch(post *model.Post) {
// TODO: 暂时不索引私密文章,后续再完善
if post.Visibility == model.PostVisitPrivate {
return
}
indexName := conf.ZincSetting.Index indexName := conf.ZincSetting.Index
postFormated := post.Format() postFormated := post.Format()
@ -459,6 +548,7 @@ func PushPostToSearch(post *model.Post) {
"comment_count": post.CommentCount, "comment_count": post.CommentCount,
"collection_count": post.CollectionCount, "collection_count": post.CollectionCount,
"upvote_count": post.UpvoteCount, "upvote_count": post.UpvoteCount,
"visibility": post.Visibility,
"is_top": post.IsTop, "is_top": post.IsTop,
"is_essence": post.IsEssence, "is_essence": post.IsEssence,
"content": contentFormated, "content": contentFormated,
@ -482,7 +572,9 @@ func DeleteSearchPost(post *model.Post) error {
func PushPostsToSearch(c *gin.Context) { func PushPostsToSearch(c *gin.Context) {
if ok, _ := conf.Redis.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok { if ok, _ := conf.Redis.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok {
splitNum := 1000 splitNum := 1000
totalRows, _ := GetPostCount(&model.ConditionsT{}) totalRows, _ := GetPostCount(&model.ConditionsT{
"visibility IN ?": []model.PostVisibleT{model.PostVisitPublic, model.PostVisitFriend},
})
pages := math.Ceil(float64(totalRows) / float64(splitNum)) pages := math.Ceil(float64(totalRows) / float64(splitNum))
nums := int(pages) nums := int(pages)
@ -495,9 +587,11 @@ func PushPostsToSearch(c *gin.Context) {
data := []map[string]interface{}{} data := []map[string]interface{}{}
posts, _ := GetPostList(&PostListReq{ posts, _ := GetPostList(&PostListReq{
Conditions: &model.ConditionsT{}, Conditions: &model.ConditionsT{
Offset: i * splitNum, "visibility IN ?": []model.PostVisibleT{model.PostVisitPublic, model.PostVisitFriend},
Limit: splitNum, },
Offset: i * splitNum,
Limit: splitNum,
}) })
for _, post := range posts { for _, post := range posts {
@ -520,6 +614,7 @@ func PushPostsToSearch(c *gin.Context) {
"comment_count": post.CommentCount, "comment_count": post.CommentCount,
"collection_count": post.CollectionCount, "collection_count": post.CollectionCount,
"upvote_count": post.UpvoteCount, "upvote_count": post.UpvoteCount,
"visibility": post.Visibility,
"is_top": post.IsTop, "is_top": post.IsTop,
"is_essence": post.IsEssence, "is_essence": post.IsEssence,
"content": contentFormated, "content": contentFormated,

@ -274,21 +274,11 @@ func GetUserCollections(userID int64, offset, limit int) ([]*model.PostFormated,
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
postIDs := []int64{} var posts []*model.Post
for _, collection := range collections { for _, collection := range collections {
postIDs = append(postIDs, collection.PostID) posts = append(posts, collection.Post)
} }
postsFormated, err := ds.MergePosts(posts)
// 获取Posts
posts, err := ds.GetPosts(&model.ConditionsT{
"id IN ?": postIDs,
"ORDER": "id DESC",
}, 0, 0)
if err != nil {
return nil, 0, err
}
postsFormated, err := FormatPosts(posts)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -306,21 +296,12 @@ func GetUserStars(userID int64, offset, limit int) ([]*model.PostFormated, int64
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
postIDs := []int64{}
for _, star := range stars {
postIDs = append(postIDs, star.PostID)
}
// 获取Posts var posts []*model.Post
posts, err := ds.GetPosts(&model.ConditionsT{ for _, star := range stars {
"id IN ?": postIDs, posts = append(posts, star.Post)
"ORDER": "id DESC",
}, 0, 0)
if err != nil {
return nil, 0, err
} }
postsFormated, err := ds.MergePosts(posts)
postsFormated, err := FormatPosts(posts)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -390,3 +371,15 @@ func GetSuggestTags(keyword string) ([]string, error) {
return ts, nil return ts, nil
} }
func IsFriend(userId, friendId int64) bool {
return ds.IsFriend(userId, friendId)
}
// checkPermision 检查是否拥有者或管理员
func checkPermision(user *model.User, targetUserId int64) *errcode.Error {
if user == nil || (user.ID != targetUserId && !user.IsAdmin) {
return errcode.NoPermission
}
return nil
}

@ -35,6 +35,7 @@ var (
InsuffientDownloadMoney = NewError(30009, "附件下载失败:账户资金不足") InsuffientDownloadMoney = NewError(30009, "附件下载失败:账户资金不足")
DownloadExecFail = NewError(30010, "附件下载失败:扣费失败") DownloadExecFail = NewError(30010, "附件下载失败:扣费失败")
StickPostFailed = NewError(30011, "动态置顶失败") StickPostFailed = NewError(30011, "动态置顶失败")
VisblePostFailed = NewError(30012, "更新可见性失败")
GetCommentsFailed = NewError(40001, "获取评论列表失败") GetCommentsFailed = NewError(40001, "获取评论列表失败")
CreateCommentFailed = NewError(40002, "评论发布失败") CreateCommentFailed = NewError(40002, "评论发布失败")

@ -0,0 +1,9 @@
-- ----------------------------
-- Table p_post alter add visibility column
-- ----------------------------
ALTER TABLE `p_post` ADD COLUMN `visibility` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '可见性 0公开 1私密 2好友可见';
-- ----------------------------
-- Indexes structure for table p_post
-- ----------------------------
CREATE INDEX `idx_visibility` ON `p_post` ( `visibility` ) USING BTREE;

@ -0,0 +1,310 @@
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for p_attachment
-- ----------------------------
DROP TABLE IF EXISTS `p_attachment`;
CREATE TABLE `p_attachment` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`user_id` bigint unsigned NOT NULL DEFAULT '0',
`file_size` bigint unsigned NOT NULL,
`img_width` bigint unsigned NOT NULL DEFAULT '0',
`img_height` bigint unsigned NOT NULL DEFAULT '0',
`type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '1图片2视频3其他附件',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=100041 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='附件';
-- ----------------------------
-- Table structure for p_captcha
-- ----------------------------
DROP TABLE IF EXISTS `p_captcha`;
CREATE TABLE `p_captcha` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '验证码ID',
`phone` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
`captcha` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '验证码',
`use_times` int unsigned NOT NULL DEFAULT '0' COMMENT '使用次数',
`expired_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '过期时间',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_phone` (`phone`) USING BTREE,
KEY `idx_expired_on` (`expired_on`) USING BTREE,
KEY `idx_use_times` (`use_times`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1021 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='手机验证码';
-- ----------------------------
-- Table structure for p_comment
-- ----------------------------
DROP TABLE IF EXISTS `p_comment`;
CREATE TABLE `p_comment` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '评论ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP城市地址',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post` (`post_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6001736 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='评论';
-- ----------------------------
-- Table structure for p_comment_content
-- ----------------------------
DROP TABLE IF EXISTS `p_comment_content`;
CREATE TABLE `p_comment_content` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '内容ID',
`comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`type` tinyint unsigned NOT NULL DEFAULT '2' COMMENT '类型1标题2文字段落3图片地址4视频地址5语音地址6链接地址',
`sort` bigint unsigned NOT NULL DEFAULT '100' COMMENT '排序,越小越靠前',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_reply` (`comment_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE,
KEY `idx_type` (`type`) USING BTREE,
KEY `idx_sort` (`sort`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11001738 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='评论内容';
-- ----------------------------
-- Table structure for p_comment_reply
-- ----------------------------
DROP TABLE IF EXISTS `p_comment_reply`;
CREATE TABLE `p_comment_reply` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '回复ID',
`comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`at_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '@用户ID',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP城市地址',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_comment` (`comment_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12000015 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='评论回复';
-- ----------------------------
-- Table structure for p_message
-- ----------------------------
DROP TABLE IF EXISTS `p_message`;
CREATE TABLE `p_message` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '消息通知ID',
`sender_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '发送方用户ID',
`receiver_user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '接收方用户ID',
`type` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '通知类型1动态2评论3回复4私信99系统通知',
`brief` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '摘要说明',
`content` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '详细内容',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '动态ID',
`comment_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论ID',
`reply_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '回复ID',
`is_read` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否已读',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_receiver` (`receiver_user_id`) USING BTREE,
KEY `idx_is_read` (`is_read`) USING BTREE,
KEY `idx_type` (`type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=16000033 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='消息通知';
-- ----------------------------
-- Table structure for p_post
-- ----------------------------
DROP TABLE IF EXISTS `p_post`;
CREATE TABLE `p_post` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主题ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`comment_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论数',
`collection_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '收藏数',
`upvote_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '点赞数',
`is_top` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否置顶',
`is_essence` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否精华',
`is_lock` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否锁定',
`latest_replied_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '最新回复时间',
`tags` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '标签',
`attachment_price` bigint unsigned NOT NULL DEFAULT '0' COMMENT '附件价格(分)',
`ip` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP地址',
`ip_loc` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'IP城市地址',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1080017989 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章';
-- ----------------------------
-- Table structure for p_post_attachment_bill
-- ----------------------------
DROP TABLE IF EXISTS `p_post_attachment_bill`;
CREATE TABLE `p_post_attachment_bill` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '购买记录ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`paid_amount` bigint unsigned NOT NULL DEFAULT '0' COMMENT '支付金额',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post` (`post_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=5000002 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章附件账单';
-- ----------------------------
-- Table structure for p_post_collection
-- ----------------------------
DROP TABLE IF EXISTS `p_post_collection`;
CREATE TABLE `p_post_collection` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '收藏ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post` (`post_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6000012 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章收藏';
-- ----------------------------
-- Table structure for p_post_content
-- ----------------------------
DROP TABLE IF EXISTS `p_post_content`;
CREATE TABLE `p_post_content` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '内容ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`content` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '内容',
`type` tinyint unsigned NOT NULL DEFAULT '2' COMMENT '类型1标题2文字段落3图片地址4视频地址5语音地址6链接地址7附件资源8收费资源',
`sort` int unsigned NOT NULL DEFAULT '100' COMMENT '排序,越小越靠前',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post` (`post_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=180022546 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章内容';
-- ----------------------------
-- Table structure for p_post_star
-- ----------------------------
DROP TABLE IF EXISTS `p_post_star`;
CREATE TABLE `p_post_star` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '收藏ID',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT 'POST ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_post` (`post_id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6000028 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章点赞';
-- ----------------------------
-- Table structure for p_tag
-- ----------------------------
DROP TABLE IF EXISTS `p_tag`;
CREATE TABLE `p_tag` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '标签ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建者ID',
`tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '标签名',
`quote_num` bigint unsigned NOT NULL DEFAULT '0' COMMENT '引用数',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_tag` (`tag`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE,
KEY `idx_num` (`quote_num`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=9000065 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='标签';
-- ----------------------------
-- Table structure for p_user
-- ----------------------------
DROP TABLE IF EXISTS `p_user`;
CREATE TABLE `p_user` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`nickname` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '昵称',
`username` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
`phone` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',
`password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'MD5密码',
`salt` varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '盐值',
`status` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '状态1正常2停用',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户头像',
`balance` bigint unsigned NOT NULL COMMENT '用户余额(分)',
`is_admin` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否管理员',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE KEY `idx_username` (`username`) USING BTREE,
KEY `idx_phone` (`phone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=100058 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户';
-- ----------------------------
-- Table structure for p_wallet_recharge
-- ----------------------------
DROP TABLE IF EXISTS `p_wallet_recharge`;
CREATE TABLE `p_wallet_recharge` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '充值ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`amount` bigint NOT NULL DEFAULT '0' COMMENT '充值金额',
`trade_no` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '支付宝订单号',
`trade_status` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '交易状态',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE,
KEY `idx_trade_no` (`trade_no`) USING BTREE,
KEY `idx_trade_status` (`trade_status`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10023 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='钱包流水';
-- ----------------------------
-- Table structure for p_wallet_statement
-- ----------------------------
DROP TABLE IF EXISTS `p_wallet_statement`;
CREATE TABLE `p_wallet_statement` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '账单ID',
`user_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '用户ID',
`change_amount` bigint NOT NULL DEFAULT '0' COMMENT '变动金额',
`balance_snapshot` bigint NOT NULL DEFAULT '0' COMMENT '资金快照',
`reason` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '变动原因',
`post_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '关联动态',
`created_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '创建时间',
`modified_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '修改时间',
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=10010 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='钱包流水';
SET FOREIGN_KEY_CHECKS = 1;

@ -0,0 +1,309 @@
PRAGMA foreign_keys = false;
-- ----------------------------
-- Table structure for p_attachment
-- ----------------------------
DROP TABLE IF EXISTS "p_attachment";
CREATE TABLE "p_attachment" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"file_size" integer NOT NULL,
"img_width" integer NOT NULL,
"img_height" integer NOT NULL,
"type" integer NOT NULL,
"content" text(255) NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_captcha
-- ----------------------------
DROP TABLE IF EXISTS "p_captcha";
CREATE TABLE "p_captcha" (
"id" integer NOT NULL,
"phone" text(16) NOT NULL,
"captcha" text(16) NOT NULL,
"use_times" integer NOT NULL,
"expired_on" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_comment
-- ----------------------------
DROP TABLE IF EXISTS "p_comment";
CREATE TABLE "p_comment" (
"id" integer NOT NULL,
"post_id" integer NOT NULL,
"user_id" integer NOT NULL,
"ip" text(64) NOT NULL,
"ip_loc" text(64) NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_comment_content
-- ----------------------------
DROP TABLE IF EXISTS "p_comment_content";
CREATE TABLE "p_comment_content" (
"id" integer NOT NULL,
"comment_id" integer NOT NULL,
"user_id" integer NOT NULL,
"content" text(255) NOT NULL,
"type" integer NOT NULL,
"sort" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_comment_reply
-- ----------------------------
DROP TABLE IF EXISTS "p_comment_reply";
CREATE TABLE "p_comment_reply" (
"id" integer NOT NULL,
"comment_id" integer NOT NULL,
"user_id" integer NOT NULL,
"at_user_id" integer NOT NULL,
"content" text(255) NOT NULL,
"ip" text(64) NOT NULL,
"ip_loc" text(64) NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_message
-- ----------------------------
DROP TABLE IF EXISTS "p_message";
CREATE TABLE "p_message" (
"id" integer NOT NULL,
"sender_user_id" integer NOT NULL,
"receiver_user_id" integer NOT NULL,
"type" integer NOT NULL,
"brief" text(255) NOT NULL,
"content" text(255) NOT NULL,
"post_id" integer NOT NULL,
"comment_id" integer NOT NULL,
"reply_id" integer NOT NULL,
"is_read" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_post
-- ----------------------------
DROP TABLE IF EXISTS "p_post";
CREATE TABLE "p_post" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"comment_count" integer NOT NULL,
"collection_count" integer NOT NULL,
"upvote_count" integer NOT NULL,
"is_top" integer NOT NULL,
"is_essence" integer NOT NULL,
"is_lock" integer NOT NULL,
"latest_replied_on" integer NOT NULL,
"tags" text(255) NOT NULL,
"attachment_price" integer NOT NULL,
"ip" text(64) NOT NULL,
"ip_loc" text(64) NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_post_attachment_bill
-- ----------------------------
DROP TABLE IF EXISTS "p_post_attachment_bill";
CREATE TABLE "p_post_attachment_bill" (
"id" integer NOT NULL,
"post_id" integer NOT NULL,
"user_id" integer NOT NULL,
"paid_amount" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_post_collection
-- ----------------------------
DROP TABLE IF EXISTS "p_post_collection";
CREATE TABLE "p_post_collection" (
"id" integer NOT NULL,
"post_id" integer NOT NULL,
"user_id" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_post_content
-- ----------------------------
DROP TABLE IF EXISTS "p_post_content";
CREATE TABLE "p_post_content" (
"id" integer NOT NULL,
"post_id" integer NOT NULL,
"user_id" integer NOT NULL,
"content" text(2000) NOT NULL,
"type" integer NOT NULL,
"sort" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_post_star
-- ----------------------------
DROP TABLE IF EXISTS "p_post_star";
CREATE TABLE "p_post_star" (
"id" integer NOT NULL,
"post_id" integer NOT NULL,
"user_id" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_tag
-- ----------------------------
DROP TABLE IF EXISTS "p_tag";
CREATE TABLE "p_tag" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"tag" text(255) NOT NULL,
"quote_num" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_user
-- ----------------------------
DROP TABLE IF EXISTS "p_user";
CREATE TABLE "p_user" (
"id" integer NOT NULL,
"nickname" text(32) NOT NULL,
"username" text(32) NOT NULL,
"phone" text(16) NOT NULL,
"password" text(32) NOT NULL,
"salt" text(16) NOT NULL,
"status" integer NOT NULL,
"avatar" text(255) NOT NULL,
"balance" integer NOT NULL,
"is_admin" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_wallet_recharge
-- ----------------------------
DROP TABLE IF EXISTS "p_wallet_recharge";
CREATE TABLE "p_wallet_recharge" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"amount" integer NOT NULL,
"trade_no" text(64) NOT NULL,
"trade_status" text(32) NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Table structure for p_wallet_statement
-- ----------------------------
DROP TABLE IF EXISTS "p_wallet_statement";
CREATE TABLE "p_wallet_statement" (
"id" integer NOT NULL,
"user_id" integer NOT NULL,
"change_amount" integer NOT NULL,
"balance_snapshot" integer NOT NULL,
"reason" text(255) NOT NULL,
"post_id" integer NOT NULL,
"created_on" integer NOT NULL,
"modified_on" integer NOT NULL,
"deleted_on" integer NOT NULL,
"is_del" integer NOT NULL,
PRIMARY KEY ("id")
);
-- ----------------------------
-- Indexes structure for table p_attachment
-- ----------------------------
CREATE INDEX "main"."idx_user"
ON "p_attachment" (
"user_id" ASC
);
-- ----------------------------
-- Indexes structure for table p_captcha
-- ----------------------------
CREATE INDEX "main"."idx_expired_on"
ON "p_captcha" (
"expired_on" ASC
);
CREATE INDEX "main"."idx_phone"
ON "p_captcha" (
"phone" ASC
);
CREATE INDEX "main"."idx_use_times"
ON "p_captcha" (
"use_times" ASC
);
-- ----------------------------
-- Indexes structure for table p_comment
-- ----------------------------
CREATE INDEX "main"."idx_post"
ON "p_comment" (
"post_id" ASC
);
PRAGMA foreign_keys = true;

@ -0,0 +1,12 @@
-- ----------------------------
-- Table p_post alter add visibility column
-- ----------------------------
ALTER TABLE `p_post` ADD COLUMN `visibility` integer NOT NULL DEFAULT '0';
-- ----------------------------
-- Indexes structure for table p_post
-- ----------------------------
CREATE INDEX "main"."idx_visibility"
ON "p_post" (
"visibility" ASC
);

@ -137,6 +137,7 @@ CREATE TABLE `p_post` (
`comment_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论数', `comment_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '评论数',
`collection_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '收藏数', `collection_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '收藏数',
`upvote_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '点赞数', `upvote_count` bigint unsigned NOT NULL DEFAULT '0' COMMENT '点赞数',
`visibility` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '可见性 0公开 1私密 2好友可见',
`is_top` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否置顶', `is_top` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否置顶',
`is_essence` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否精华', `is_essence` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否精华',
`is_lock` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否锁定', `is_lock` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否锁定',
@ -150,7 +151,8 @@ CREATE TABLE `p_post` (
`deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间', `deleted_on` bigint unsigned NOT NULL DEFAULT '0' COMMENT '删除时间',
`is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除', `is_del` tinyint unsigned NOT NULL DEFAULT '0' COMMENT '是否删除 0 为未删除、1 为已删除',
PRIMARY KEY (`id`) USING BTREE, PRIMARY KEY (`id`) USING BTREE,
KEY `idx_user` (`user_id`) USING BTREE KEY `idx_user` (`user_id`) USING BTREE,
KEY `idx_visibility` (`visibility`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1080017989 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章'; ) ENGINE=InnoDB AUTO_INCREMENT=1080017989 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='冒泡/文章';
-- ---------------------------- -- ----------------------------

@ -1,16 +1,3 @@
/*
Navicat Premium Data Transfer
Source Server : paopao-ce
Source Server Type : SQLite
Source Server Version : 3035005
Source Schema : main
Target Server Type : SQLite
Target Server Version : 3035005
File Encoding : 65001
*/
PRAGMA foreign_keys = false; PRAGMA foreign_keys = false;
-- ---------------------------- -- ----------------------------
@ -135,6 +122,7 @@ CREATE TABLE "p_post" (
"comment_count" integer NOT NULL, "comment_count" integer NOT NULL,
"collection_count" integer NOT NULL, "collection_count" integer NOT NULL,
"upvote_count" integer NOT NULL, "upvote_count" integer NOT NULL,
"visibility" integer NOT NULL,
"is_top" integer NOT NULL, "is_top" integer NOT NULL,
"is_essence" integer NOT NULL, "is_essence" integer NOT NULL,
"is_lock" integer NOT NULL, "is_lock" integer NOT NULL,
@ -181,6 +169,14 @@ CREATE TABLE "p_post_collection" (
PRIMARY KEY ("id") PRIMARY KEY ("id")
); );
-- ----------------------------
-- Indexes structure for table p_post
-- ----------------------------
CREATE INDEX "main"."idx_visibility"
ON "p_post" (
"visibility" ASC
);
-- ---------------------------- -- ----------------------------
-- Table structure for p_post_content -- Table structure for p_post_content
-- ---------------------------- -- ----------------------------

@ -1,4 +1,4 @@
{ {
"version": "3", "version": "8",
"buildTime": "2022-06-08 23:29:43" "buildTime": "2022-06-13 17:16:22"
} }

@ -108,6 +108,15 @@ export const stickPost = (data: NetParams.PostStickPost): Promise<NetReq.PostSti
}); });
}; };
/** 置顶/取消置顶动态 */
export const visibilityPost = (data: NetParams.PostVisibilityPost): Promise<NetReq.PostVisibilityPost> => {
return request({
method: 'post',
url: '/v1/post/visibility',
data
});
};
/** 发布动态评论 */ /** 发布动态评论 */
export const createComment = (data: NetParams.PostCreateComment): Promise<NetReq.PostCreateComment> => { export const createComment = (data: NetParams.PostCreateComment): Promise<NetReq.PostCreateComment> => {
return request({ return request({

@ -141,6 +141,19 @@
</n-icon> </n-icon>
</template> </template>
</n-button> </n-button>
<n-button
quaternary
circle
type="primary"
@click.stop="switchEye"
>
<template #icon>
<n-icon size="20" color="var(--primary-color)">
<eye-outline />
</n-icon>
</template>
</n-button>
</div> </div>
<div class="submit-wrap"> <div class="submit-wrap">
@ -200,6 +213,19 @@
<template #create-button-default> </template> <template #create-button-default> </template>
</n-dynamic-input> </n-dynamic-input>
</div> </div>
<div class="eye-wrap" v-if="showEyeSet">
<n-radio-group v-model:value="visitType" name="radiogroup">
<n-space>
<n-radio
v-for="visit in visibilities"
:key="visit.value"
:value="visit.value"
:label="visit.label"
/>
</n-space>
</n-radio-group>
</div>
</div> </div>
<div class="compose-wrap" v-else> <div class="compose-wrap" v-else>
@ -242,10 +268,14 @@ import {
VideocamOutline, VideocamOutline,
AttachOutline, AttachOutline,
CompassOutline, CompassOutline,
EyeOutline,
} from '@vicons/ionicons5'; } from '@vicons/ionicons5';
import { createPost } from '@/api/post'; import { createPost } from '@/api/post';
import { parsePostTag } from '@/utils/content'; import { parsePostTag } from '@/utils/content';
import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui'; import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui';
import { VisibilityEnum, PostItemTypeEnum } from '@/utils/IEnum';
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'post-success', post: Item.PostProps): void; (e: 'post-success', post: Item.PostProps): void;
@ -257,8 +287,10 @@ const optionsRef = ref<MentionOption[]>([]);
const loading = ref(false); const loading = ref(false);
const submitting = ref(false); const submitting = ref(false);
const showLinkSet = ref(false); const showLinkSet = ref(false);
const showEyeSet = ref(false);
const content = ref(''); const content = ref('');
const links = ref([]); const links = ref([]);
const uploadRef = ref<UploadInst>(); const uploadRef = ref<UploadInst>();
const attachmentPrice = ref(0); const attachmentPrice = ref(0);
const uploadType = ref('public/image'); const uploadType = ref('public/image');
@ -266,6 +298,12 @@ const fileQueue = ref<UploadFileInfo[]>([]);
const imageContents = ref<Item.CommentItemProps[]>([]); const imageContents = ref<Item.CommentItemProps[]>([]);
const videoContents = ref<Item.CommentItemProps[]>([]); const videoContents = ref<Item.CommentItemProps[]>([]);
const attachmentContents = ref<Item.AttachmentProps[]>([]); const attachmentContents = ref<Item.AttachmentProps[]>([]);
const visitType = ref<VisibilityEnum>(VisibilityEnum.PUBLIC);
const visibilities = [
{value: VisibilityEnum.PUBLIC, label: "公开"}
, {value: VisibilityEnum.PRIVATE, label: "私密"}
, {value: VisibilityEnum.FRIEND, label: "好友可见"}
];
const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment'; const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment';
const uploadToken = ref(); const uploadToken = ref();
@ -277,6 +315,10 @@ const switchLink = () => {
} }
}; };
const switchEye = () => {
showEyeSet.value = !showEyeSet.value;
};
// 加载at用户列表 // 加载at用户列表
const loadSuggestionUsers = debounce((k) => { const loadSuggestionUsers = debounce((k) => {
getSuggestUsers({ getSuggestUsers({
@ -468,7 +510,7 @@ const submitPost = () => {
contents.push({ contents.push({
content: content.value, content: content.value,
type: 2, // 文字 type: PostItemTypeEnum.TEXT, // 文字
sort, sort,
}); });
@ -476,7 +518,7 @@ const submitPost = () => {
sort++; sort++;
contents.push({ contents.push({
content: img.content, content: img.content,
type: 3, // 图片 type: PostItemTypeEnum.IMAGEURL, // 图片
sort, sort,
}); });
}); });
@ -484,7 +526,7 @@ const submitPost = () => {
sort++; sort++;
contents.push({ contents.push({
content: video.content, content: video.content,
type: 4, // 图片 type: PostItemTypeEnum.VIDEOURL, // 视频
sort, sort,
}); });
}); });
@ -492,7 +534,7 @@ const submitPost = () => {
sort++; sort++;
contents.push({ contents.push({
content: attachment.content, content: attachment.content,
type: 7, // 附件 type: PostItemTypeEnum.ATTACHMENT, // 附件
sort, sort,
}); });
}); });
@ -501,7 +543,7 @@ const submitPost = () => {
sort++; sort++;
contents.push({ contents.push({
content: link, content: link,
type: 6, // 链接 type: PostItemTypeEnum.LINKURL, // 链接
sort, sort,
}); });
}); });
@ -513,6 +555,7 @@ const submitPost = () => {
tags: Array.from(new Set(tags)), tags: Array.from(new Set(tags)),
users: Array.from(new Set(users)), users: Array.from(new Set(users)),
attachment_price: +attachmentPrice.value * 100, attachment_price: +attachmentPrice.value * 100,
visibility: visitType.value
}) })
.then((res) => { .then((res) => {
window.$message.success(''); window.$message.success('');
@ -521,6 +564,7 @@ const submitPost = () => {
// 置空 // 置空
showLinkSet.value = false; showLinkSet.value = false;
showEyeSet.value = false;
uploadRef.value?.clear(); uploadRef.value?.clear();
fileQueue.value = []; fileQueue.value = [];
content.value = ''; content.value = '';
@ -528,6 +572,7 @@ const submitPost = () => {
imageContents.value = []; imageContents.value = [];
videoContents.value = []; videoContents.value = [];
attachmentContents.value = []; attachmentContents.value = [];
visitType.value = VisibilityEnum.PUBLIC;
}) })
.catch((err) => { .catch((err) => {
submitting.value = false; submitting.value = false;
@ -597,4 +642,7 @@ onMounted(() => {
overflow: hidden; overflow: hidden;
} }
} }
.eye-wrap {
margin-left: 64px;
}
</style> </style>

@ -16,6 +16,33 @@
{{ post.user.nickname }} {{ post.user.nickname }}
</router-link> </router-link>
<span class="username-wrap"> @{{ post.user.username }} </span> <span class="username-wrap"> @{{ post.user.username }} </span>
<n-tag
v-if="post.is_top"
class="top-tag"
type="warning"
size="small"
round
>
</n-tag>
<n-tag
v-if="post.visibility == VisibilityEnum.PRIVATE"
class="top-tag"
type="error"
size="small"
round
>
</n-tag>
<n-tag
v-if="post.visibility == VisibilityEnum.FRIEND"
class="top-tag"
type="info"
size="small"
round
>
</n-tag>
</template> </template>
<template #header-extra> <template #header-extra>
<div <div
@ -83,6 +110,21 @@
negative-text="取消" negative-text="取消"
@positive-click="execStickAction" @positive-click="execStickAction"
/> />
<!-- -->
<n-modal
v-model:show="showVisibilityModal"
:mask-closable="false"
preset="dialog"
title="提示"
:content="
'' +
(tempVisibility == 0 ? '' : (tempVisibility == 1 ? '' : '')) +
''
"
positive-text="确认"
negative-text="取消"
@positive-click="execVisibilityAction"
/>
</template> </template>
<div v-if="post.texts.length > 0"> <div v-if="post.texts.length > 0">
<span <span
@ -174,7 +216,10 @@ import {
deletePost, deletePost,
lockPost, lockPost,
stickPost, stickPost,
visibilityPost
} from '@/api/post'; } from '@/api/post';
import type { DropdownOption } from 'naive-ui';
import { VisibilityEnum } from '@/utils/IEnum';
const store = useStore(); const store = useStore();
const router = useRouter(); const router = useRouter();
@ -189,7 +234,9 @@ const props = withDefaults(
const showDelModal = ref(false); const showDelModal = ref(false);
const showLockModal = ref(false); const showLockModal = ref(false);
const showStickModal = ref(false); const showStickModal = ref(false);
const showVisibilityModal = ref(false);
const loading = ref(false); const loading = ref(false);
const tempVisibility = ref<VisibilityEnum>(VisibilityEnum.PUBLIC);
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'reload'): void; (e: 'reload'): void;
@ -238,7 +285,7 @@ const post = computed({
}); });
const adminOptions = computed(() => { const adminOptions = computed(() => {
let options = [ let options: DropdownOption[] = [
{ {
label: '', label: '',
key: 'delete', key: 'delete',
@ -268,6 +315,34 @@ const adminOptions = computed(() => {
}); });
} }
} }
if (post.value.visibility === VisibilityEnum.PUBLIC) {
options.push({
label: '',
key: 'vpublic',
children: [
{ label: '', key: 'vprivate' }
, { label: '', key: 'vfriend' }
]
})
} else if (post.value.visibility === VisibilityEnum.PRIVATE) {
options.push({
label: '',
key: 'vprivate',
children: [
{ label: '', key: 'vpublic' }
, { label: '', key: 'vfriend' }
]
})
} else {
options.push({
label: '',
key: 'vfriend',
children: [
{ label: '', key: 'vpublic' }
, { label: '', key: 'vprivate' }
]
})
}
return options; return options;
}); });
@ -306,16 +381,34 @@ const doClickText = (e: MouseEvent, id: number) => {
goPostDetail(id); goPostDetail(id);
}; };
const handlePostAction = ( const handlePostAction = (
item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick' item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick' | 'vpublic' | 'vprivate' | 'vfriend'
) => { ) => {
if (item === 'delete') { switch (item) {
showDelModal.value = true; case 'delete':
} showDelModal.value = true;
if (item === 'lock' || item === 'unlock') { break;
showLockModal.value = true; case 'lock':
} case 'unlock':
if (item === 'stick' || item === 'unstick') { showLockModal.value = true;
showStickModal.value = true; break;
case 'stick':
case 'unstick':
showStickModal.value = true;
break;
case 'vpublic':
tempVisibility.value = 0;
showVisibilityModal.value = true;
break;
case 'vprivate':
tempVisibility.value = 1;
showVisibilityModal.value = true;
break;
case 'vfriend':
tempVisibility.value = 2;
showVisibilityModal.value = true;
break;
default:
break;
} }
}; };
const execDelAction = () => { const execDelAction = () => {
@ -366,6 +459,19 @@ const execStickAction = () => {
loading.value = false; loading.value = false;
}); });
}; };
const execVisibilityAction = () => {
visibilityPost({
id: post.value.id,
visibility: tempVisibility.value
})
.then((res) => {
emit('reload');
window.$message.success('');
})
.catch((err) => {
loading.value = false;
});
};
const handlePostStar = () => { const handlePostStar = () => {
postStar({ postStar({
id: post.value.id, id: post.value.id,
@ -450,7 +556,9 @@ onMounted(() => {
font-size: 14px; font-size: 14px;
opacity: 0.75; opacity: 0.75;
} }
.top-tag {
transform: scale(0.75);
}
.options { .options {
opacity: 0.75; opacity: 0.75;
} }

@ -27,6 +27,24 @@
> >
</n-tag> </n-tag>
<n-tag
v-if="post.visibility == 1"
class="top-tag"
type="error"
size="small"
round
>
</n-tag>
<n-tag
v-if="post.visibility == 2"
class="top-tag"
type="info"
size="small"
round
>
</n-tag>
</template> </template>
<template #header-extra> <template #header-extra>
<span class="timestamp"> <span class="timestamp">

@ -17,4 +17,4 @@ declare global {
$message: MessageApiInjection, $message: MessageApiInjection,
$store: any $store: any
} }
} }

@ -28,7 +28,7 @@ declare module Item {
/** 评论者UID */ /** 评论者UID */
user_id: number, user_id: number,
/** 类别1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址 */ /** 类别1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址 */
type: number, type: import('@/utils/IEnum').CommentItemTypeEnum,
/** 内容 */ /** 内容 */
content: string, content: string,
/** 排序,越小越靠前 */ /** 排序,越小越靠前 */
@ -111,7 +111,7 @@ declare module Item {
/** 内容ID */ /** 内容ID */
id: number, id: number,
/** 类型1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址7为附件资源8为收费资源 */ /** 类型1为标题2为文字段落3为图片地址4为视频地址5为语音地址6为链接地址7为附件资源8为收费资源 */
type: number, type: import('@/utils/IEnum').PostItemTypeEnum,
/** POST ID */ /** POST ID */
post_id: number, post_id: number,
/** 内容 */ /** 内容 */
@ -161,6 +161,8 @@ declare module Item {
contents: PostItemProps[], contents: PostItemProps[],
/** 标签列表 */ /** 标签列表 */
tags: { [key: string]: number } | string, tags: { [key: string]: number } | string,
/** 可见性0为公开1为私密2为好友可见 */
visibility: import('@/utils/IEnum').VisibilityEnum,
/** 是否锁定 */ /** 是否锁定 */
is_lock: number, is_lock: number,
/** 是否置顶 */ /** 是否置顶 */
@ -190,7 +192,7 @@ declare module Item {
interface MessageProps { interface MessageProps {
id: number, id: number,
/** 类型1为动态2为评论3为回复4为私信99为系统通知 */ /** 类型1为动态2为评论3为回复4为私信99为系统通知 */
type: 1 | 2 | 3 | 4 | 99, type: import('@/utils/IEnum').MessageTypeEnum,
/** 摘要说明 */ /** 摘要说明 */
brief: string, brief: string,
/** 详细内容 */ /** 详细内容 */
@ -228,7 +230,7 @@ declare module Item {
interface AttachmentProps { interface AttachmentProps {
id: number, id: number,
/** 类别1为图片2为视频3为其他附件 */ /** 类别1为图片2为视频3为其他附件 */
type: 1 | 2 | 3, type: import('@/utils/IEnum').AttachmentTypeEnum,
/** 发布者用户UID */ /** 发布者用户UID */
user_id: number, user_id: number,
/** 发布者用户数据 */ /** 发布者用户数据 */
@ -287,4 +289,4 @@ declare module Item {
created_on: number created_on: number
} }
} }

@ -124,6 +124,12 @@ declare module NetParams {
id: number id: number
} }
interface PostVisibilityPost {
id: number,
/** 可见性0为公开1为私密2为好友可见 */
visibility: import('@/utils/IEnum').VisibilityEnum
}
interface PostGetPostStar { interface PostGetPostStar {
id: number id: number
} }
@ -157,7 +163,9 @@ declare module NetParams {
/** 艾特用户列表 */ /** 艾特用户列表 */
users: string[], users: string[],
/** 附件价格 */ /** 附件价格 */
attachment_price: number attachment_price: number,
/** 可见性0为公开1为私密2为好友可见 */
visibility: import('@/utils/IEnum').VisibilityEnum
} }
interface PostDeletePost { interface PostDeletePost {

@ -116,6 +116,11 @@ declare module NetReq {
top_status: 0 | 1 top_status: 0 | 1
} }
interface PostVisibilityPost {
/** 可见性0为公开1为私密2为好友可见 */
visibility_status: import('@/utils/IEnum').VisibilityEnum
}
interface PostGetPostStar { interface PostGetPostStar {
status: boolean status: boolean
} }

@ -0,0 +1,69 @@
/** 动态内容类型枚举 */
export enum PostItemTypeEnum {
/** 标题 */
TITLE = 1,
/** 文字段落 */
TEXT = 2,
/** 图片地址 */
IMAGEURL = 3,
/** 视频地址 */
VIDEOURL = 4,
/** 音频地址 */
AUDIOURL = 5,
/** 链接地址 */
LINKURL = 6,
/** 附件资源 */
ATTACHMENT = 7,
/** 收费资源 */
CHARGEATTACHMENT = 8
}
/** 回复内容类型枚举 */
export enum CommentItemTypeEnum {
/** 标题 */
TITLE = 1,
/** 文字段落 */
TEXT = 2,
/** 图片地址 */
IMAGEURL = 3,
/** 视频地址 */
VIDEOURL = 4,
/** 音频地址 */
AUDIOURL = 5,
/** 链接地址 */
LINKURL = 6
}
/** 附件类型枚举 */
export enum AttachmentTypeEnum {
/** 图片 */
IMAGE = 1,
/** 视频 */
VIDEO = 2,
/** 其他 */
OTHER = 3
}
/** 消息类型枚举 */
export enum MessageTypeEnum {
/** 动态 */
POST = 1,
/** 评论 */
COMMENT = 2,
/** 回复 */
REPLY = 3,
/** 私信 */
PRIVATELETTER = 4,
/** 系统通知 */
SYSTEMNOTICE = 99
}
/** 动态可见度枚举 */
export enum VisibilityEnum {
/** 公开 */
PUBLIC,
/** 私密 */
PRIVATE,
/** 好友可见 */
FRIEND
}

@ -102,7 +102,7 @@
<n-form-item path="phone" label="手机号"> <n-form-item path="phone" label="手机号">
<n-input <n-input
:value="modelData.phone" :value="modelData.phone"
@update:value="(v) => (modelData.phone = v.trim())" @update:value="(v: string) => (modelData.phone = v.trim())"
placeholder="请输入中国大陆手机号" placeholder="请输入中国大陆手机号"
@keydown.enter.prevent @keydown.enter.prevent
/> />

Loading…
Cancel
Save