From 6f355c10b7e5f7f88178169ed308271818a2be3c Mon Sep 17 00:00:00 2001 From: alimy Date: Mon, 13 Jun 2022 17:50:43 +0800 Subject: [PATCH 01/11] support set visibility(public/private/friend) for post --- internal/core/core.go | 3 ++ internal/core/index.go | 2 +- internal/core/search.go | 6 ++- internal/dao/cache_index.go | 6 +-- internal/dao/dao.go | 3 +- internal/dao/post_index.go | 12 +++--- internal/dao/user.go | 5 +++ internal/middleware/jwt.go | 33 +++++++++++++++ internal/model/post.go | 35 ++++++++++------ internal/routers/api/post.go | 3 +- internal/routers/api/user.go | 20 ++++++--- internal/routers/router.go | 6 ++- internal/service/post.go | 23 ++++++++--- internal/service/user.go | 4 ++ scripts/migration/mysql/001-2206131310.sql | 9 ++++ scripts/migration/sqlite3/001-2206131310.sql | 12 ++++++ web/build/info.json | 4 +- web/src/components/compose.vue | 43 ++++++++++++++++++++ web/src/types/NetParams.d.ts | 4 +- web/src/types/item.d.ts | 2 + 20 files changed, 196 insertions(+), 39 deletions(-) create mode 100644 scripts/migration/mysql/001-2206131310.sql create mode 100644 scripts/migration/sqlite3/001-2206131310.sql diff --git a/internal/core/core.go b/internal/core/core.go index f815525b..10ae82bf 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -36,6 +36,7 @@ type DataService interface { StickPost(post *model.Post) error GetPostByID(id int64) (*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) UpdatePost(post *model.Post) error GetUserPostStar(postID, userID int64) (*model.PostStar, error) @@ -70,4 +71,6 @@ type DataService interface { GetLatestPhoneCaptcha(phone string) (*model.Captcha, error) UsePhoneCaptcha(captcha *model.Captcha) error SendPhoneCaptcha(phone string) error + + IsFriend(userID int64, friendID int64) bool } diff --git a/internal/core/index.go b/internal/core/index.go index afc02048..0188e6a2 100644 --- a/internal/core/index.go +++ b/internal/core/index.go @@ -5,5 +5,5 @@ import ( ) type IndexPostsService interface { - IndexPosts(offset int, limit int) ([]*model.PostFormated, error) + IndexPosts(userId int64, offset int, limit int) ([]*model.PostFormated, error) } diff --git a/internal/core/search.go b/internal/core/search.go index b2033cd3..823da0d3 100644 --- a/internal/core/search.go +++ b/internal/core/search.go @@ -1,6 +1,7 @@ package core import ( + "github.com/rocboss/paopao-ce/internal/model" "github.com/rocboss/paopao-ce/pkg/zinc" ) @@ -12,8 +13,9 @@ const ( type SearchType string type QueryT struct { - Query string - Type SearchType + Query string + Visibility []model.PostVisibleT + Type SearchType } // SearchService search service interface that implement base zinc diff --git a/internal/dao/cache_index.go b/internal/dao/cache_index.go index b8626be9..886cdb42 100644 --- a/internal/dao/cache_index.go +++ b/internal/dao/cache_index.go @@ -14,7 +14,7 @@ var ( 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 cacheIndex := &simpleCacheIndexServant{ getIndexPosts: getIndexPosts, @@ -48,7 +48,7 @@ func newSimpleCacheIndexServant(getIndexPosts func(offset, limit int) ([]*model. 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) end := offset + limit size := len(posts) @@ -78,7 +78,7 @@ func (s *simpleCacheIndexServant) startIndexPosts() { case <-s.checkTick.C: if len(s.indexPosts) == 0 { 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) } else { logrus.Errorf("get index posts err: %v", err) diff --git a/internal/dao/dao.go b/internal/dao/dao.go index 59634bfb..fb82b71c 100644 --- a/internal/dao/dao.go +++ b/internal/dao/dao.go @@ -32,8 +32,9 @@ type dataServant struct { zinc *zinc.ZincClient } +type indexPostsFunc func(int64, int, int) ([]*model.PostFormated, error) type simpleCacheIndexServant struct { - getIndexPosts func(offset, limit int) ([]*model.PostFormated, error) + getIndexPosts indexPostsFunc indexActionCh chan core.IndexActionT indexPosts []*model.PostFormated atomicIndex atomic.Value diff --git a/internal/dao/post_index.go b/internal/dao/post_index.go index 0a184dd7..4353e938 100644 --- a/internal/dao/post_index.go +++ b/internal/dao/post_index.go @@ -6,15 +6,15 @@ import ( "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 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") return posts, nil } } 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) { @@ -56,9 +56,11 @@ func (d *dataServant) MergePosts(posts []*model.Post) ([]*model.PostFormated, er 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{ - "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) if err != nil { logrus.Debugf("getIndexPosts err: %v", err) diff --git a/internal/dao/user.go b/internal/dao/user.go index ab0d72a8..ce07f5c9 100644 --- a/internal/dao/user.go +++ b/internal/dao/user.go @@ -158,3 +158,8 @@ func (d *dataServant) SendPhoneCaptcha(phone string) error { captchaModel.Create(d.engine) return nil } + +func (d *dataServant) IsFriend(_userID int64, _friendID int64) bool { + // TODO: you are friend in all now + return true +} diff --git a/internal/middleware/jwt.go b/internal/middleware/jwt.go index e8e99950..fc6db41e 100644 --- a/internal/middleware/jwt.go +++ b/internal/middleware/jwt.go @@ -74,3 +74,36 @@ func JWT() gin.HandlerFunc { 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() + } +} diff --git a/internal/model/post.go b/internal/model/post.go index 3ce80414..39bc5844 100644 --- a/internal/model/post.go +++ b/internal/model/post.go @@ -7,20 +7,30 @@ import ( "gorm.io/gorm" ) +// PostVisibleT 可访问类型,0公开,1私密,2好友 +type PostVisibleT int + +const ( + PostVisitPublic PostVisibleT = iota + PostVisitPrivate + PostVisitFriend +) + type Post struct { *Model - UserID int64 `json:"user_id"` - CommentCount int64 `json:"comment_count"` - CollectionCount int64 `json:"collection_count"` - UpvoteCount int64 `json:"upvote_count"` - IsTop int `json:"is_top"` - IsEssence int `json:"is_essence"` - IsLock int `json:"is_lock"` - LatestRepliedOn int64 `json:"latest_replied_on"` - Tags string `json:"tags"` - AttachmentPrice int64 `json:"attachment_price"` - IP string `json:"ip"` - IPLoc string `json:"ip_loc"` + UserID int64 `json:"user_id"` + CommentCount int64 `json:"comment_count"` + CollectionCount int64 `json:"collection_count"` + UpvoteCount int64 `json:"upvote_count"` + Visibility PostVisibleT `json:"visibility"` + IsTop int `json:"is_top"` + IsEssence int `json:"is_essence"` + IsLock int `json:"is_lock"` + LatestRepliedOn int64 `json:"latest_replied_on"` + Tags string `json:"tags"` + AttachmentPrice int64 `json:"attachment_price"` + IP string `json:"ip"` + IPLoc string `json:"ip_loc"` } type PostFormated struct { @@ -31,6 +41,7 @@ type PostFormated struct { CommentCount int64 `json:"comment_count"` CollectionCount int64 `json:"collection_count"` UpvoteCount int64 `json:"upvote_count"` + Visibility PostVisibleT `json:"visibility"` IsTop int `json:"is_top"` IsEssence int `json:"is_essence"` IsLock int `json:"is_lock"` diff --git a/internal/routers/api/post.go b/internal/routers/api/post.go index 8b4f3703..c1452b8f 100644 --- a/internal/routers/api/post.go +++ b/internal/routers/api/post.go @@ -31,7 +31,8 @@ func GetPostList(c *gin.Context) { return } 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) diff --git a/internal/routers/api/user.go b/internal/routers/api/user.go index 94b3bcb9..f7ca4633 100644 --- a/internal/routers/api/user.go +++ b/internal/routers/api/user.go @@ -313,13 +313,23 @@ func GetUserPosts(c *gin.Context) { return } - conditions := &model.ConditionsT{ - "user_id": user.ID, - "ORDER": "latest_replied_on DESC", + visibilities := []model.PostVisibleT{model.PostVisitPublic} + if u, exists := c.Get("USER"); exists { + 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{ - Conditions: conditions, + Conditions: &conditions, Offset: (app.GetPage(c) - 1) * app.GetPageSize(c), Limit: app.GetPageSize(c), }) @@ -328,7 +338,7 @@ func GetUserPosts(c *gin.Context) { response.ToErrorResponse(errcode.GetPostsFailed) return } - totalRows, _ := service.GetPostCount(conditions) + totalRows, _ := service.GetPostCount(&conditions) response.ToResponseList(posts, totalRows) } diff --git a/internal/routers/router.go b/internal/routers/router.go index 2a478664..9465ba4b 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -69,9 +69,13 @@ func NewRouter() *gin.Engine { // 获取用户基本信息 noAuthApi.GET("/user/profile", api.GetUserProfile) + } + // 宽松鉴权路由组 + looseApi := r.Group("/").Use(middleware.JwtLoose()) + { // 获取用户动态列表 - noAuthApi.GET("/user/posts", api.GetUserPosts) + looseApi.GET("/user/posts", api.GetUserPosts) } // 鉴权路由组 diff --git a/internal/service/post.go b/internal/service/post.go index 00bc494c..b65ff0d9 100644 --- a/internal/service/post.go +++ b/internal/service/post.go @@ -36,6 +36,7 @@ type PostCreationReq struct { Tags []string `json:"tags" binding:"required"` Users []string `json:"users" binding:"required"` AttachmentPrice int64 `json:"attachment_price"` + Visibility model.PostVisibleT `json:"visibility"` } type PostDelReq struct { @@ -92,6 +93,7 @@ func CreatePost(c *gin.Context, userID int64, param PostCreationReq) (*model.Pos IP: ip, IPLoc: util.GetIPLoc(ip), AttachmentPrice: param.AttachmentPrice, + Visibility: param.Visibility, } post, err := ds.CreatePost(post) if err != nil { @@ -325,7 +327,7 @@ func GetPostContentByID(id int64) (*model.PostContent, 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) { @@ -411,6 +413,11 @@ func GetPostListFromSearchByQuery(query string, offset, limit int) ([]*model.Pos } func PushPostToSearch(post *model.Post) { + // TODO: 暂时不索引私密文章,后续再完善 + if post.Visibility == model.PostVisitPrivate { + return + } + indexName := conf.ZincSetting.Index postFormated := post.Format() @@ -447,6 +454,7 @@ func PushPostToSearch(post *model.Post) { "comment_count": post.CommentCount, "collection_count": post.CollectionCount, "upvote_count": post.UpvoteCount, + "visibility": post.Visibility, "is_top": post.IsTop, "is_essence": post.IsEssence, "content": contentFormated, @@ -470,7 +478,9 @@ func DeleteSearchPost(post *model.Post) error { func PushPostsToSearch(c *gin.Context) { if ok, _ := conf.Redis.SetNX(c, "JOB_PUSH_TO_SEARCH", 1, time.Hour).Result(); ok { 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)) nums := int(pages) @@ -483,9 +493,11 @@ func PushPostsToSearch(c *gin.Context) { data := []map[string]interface{}{} posts, _ := GetPostList(&PostListReq{ - Conditions: &model.ConditionsT{}, - Offset: i * splitNum, - Limit: splitNum, + Conditions: &model.ConditionsT{ + "visibility IN ?": []model.PostVisibleT{model.PostVisitPublic, model.PostVisitFriend}, + }, + Offset: i * splitNum, + Limit: splitNum, }) for _, post := range posts { @@ -508,6 +520,7 @@ func PushPostsToSearch(c *gin.Context) { "comment_count": post.CommentCount, "collection_count": post.CollectionCount, "upvote_count": post.UpvoteCount, + "visibility": post.Visibility, "is_top": post.IsTop, "is_essence": post.IsEssence, "content": contentFormated, diff --git a/internal/service/user.go b/internal/service/user.go index 25f5a225..5b4edfc4 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -390,3 +390,7 @@ func GetSuggestTags(keyword string) ([]string, error) { return ts, nil } + +func IsFriend(userId, friendId int64) bool { + return ds.IsFriend(userId, friendId) +} diff --git a/scripts/migration/mysql/001-2206131310.sql b/scripts/migration/mysql/001-2206131310.sql new file mode 100644 index 00000000..c654a3bb --- /dev/null +++ b/scripts/migration/mysql/001-2206131310.sql @@ -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; diff --git a/scripts/migration/sqlite3/001-2206131310.sql b/scripts/migration/sqlite3/001-2206131310.sql new file mode 100644 index 00000000..63657693 --- /dev/null +++ b/scripts/migration/sqlite3/001-2206131310.sql @@ -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 +); diff --git a/web/build/info.json b/web/build/info.json index f430d3b0..290a7eb3 100644 --- a/web/build/info.json +++ b/web/build/info.json @@ -1,4 +1,4 @@ { - "version": "3", - "buildTime": "2022-06-08 23:29:43" + "version": "8", + "buildTime": "2022-06-13 17:16:22" } \ No newline at end of file diff --git a/web/src/components/compose.vue b/web/src/components/compose.vue index 8d5d8771..90961614 100644 --- a/web/src/components/compose.vue +++ b/web/src/components/compose.vue @@ -141,6 +141,19 @@ + + + +
@@ -200,6 +213,19 @@
+ +
+ + + + + +
@@ -242,11 +268,14 @@ import { VideocamOutline, AttachOutline, CompassOutline, + EyeOutline, } from '@vicons/ionicons5'; import { createPost } from '@/api/post'; import { parsePostTag } from '@/utils/content'; import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui'; + + const emit = defineEmits<{ (e: 'post-success', post: Item.PostProps): void; }>(); @@ -257,8 +286,10 @@ const optionsRef = ref([]); const loading = ref(false); const submitting = ref(false); const showLinkSet = ref(false); +const showEyeSet = ref(false); const content = ref(''); const links = ref([]); + const uploadRef = ref(); const attachmentPrice = ref(0); const uploadType = ref('public/image'); @@ -266,6 +297,8 @@ const fileQueue = ref([]); const imageContents = ref([]); const videoContents = ref([]); const attachmentContents = ref([]); +const visitType = ref(0) +const visibilities = [{value: 0, label: "公开"}, {value: 1, label: "私密"}, {value: 2, label: "好友可见"}] const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment'; const uploadToken = ref(); @@ -277,6 +310,10 @@ const switchLink = () => { } }; +const switchEye = () => { + showEyeSet.value = !showEyeSet.value; +}; + // 加载at用户列表 const loadSuggestionUsers = debounce((k) => { getSuggestUsers({ @@ -513,6 +550,7 @@ const submitPost = () => { tags: Array.from(new Set(tags)), users: Array.from(new Set(users)), attachment_price: +attachmentPrice.value * 100, + visibility: visitType.value }) .then((res) => { window.$message.success('发布成功'); @@ -521,6 +559,7 @@ const submitPost = () => { // 置空 showLinkSet.value = false; + showEyeSet.value = false; uploadRef.value?.clear(); fileQueue.value = []; content.value = ''; @@ -528,6 +567,7 @@ const submitPost = () => { imageContents.value = []; videoContents.value = []; attachmentContents.value = []; + visitType.value = 0; }) .catch((err) => { submitting.value = false; @@ -597,4 +637,7 @@ onMounted(() => { overflow: hidden; } } +.eye-wrap { + margin-left: 64px; +} \ No newline at end of file diff --git a/web/src/types/NetParams.d.ts b/web/src/types/NetParams.d.ts index a26a2c6d..122e3189 100644 --- a/web/src/types/NetParams.d.ts +++ b/web/src/types/NetParams.d.ts @@ -157,7 +157,9 @@ declare module NetParams { /** 艾特用户列表 */ users: string[], /** 附件价格 */ - attachment_price: number + attachment_price: number, + /** 可见性 0公开 1私密 2好友可见 */ + visibility: 0 | 1 | 2 } interface PostDeletePost { diff --git a/web/src/types/item.d.ts b/web/src/types/item.d.ts index 6202929a..bc369592 100644 --- a/web/src/types/item.d.ts +++ b/web/src/types/item.d.ts @@ -161,6 +161,8 @@ declare module Item { contents: PostItemProps[], /** 标签列表 */ tags: { [key: string]: number } | string, + /** 可见性 0公开 1私密 2好友可见 */ + visibility: 0 | 1 | 2, /** 是否锁定 */ is_lock: number, /** 是否置顶 */ From f84cd49a18ca597082a692c80c1c7c7a0a995901 Mon Sep 17 00:00:00 2001 From: alimy Date: Mon, 13 Jun 2022 20:43:24 +0800 Subject: [PATCH 02/11] optimize #97 view tag info of (friend/private) in post item --- internal/model/post.go | 1 + web/src/components/post-detail.vue | 31 +++++++++++++++++++++++++++++- web/src/components/post-item.vue | 18 +++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/internal/model/post.go b/internal/model/post.go index 39bc5844..3f08f7a1 100644 --- a/internal/model/post.go +++ b/internal/model/post.go @@ -67,6 +67,7 @@ func (p *Post) Format() *PostFormated { CommentCount: p.CommentCount, CollectionCount: p.CollectionCount, UpvoteCount: p.UpvoteCount, + Visibility: p.Visibility, IsTop: p.IsTop, IsEssence: p.IsEssence, IsLock: p.IsLock, diff --git a/web/src/components/post-detail.vue b/web/src/components/post-detail.vue index af283e9c..f937b10c 100644 --- a/web/src/components/post-detail.vue +++ b/web/src/components/post-detail.vue @@ -16,6 +16,33 @@ {{ post.user.nickname }} @{{ post.user.username }} + + 置顶 + + + 私密 + + + 好友可见 +
(0); const emit = defineEmits<{ (e: 'reload'): void; @@ -265,7 +284,7 @@ const post = computed({ }); const adminOptions = computed(() => { - let options = [ + let options: DropdownOption[] = [ { label: '删除', key: 'delete', @@ -295,6 +314,34 @@ const adminOptions = computed(() => { }); } } + if (post.value.visibility === 0) { + options.push({ + label: '公开', + key: 'vpublic', + children: [ + { label: '私密', key: 'vprivate' } + , { label: '好友可见', key: 'vfriend' } + ] + }) + } else if (post.value.visibility === 1) { + 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; }); @@ -333,16 +380,34 @@ const doClickText = (e: MouseEvent, id: number) => { goPostDetail(id); }; const handlePostAction = ( - item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick' + item: 'delete' | 'lock' | 'unlock' | 'stick' | 'unstick' | 'vpublic' | 'vprivate' | 'vfriend' ) => { - if (item === 'delete') { - showDelModal.value = true; - } - if (item === 'lock' || item === 'unlock') { - showLockModal.value = true; - } - if (item === 'stick' || item === 'unstick') { - showStickModal.value = true; + switch (item) { + case 'delete': + showDelModal.value = true; + break; + case 'lock': + case 'unlock': + showLockModal.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 = () => { @@ -393,6 +458,19 @@ const execStickAction = () => { 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 = () => { postStar({ id: post.value.id, diff --git a/web/src/types/NetParams.d.ts b/web/src/types/NetParams.d.ts index 122e3189..b6371564 100644 --- a/web/src/types/NetParams.d.ts +++ b/web/src/types/NetParams.d.ts @@ -124,6 +124,12 @@ declare module NetParams { id: number } + interface PostVisibilityPost { + id: number, + /** 可见性 0公开 1私密 2好友可见 */ + visibility: 0 | 1 | 2 + } + interface PostGetPostStar { id: number } diff --git a/web/src/types/NetReq.d.ts b/web/src/types/NetReq.d.ts index 8c34f61e..ffc6c55d 100644 --- a/web/src/types/NetReq.d.ts +++ b/web/src/types/NetReq.d.ts @@ -116,6 +116,11 @@ declare module NetReq { top_status: 0 | 1 } + interface PostVisibilityPost { + /** 可见性 0公开 1私密 2好友可见 */ + visibility_status: 0 | 1 | 2 + } + interface PostGetPostStar { status: boolean } From ba4023d0a743af7b2f57e07a3b7914346502bfcd Mon Sep 17 00:00:00 2001 From: alimy Date: Tue, 14 Jun 2022 21:02:28 +0800 Subject: [PATCH 06/11] optimize visible post logic add tags/search-index process logic --- internal/core/cache.go | 3 +++ internal/core/core.go | 2 +- internal/dao/cache_index.go | 7 +++++- internal/dao/post.go | 41 ++++++++++++++++++++++++++++++++++-- internal/dao/tag.go | 32 ++++++++++++++++++---------- internal/model/post.go | 18 +++++++++++++++- internal/routers/api/post.go | 24 +++++---------------- internal/routers/api/user.go | 9 ++++++++ internal/routers/router.go | 2 +- internal/service/post.go | 37 +++++++++++++++++++++++++++----- internal/service/user.go | 8 +++++++ pkg/errcode/module_code.go | 1 + 12 files changed, 143 insertions(+), 41 deletions(-) diff --git a/internal/core/cache.go b/internal/core/cache.go index 08eae18f..07afbdb4 100644 --- a/internal/core/cache.go +++ b/internal/core/cache.go @@ -6,6 +6,7 @@ const ( IdxActUpdatePost IdxActDeletePost IdxActStickPost + IdxActVisiblePost ) type IndexActionT uint8 @@ -22,6 +23,8 @@ func (a IndexActionT) String() string { return "delete post" case IdxActStickPost: return "stick post" + case IdxActVisiblePost: + return "visible post" default: return "unknow action" } diff --git a/internal/core/core.go b/internal/core/core.go index 65305200..adcdf537 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -34,7 +34,7 @@ type DataService interface { DeletePost(post *model.Post) error LockPost(post *model.Post) error StickPost(post *model.Post) error - VisibilityPost(post *model.Post) error + VisiblePost(post *model.Post, visibility model.PostVisibleT) error GetPostByID(id int64) (*model.Post, error) GetPosts(conditions *model.ConditionsT, offset, limit int) ([]*model.Post, error) MergePosts(posts []*model.Post) ([]*model.PostFormated, error) diff --git a/internal/dao/cache_index.go b/internal/dao/cache_index.go index 886cdb42..adbd9fb2 100644 --- a/internal/dao/cache_index.go +++ b/internal/dao/cache_index.go @@ -92,7 +92,12 @@ func (s *simpleCacheIndexServant) startIndexPosts() { } case action := <-s.indexActionCh: 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 if len(s.indexPosts) != 0 { logrus.Debugf("remove index posts by action %s", action) diff --git a/internal/dao/post.go b/internal/dao/post.go index 3d757746..de69205c 100644 --- a/internal/dao/post.go +++ b/internal/dao/post.go @@ -1,6 +1,7 @@ package dao import ( + "strings" "time" "github.com/rocboss/paopao-ce/internal/core" @@ -39,8 +40,44 @@ func (d *dataServant) StickPost(post *model.Post) error { return nil } -func (d *dataServant) VisibilityPost(post *model.Post) error { - return post.Update(d.engine) +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) { diff --git a/internal/dao/tag.go b/internal/dao/tag.go index 3f2765ae..d0935f7d 100644 --- a/internal/dao/tag.go +++ b/internal/dao/tag.go @@ -1,17 +1,32 @@ 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) { + 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(d.engine) + return tag.Create(db) } // 更新 t.QuoteNum++ - err = t.Update(d.engine) + err = t.Update(db) if err != nil { return nil, err @@ -20,16 +35,11 @@ func (d *dataServant) CreateTag(tag *model.Tag) (*model.Tag, error) { return t, nil } -func (d *dataServant) DeleteTag(tag *model.Tag) error { - tag, err := tag.Get(d.engine) +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(d.engine) -} - -func (d *dataServant) GetTags(conditions *model.ConditionsT, offset, limit int) ([]*model.Tag, error) { - return (&model.Tag{}).List(d.engine, conditions, offset, limit) + return tag.Update(db) } diff --git a/internal/model/post.go b/internal/model/post.go index 3f08f7a1..c8943464 100644 --- a/internal/model/post.go +++ b/internal/model/post.go @@ -8,12 +8,13 @@ import ( ) // PostVisibleT 可访问类型,0公开,1私密,2好友 -type PostVisibleT int +type PostVisibleT uint8 const ( PostVisitPublic PostVisibleT = iota PostVisitPrivate PostVisitFriend + PostVisitInvalid ) type Post struct { @@ -156,3 +157,18 @@ func (p *Post) Count(db *gorm.DB, conditions *ConditionsT) (int64, 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 } + +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" + } +} diff --git a/internal/routers/api/post.go b/internal/routers/api/post.go index d54cad02..f7d0fc1e 100644 --- a/internal/routers/api/post.go +++ b/internal/routers/api/post.go @@ -288,7 +288,7 @@ func StickPost(c *gin.Context) { }) } -func VisibilityPost(c *gin.Context) { +func VisiblePost(c *gin.Context) { param := service.PostVisibilityReq{} response := app.NewResponse(c) valid, errs := app.BindAndValid(c, ¶m) @@ -298,24 +298,10 @@ func VisibilityPost(c *gin.Context) { return } - user, _ := c.Get("USER") - - // 获取Post - postFormated, err := service.GetPost(param.ID) - if err != nil { - logrus.Errorf("service.GetPost err: %v\n", err) - response.ToErrorResponse(errcode.GetPostFailed) - return - } - - if postFormated.UserID != user.(*model.User).ID && !user.(*model.User).IsAdmin { - response.ToErrorResponse(errcode.NoPermission) - return - } - err = service.VisibilityPost(param.ID, param.Visibility) - if err != nil { - logrus.Errorf("service.LockPost err: %v\n", err) - response.ToErrorResponse(errcode.LockPostFailed) + 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 } diff --git a/internal/routers/api/user.go b/internal/routers/api/user.go index f7ca4633..9529a805 100644 --- a/internal/routers/api/user.go +++ b/internal/routers/api/user.go @@ -564,3 +564,12 @@ func GetUserWalletBills(c *gin.Context) { 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 +} diff --git a/internal/routers/router.go b/internal/routers/router.go index 309385e4..54ffb90d 100644 --- a/internal/routers/router.go +++ b/internal/routers/router.go @@ -168,7 +168,7 @@ func NewRouter() *gin.Engine { privApi.POST("/post/stick", api.StickPost) // 修改动态可见度 - privApi.POST("/post/visibility", api.VisibilityPost) + privApi.POST("/post/visibility", api.VisiblePost) // 发布动态评论 privApi.POST("/post/comment", api.CreatePostComment) diff --git a/internal/service/post.go b/internal/service/post.go index a2b4f9ce..5d89dceb 100644 --- a/internal/service/post.go +++ b/internal/service/post.go @@ -11,6 +11,7 @@ import ( "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/errcode" "github.com/rocboss/paopao-ce/pkg/util" "github.com/rocboss/paopao-ce/pkg/zinc" "github.com/sirupsen/logrus" @@ -219,16 +220,42 @@ func StickPost(id int64) error { return nil } -func VisibilityPost(id int64, visibility model.PostVisibleT) error { - post, _ := ds.GetPostByID(id) +func VisiblePost(user *model.User, postId int64, visibility model.PostVisibleT) *errcode.Error { + if visibility >= model.PostVisitInvalid { + return errcode.InvalidParams + } - // 修改可见度 - post.Visibility = visibility - err := ds.VisibilityPost(post) + 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 } diff --git a/internal/service/user.go b/internal/service/user.go index 5b4edfc4..934b8525 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -394,3 +394,11 @@ func GetSuggestTags(keyword string) ([]string, error) { 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 +} diff --git a/pkg/errcode/module_code.go b/pkg/errcode/module_code.go index 02976dc6..ea3b5e25 100644 --- a/pkg/errcode/module_code.go +++ b/pkg/errcode/module_code.go @@ -35,6 +35,7 @@ var ( InsuffientDownloadMoney = NewError(30009, "附件下载失败:账户资金不足") DownloadExecFail = NewError(30010, "附件下载失败:扣费失败") StickPostFailed = NewError(30011, "动态置顶失败") + VisblePostFailed = NewError(30012, "更新可见性失败") GetCommentsFailed = NewError(40001, "获取评论列表失败") CreateCommentFailed = NewError(40002, "评论发布失败") From 54da8cdf35c7a318bd92861b24039c57498ed25d Mon Sep 17 00:00:00 2001 From: orzi! <1063614727@qq.com> Date: Wed, 15 Jun 2022 17:20:39 +0800 Subject: [PATCH 07/11] chang type --- web/src/components/compose.vue | 2 +- web/src/components/post-detail.vue | 2 +- web/src/types/NetParams.d.ts | 8 ++++---- web/src/types/NetReq.d.ts | 4 ++-- web/src/types/item.d.ts | 7 +++++-- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/web/src/components/compose.vue b/web/src/components/compose.vue index 74811c15..0ab60c9f 100644 --- a/web/src/components/compose.vue +++ b/web/src/components/compose.vue @@ -297,7 +297,7 @@ const fileQueue = ref([]); const imageContents = ref([]); const videoContents = ref([]); const attachmentContents = ref([]); -const visitType = ref<0 | 1 | 2>(0) +const visitType = ref(0) const visibilities = [{value: 0, label: "公开"}, {value: 1, label: "私密"}, {value: 2, label: "好友可见"}] const uploadGateway = import.meta.env.VITE_HOST + '/v1/attachment'; diff --git a/web/src/components/post-detail.vue b/web/src/components/post-detail.vue index 69e8216b..c8879d45 100644 --- a/web/src/components/post-detail.vue +++ b/web/src/components/post-detail.vue @@ -235,7 +235,7 @@ const showLockModal = ref(false); const showStickModal = ref(false); const showVisibilityModal = ref(false); const loading = ref(false); -const tempVisibility = ref<0 | 1 | 2>(0); +const tempVisibility = ref(0); const emit = defineEmits<{ (e: 'reload'): void; diff --git a/web/src/types/NetParams.d.ts b/web/src/types/NetParams.d.ts index b6371564..fb507498 100644 --- a/web/src/types/NetParams.d.ts +++ b/web/src/types/NetParams.d.ts @@ -126,8 +126,8 @@ declare module NetParams { interface PostVisibilityPost { id: number, - /** 可见性 0公开 1私密 2好友可见 */ - visibility: 0 | 1 | 2 + /** 可见性:0为公开,1为私密,2为好友可见 */ + visibility: Item.VisibilityStatus } interface PostGetPostStar { @@ -164,8 +164,8 @@ declare module NetParams { users: string[], /** 附件价格 */ attachment_price: number, - /** 可见性 0公开 1私密 2好友可见 */ - visibility: 0 | 1 | 2 + /** 可见性:0为公开,1为私密,2为好友可见 */ + visibility: Item.VisibilityStatus } interface PostDeletePost { diff --git a/web/src/types/NetReq.d.ts b/web/src/types/NetReq.d.ts index ffc6c55d..bc5b4fcf 100644 --- a/web/src/types/NetReq.d.ts +++ b/web/src/types/NetReq.d.ts @@ -117,8 +117,8 @@ declare module NetReq { } interface PostVisibilityPost { - /** 可见性 0公开 1私密 2好友可见 */ - visibility_status: 0 | 1 | 2 + /** 可见性:0为公开,1为私密,2为好友可见 */ + visibility_status: Item.VisibilityStatus } interface PostGetPostStar { diff --git a/web/src/types/item.d.ts b/web/src/types/item.d.ts index bc369592..5a4a18e8 100644 --- a/web/src/types/item.d.ts +++ b/web/src/types/item.d.ts @@ -1,5 +1,8 @@ declare module Item { + /** 可见性:0为公开,1为私密,2为好友可见 */ + type VisibilityStatus = 0 | 1 | 2; + interface UserInfo { /** 用户UID */ id: number, @@ -161,8 +164,8 @@ declare module Item { contents: PostItemProps[], /** 标签列表 */ tags: { [key: string]: number } | string, - /** 可见性 0公开 1私密 2好友可见 */ - visibility: 0 | 1 | 2, + /** 可见性:0为公开,1为私密,2为好友可见 */ + visibility: VisibilityStatus, /** 是否锁定 */ is_lock: number, /** 是否置顶 */ From d33d7b4c5711e27429ff84cfc19782917a204475 Mon Sep 17 00:00:00 2001 From: orzi! <1063614727@qq.com> Date: Wed, 15 Jun 2022 19:34:42 +0800 Subject: [PATCH 08/11] type -> enum --- web/src/components/compose.vue | 21 +++++---- web/src/components/post-detail.vue | 11 ++--- web/src/main.ts | 2 +- web/src/types/NetParams.d.ts | 4 +- web/src/types/NetReq.d.ts | 2 +- web/src/types/item.d.ts | 13 +++--- web/src/utils/IEnum.ts | 69 ++++++++++++++++++++++++++++++ web/src/views/Setting.vue | 2 +- 8 files changed, 98 insertions(+), 26 deletions(-) create mode 100644 web/src/utils/IEnum.ts diff --git a/web/src/components/compose.vue b/web/src/components/compose.vue index 0ab60c9f..ca4409b6 100644 --- a/web/src/components/compose.vue +++ b/web/src/components/compose.vue @@ -273,6 +273,7 @@ import { import { createPost } from '@/api/post'; import { parsePostTag } from '@/utils/content'; import type { MentionOption, UploadFileInfo, UploadInst } from 'naive-ui'; +import { VisibilityEnum, PostItemTypeEnum } from '@/utils/IEnum'; @@ -297,8 +298,12 @@ const fileQueue = ref([]); const imageContents = ref([]); const videoContents = ref([]); const attachmentContents = ref([]); -const visitType = ref(0) -const visibilities = [{value: 0, label: "公开"}, {value: 1, label: "私密"}, {value: 2, label: "好友可见"}] +const visitType = ref(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 uploadToken = ref(); @@ -505,7 +510,7 @@ const submitPost = () => { contents.push({ content: content.value, - type: 2, // 文字 + type: PostItemTypeEnum.TEXT, // 文字 sort, }); @@ -513,7 +518,7 @@ const submitPost = () => { sort++; contents.push({ content: img.content, - type: 3, // 图片 + type: PostItemTypeEnum.IMAGEURL, // 图片 sort, }); }); @@ -521,7 +526,7 @@ const submitPost = () => { sort++; contents.push({ content: video.content, - type: 4, // 图片 + type: PostItemTypeEnum.VIDEOURL, // 视频 sort, }); }); @@ -529,7 +534,7 @@ const submitPost = () => { sort++; contents.push({ content: attachment.content, - type: 7, // 附件 + type: PostItemTypeEnum.ATTACHMENT, // 附件 sort, }); }); @@ -538,7 +543,7 @@ const submitPost = () => { sort++; contents.push({ content: link, - type: 6, // 链接 + type: PostItemTypeEnum.LINKURL, // 链接 sort, }); }); @@ -567,7 +572,7 @@ const submitPost = () => { imageContents.value = []; videoContents.value = []; attachmentContents.value = []; - visitType.value = 0; + visitType.value = VisibilityEnum.PUBLIC; }) .catch((err) => { submitting.value = false; diff --git a/web/src/components/post-detail.vue b/web/src/components/post-detail.vue index c8879d45..65a7cbec 100644 --- a/web/src/components/post-detail.vue +++ b/web/src/components/post-detail.vue @@ -26,7 +26,7 @@ 置顶 (0); +const tempVisibility = ref(VisibilityEnum.PUBLIC); const emit = defineEmits<{ (e: 'reload'): void; @@ -314,7 +315,7 @@ const adminOptions = computed(() => { }); } } - if (post.value.visibility === 0) { + if (post.value.visibility === VisibilityEnum.PUBLIC) { options.push({ label: '公开', key: 'vpublic', @@ -323,7 +324,7 @@ const adminOptions = computed(() => { , { label: '好友可见', key: 'vfriend' } ] }) - } else if (post.value.visibility === 1) { + } else if (post.value.visibility === VisibilityEnum.PRIVATE) { options.push({ label: '私密', key: 'vprivate', diff --git a/web/src/main.ts b/web/src/main.ts index 1040e04c..9385ea3a 100644 --- a/web/src/main.ts +++ b/web/src/main.ts @@ -17,4 +17,4 @@ declare global { $message: MessageApiInjection, $store: any } -} \ No newline at end of file +} diff --git a/web/src/types/NetParams.d.ts b/web/src/types/NetParams.d.ts index fb507498..854787d1 100644 --- a/web/src/types/NetParams.d.ts +++ b/web/src/types/NetParams.d.ts @@ -127,7 +127,7 @@ declare module NetParams { interface PostVisibilityPost { id: number, /** 可见性:0为公开,1为私密,2为好友可见 */ - visibility: Item.VisibilityStatus + visibility: import('@/utils/IEnum').VisibilityEnum } interface PostGetPostStar { @@ -165,7 +165,7 @@ declare module NetParams { /** 附件价格 */ attachment_price: number, /** 可见性:0为公开,1为私密,2为好友可见 */ - visibility: Item.VisibilityStatus + visibility: import('@/utils/IEnum').VisibilityEnum } interface PostDeletePost { diff --git a/web/src/types/NetReq.d.ts b/web/src/types/NetReq.d.ts index bc5b4fcf..59c3bec2 100644 --- a/web/src/types/NetReq.d.ts +++ b/web/src/types/NetReq.d.ts @@ -118,7 +118,7 @@ declare module NetReq { interface PostVisibilityPost { /** 可见性:0为公开,1为私密,2为好友可见 */ - visibility_status: Item.VisibilityStatus + visibility_status: import('@/utils/IEnum').VisibilityEnum } interface PostGetPostStar { diff --git a/web/src/types/item.d.ts b/web/src/types/item.d.ts index 5a4a18e8..818557a9 100644 --- a/web/src/types/item.d.ts +++ b/web/src/types/item.d.ts @@ -1,8 +1,5 @@ declare module Item { - /** 可见性:0为公开,1为私密,2为好友可见 */ - type VisibilityStatus = 0 | 1 | 2; - interface UserInfo { /** 用户UID */ id: number, @@ -31,7 +28,7 @@ declare module Item { /** 评论者UID */ user_id: number, /** 类别:1为标题,2为文字段落,3为图片地址,4为视频地址,5为语音地址,6为链接地址 */ - type: number, + type: import('@/utils/IEnum').CommentItemTypeEnum, /** 内容 */ content: string, /** 排序,越小越靠前 */ @@ -114,7 +111,7 @@ declare module Item { /** 内容ID */ id: number, /** 类型:1为标题,2为文字段落,3为图片地址,4为视频地址,5为语音地址,6为链接地址,7为附件资源,8为收费资源 */ - type: number, + type: import('@/utils/IEnum').PostItemTypeEnum, /** POST ID */ post_id: number, /** 内容 */ @@ -165,7 +162,7 @@ declare module Item { /** 标签列表 */ tags: { [key: string]: number } | string, /** 可见性:0为公开,1为私密,2为好友可见 */ - visibility: VisibilityStatus, + visibility: import('@/utils/IEnum').VisibilityEnum, /** 是否锁定 */ is_lock: number, /** 是否置顶 */ @@ -195,7 +192,7 @@ declare module Item { interface MessageProps { id: number, /** 类型:1为动态,2为评论,3为回复,4为私信,99为系统通知 */ - type: 1 | 2 | 3 | 4 | 99, + type: import('@/utils/IEnum').MessageTypeEnum, /** 摘要说明 */ brief: string, /** 详细内容 */ @@ -233,7 +230,7 @@ declare module Item { interface AttachmentProps { id: number, /** 类别:1为图片,2为视频,3为其他附件 */ - type: 1 | 2 | 3, + type: import('@/utils/IEnum').AttachmentTypeEnum, /** 发布者用户UID */ user_id: number, /** 发布者用户数据 */ diff --git a/web/src/utils/IEnum.ts b/web/src/utils/IEnum.ts new file mode 100644 index 00000000..0566867a --- /dev/null +++ b/web/src/utils/IEnum.ts @@ -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 +} diff --git a/web/src/views/Setting.vue b/web/src/views/Setting.vue index 0567e6cb..6c5be9ae 100644 --- a/web/src/views/Setting.vue +++ b/web/src/views/Setting.vue @@ -102,7 +102,7 @@ From 50048d698619b9538b8839ba7ad9d7c73f172e62 Mon Sep 17 00:00:00 2001 From: orzi! <1063614727@qq.com> Date: Wed, 15 Jun 2022 19:40:04 +0800 Subject: [PATCH 09/11] fixed:type error --- web/src/components/post-detail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/post-detail.vue b/web/src/components/post-detail.vue index 65a7cbec..586255a1 100644 --- a/web/src/components/post-detail.vue +++ b/web/src/components/post-detail.vue @@ -26,7 +26,7 @@ 置顶 Date: Thu, 16 Jun 2022 11:32:16 +0800 Subject: [PATCH 10/11] optimize #97 user stars/collections update when post visibility changed --- config.yaml.sample | 6 ++--- internal/conf/db.go | 8 +++--- internal/conf/settting.go | 17 ++++++++++++- internal/dao/post.go | 4 +-- internal/model/post_collection.go | 33 ++++++++++++++++--------- internal/model/post_star.go | 41 ++++++++++++++++++------------- internal/routers/api/post.go | 18 +++++++++++--- internal/service/post.go | 25 +++++++++++++++++++ internal/service/user.go | 33 ++++++------------------- 9 files changed, 117 insertions(+), 68 deletions(-) diff --git a/config.yaml.sample b/config.yaml.sample index 93506fb4..524cf9df 100644 --- a/config.yaml.sample +++ b/config.yaml.sample @@ -77,8 +77,8 @@ LocalOSS: # 本地文件OSS存储配置 Bucket: paopao Domain: 127.0.0.1:8008 Database: # Database通用配置 - LogLevel: 2 - TablePrefix: p_ + LogLevel: error # 日志级别 silent|error|warn|info + TablePrefix: p_ # 表名前缀 MySQL: # MySQL数据库 Username: paopao Password: paopao @@ -88,7 +88,7 @@ MySQL: # MySQL数据库 ParseTime: True MaxIdleConns: 10 MaxOpenConns: 30 -Postgres: +Postgres: # PostgreSQL数据库 User: paopao Password: paopao DBName: paopao diff --git a/internal/conf/db.go b/internal/conf/db.go index b517b1c0..649a0627 100644 --- a/internal/conf/db.go +++ b/internal/conf/db.go @@ -23,10 +23,10 @@ func newDBEngine() (*gorm.DB, error) { newLogger := logger.New( logrus.StandardLogger(), // io writer(日志输出的目标,前缀和日志包含的内容) logger.Config{ - SlowThreshold: time.Second, // 慢 SQL 阈值 - LogLevel: databaseSetting.LogLevel, // 日志级别 - IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误 - Colorful: false, // 禁用彩色打印 + SlowThreshold: time.Second, // 慢 SQL 阈值 + LogLevel: databaseSetting.logLevel(), // 日志级别 + IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误 + Colorful: false, // 禁用彩色打印 }, ) diff --git a/internal/conf/settting.go b/internal/conf/settting.go index 7ac6548e..33cb9131 100644 --- a/internal/conf/settting.go +++ b/internal/conf/settting.go @@ -80,7 +80,7 @@ type ZincSettingS struct { type DatabaseSetingS struct { TablePrefix string - LogLevel logger.LogLevel + LogLevel string } type MySQLSettingS struct { @@ -282,3 +282,18 @@ func (s PostgresSettingS) Dsn() string { } 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 + } +} diff --git a/internal/dao/post.go b/internal/dao/post.go index de69205c..328de819 100644 --- a/internal/dao/post.go +++ b/internal/dao/post.go @@ -120,7 +120,7 @@ func (d *dataServant) GetUserPostStars(userID int64, offset, limit int) ([]*mode } return star.List(d.engine, &model.ConditionsT{ - "ORDER": "id DESC", + "ORDER": d.engine.NamingStrategy.TableName("PostStar") + ".id DESC", }, offset, limit) } @@ -159,7 +159,7 @@ func (d *dataServant) GetUserPostCollections(userID int64, offset, limit int) ([ } return collection.List(d.engine, &model.ConditionsT{ - "ORDER": "id DESC", + "ORDER": d.engine.NamingStrategy.TableName("PostCollection") + ".id DESC", }, offset, limit) } diff --git a/internal/model/post_collection.go b/internal/model/post_collection.go index 2c7196ea..aec5d270 100644 --- a/internal/model/post_collection.go +++ b/internal/model/post_collection.go @@ -8,22 +8,26 @@ import ( type PostCollection struct { *Model + Post *Post `json:"-"` PostID int64 `json:"post_id"` UserID int64 `json:"user_id"` } func (p *PostCollection) Get(db *gorm.DB) (*PostCollection, error) { var star PostCollection + tn := db.NamingStrategy.TableName("PostCollection") + "." + 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 { - db = db.Where("post_id = ?", p.PostID) + db = db.Where(tn+"post_id = ?", p.PostID) } 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 if err != nil { 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) { - err := db.Create(&p).Error + err := db.Omit("Post").Create(&p).Error return p, err } 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(), "is_del": 1, }).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) { var collections []*PostCollection var err error + tn := db.NamingStrategy.TableName("PostCollection") + "." + if offset >= 0 && limit > 0 { db = db.Offset(offset).Limit(limit) } if p.UserID > 0 { - db = db.Where("user_id = ?", p.UserID) + db = db.Where(tn+"user_id = ?", p.UserID) } for k, v := range *conditions { if k == "ORDER" { db = db.Order(v) } 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 } @@ -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) { var count int64 + tn := db.NamingStrategy.TableName("PostCollection") + "." + if p.PostID > 0 { - db = db.Where("post_id = ?", p.PostID) + db = db.Where(tn+"post_id = ?", p.PostID) } if p.UserID > 0 { - db = db.Where("user_id = ?", p.UserID) + db = db.Where(tn+"user_id = ?", p.UserID) } for k, v := range *conditions { 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 { return 0, err } diff --git a/internal/model/post_star.go b/internal/model/post_star.go index 3f672664..d6c712ce 100644 --- a/internal/model/post_star.go +++ b/internal/model/post_star.go @@ -8,38 +8,40 @@ import ( type PostStar struct { *Model + Post *Post `json:"-"` PostID int64 `json:"post_id"` UserID int64 `json:"user_id"` } func (p *PostStar) Get(db *gorm.DB) (*PostStar, error) { var star PostStar + tn := db.NamingStrategy.TableName("PostStar") + "." + 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 { - db = db.Where("post_id = ?", p.PostID) + db = db.Where(tn+"post_id = ?", p.PostID) } if p.UserID > 0 { - db = db.Where("user_id = ?", p.UserID) + db = db.Where(tn+"user_id = ?", p.UserID) } - err := db.First(&star).Error - if err != nil { - return &star, err + db = db.Joins("Post").Where("Post.visibility <> ?", PostVisitPrivate).Order("Post.id DESC") + if err := db.First(&star).Error; err != nil { + return nil, err } - return &star, nil } func (p *PostStar) Create(db *gorm.DB) (*PostStar, error) { - err := db.Create(&p).Error + err := db.Omit("Post").Create(&p).Error return p, err } 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(), "is_del": 1, }).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) { var stars []*PostStar var err error + tn := db.NamingStrategy.TableName("PostStar") + "." + if offset >= 0 && limit > 0 { db = db.Offset(offset).Limit(limit) } if p.UserID > 0 { - db = db.Where("user_id = ?", p.UserID) + db = db.Where(tn+"user_id = ?", p.UserID) } for k, v := range *conditions { if k == "ORDER" { db = db.Order(v) } 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 stars, nil } func (p *PostStar) Count(db *gorm.DB, conditions *ConditionsT) (int64, error) { var count int64 + tn := db.NamingStrategy.TableName("PostStar") + "." + if p.PostID > 0 { - db = db.Where("post_id = ?", p.PostID) + db = db.Where(tn+"post_id = ?", p.PostID) } if p.UserID > 0 { - db = db.Where("user_id = ?", p.UserID) + db = db.Where(tn+"user_id = ?", p.UserID) } for k, v := range *conditions { 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 { return 0, err } - return count, nil } diff --git a/internal/routers/api/post.go b/internal/routers/api/post.go index f7d0fc1e..af352afb 100644 --- a/internal/routers/api/post.go +++ b/internal/routers/api/post.go @@ -156,11 +156,16 @@ func PostStar(c *gin.Context) { star, err := service.GetPostStar(param.ID, userID.(int64)) if err != nil { // 创建Star - service.CreatePostStar(param.ID, userID.(int64)) + _, err = service.CreatePostStar(param.ID, userID.(int64)) status = true } else { // 取消Star - service.DeletePostStar(star) + err = service.DeletePostStar(star) + } + + if err != nil { + response.ToErrorResponse(errcode.NoPermission) + return } response.ToResponse(gin.H{ @@ -204,11 +209,16 @@ func PostCollection(c *gin.Context) { collection, err := service.GetPostCollection(param.ID, userID.(int64)) if err != nil { // 创建collection - service.CreatePostCollection(param.ID, userID.(int64)) + _, err = service.CreatePostCollection(param.ID, userID.(int64)) status = true } else { // 取消Star - service.DeletePostCollection(collection) + err = service.DeletePostCollection(collection) + } + + if err != nil { + response.ToErrorResponse(errcode.NoPermission) + return } response.ToResponse(gin.H{ diff --git a/internal/service/post.go b/internal/service/post.go index 5d89dceb..3ab372a2 100644 --- a/internal/service/post.go +++ b/internal/service/post.go @@ -2,6 +2,7 @@ package service import ( "encoding/json" + "errors" "fmt" "math" "strings" @@ -269,6 +270,12 @@ func CreatePostStar(postID, userID int64) (*model.PostStar, error) { if err != nil { return nil, err } + + // 私密post不可操作 + if post.Visibility == model.PostVisitPrivate { + return nil, errors.New("no permision") + } + star, err := ds.CreatePostStar(postID, userID) if err != nil { return nil, err @@ -294,6 +301,12 @@ func DeletePostStar(star *model.PostStar) error { if err != nil { return err } + + // 私密post不可操作 + if post.Visibility == model.PostVisitPrivate { + return errors.New("no permision") + } + // 更新Post点赞数 post.UpvoteCount-- ds.UpdatePost(post) @@ -314,6 +327,12 @@ func CreatePostCollection(postID, userID int64) (*model.PostCollection, error) { if err != nil { return nil, err } + + // 私密post不可操作 + if post.Visibility == model.PostVisitPrivate { + return nil, errors.New("no permision") + } + collection, err := ds.CreatePostCollection(postID, userID) if err != nil { return nil, err @@ -339,6 +358,12 @@ func DeletePostCollection(collection *model.PostCollection) error { if err != nil { return err } + + // 私密post不可操作 + if post.Visibility == model.PostVisitPrivate { + return errors.New("no permision") + } + // 更新Post点赞数 post.CollectionCount-- ds.UpdatePost(post) diff --git a/internal/service/user.go b/internal/service/user.go index 934b8525..c4d0234e 100644 --- a/internal/service/user.go +++ b/internal/service/user.go @@ -274,21 +274,11 @@ func GetUserCollections(userID int64, offset, limit int) ([]*model.PostFormated, if err != nil { return nil, 0, err } - postIDs := []int64{} + var posts []*model.Post for _, collection := range collections { - postIDs = append(postIDs, collection.PostID) + posts = append(posts, collection.Post) } - - // 获取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) + postsFormated, err := ds.MergePosts(posts) if err != nil { return nil, 0, err } @@ -306,21 +296,12 @@ func GetUserStars(userID int64, offset, limit int) ([]*model.PostFormated, int64 if err != nil { return nil, 0, err } - postIDs := []int64{} - for _, star := range stars { - postIDs = append(postIDs, star.PostID) - } - // 获取Posts - posts, err := ds.GetPosts(&model.ConditionsT{ - "id IN ?": postIDs, - "ORDER": "id DESC", - }, 0, 0) - if err != nil { - return nil, 0, err + var posts []*model.Post + for _, star := range stars { + posts = append(posts, star.Post) } - - postsFormated, err := FormatPosts(posts) + postsFormated, err := ds.MergePosts(posts) if err != nil { return nil, 0, err } From cbb4a3d5a5e5b0b7303d039fc22dad71c1b951cf Mon Sep 17 00:00:00 2001 From: orzi! <36832622+orziz@users.noreply.github.com> Date: Thu, 16 Jun 2022 12:13:21 +0800 Subject: [PATCH 11/11] Rename item.d.ts to Item.d.ts --- web/src/types/{item.d.ts => Item.d.ts} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename web/src/types/{item.d.ts => Item.d.ts} (99%) diff --git a/web/src/types/item.d.ts b/web/src/types/Item.d.ts similarity index 99% rename from web/src/types/item.d.ts rename to web/src/types/Item.d.ts index 818557a9..a4c19418 100644 --- a/web/src/types/item.d.ts +++ b/web/src/types/Item.d.ts @@ -289,4 +289,4 @@ declare module Item { created_on: number } -} \ No newline at end of file +}