From 956adfb8ebd102e28def0be9f1559462d87d7b84 Mon Sep 17 00:00:00 2001 From: sunface Date: Fri, 5 Mar 2021 10:56:36 +0800 Subject: [PATCH] abstract tags module --- pages/[username]/[post_id].tsx | 12 ++-- pages/[username]/index.tsx | 8 +-- pages/bookmarks.tsx | 2 +- pages/editor/posts.tsx | 2 +- pages/index.tsx | 6 +- pages/tags/[name].tsx | 2 +- server/internal/api/posts.go | 3 +- server/internal/api/{tags.go => tag.go} | 0 server/internal/api/{users.go => user.go} | 0 server/internal/storage/sql_tables.go | 49 +++++++++------ server/internal/story/post.go | 32 +++------- server/internal/story/posts.go | 17 ++--- server/internal/tags/tags.go | 63 +++++++++++++++---- server/internal/user/users.go | 35 +++-------- src/components/comments/comment.tsx | 2 +- src/components/comments/reply.tsx | 2 +- src/components/{posts => story}/bookmark.tsx | 0 src/components/{posts => story}/like.tsx | 0 .../{posts => story}/no-more-posts.tsx | 0 .../{posts => story}/post-author.tsx | 0 src/components/{posts => story}/post-card.tsx | 0 .../{posts => story}/post-sidebar.tsx | 0 src/components/{posts => story}/posts.tsx | 0 .../{posts => story}/simple-post-card.tsx | 0 .../{posts => story}/tag-list-card.tsx | 0 .../{posts => story}/tag-text-card.tsx | 0 .../{posts => story}/text-post-card.tsx | 0 .../{posts => story}/unicorn-like.tsx | 0 src/components/tags/tag-input.tsx | 2 +- 29 files changed, 123 insertions(+), 114 deletions(-) rename server/internal/api/{tags.go => tag.go} (100%) rename server/internal/api/{users.go => user.go} (100%) rename src/components/{posts => story}/bookmark.tsx (100%) rename src/components/{posts => story}/like.tsx (100%) rename src/components/{posts => story}/no-more-posts.tsx (100%) rename src/components/{posts => story}/post-author.tsx (100%) rename src/components/{posts => story}/post-card.tsx (100%) rename src/components/{posts => story}/post-sidebar.tsx (100%) rename src/components/{posts => story}/posts.tsx (100%) rename src/components/{posts => story}/simple-post-card.tsx (100%) rename src/components/{posts => story}/tag-list-card.tsx (100%) rename src/components/{posts => story}/tag-text-card.tsx (100%) rename src/components/{posts => story}/text-post-card.tsx (100%) rename src/components/{posts => story}/unicorn-like.tsx (100%) diff --git a/pages/[username]/[post_id].tsx b/pages/[username]/[post_id].tsx index 18d050bf..e6a49634 100644 --- a/pages/[username]/[post_id].tsx +++ b/pages/[username]/[post_id].tsx @@ -1,10 +1,10 @@ import { Box, chakra, Divider, Flex, Heading, HStack, IconButton, Image, storageKey, VStack } from "@chakra-ui/react" import Comments from "components/comments/comments" import Container from "components/container" -import LikeButton from "components/posts/unicorn-like" +import LikeButton from "components/story/unicorn-like" import { MarkdownRender } from "components/markdown-editor/render" -import PostAuthor from "components/posts/post-author" -import TagTextCard from "components/posts/tag-text-card" +import PostAuthor from "components/story/post-author" +import TagTextCard from "components/story/tag-text-card" import SEO from "components/seo" import siteConfig from "configs/site-config" import useSession from "hooks/use-session" @@ -21,10 +21,10 @@ import { Comment } from "src/types/comments" import { Post } from "src/types/posts" import { Tag } from "src/types/tag" import { requestApi } from "utils/axios/request" -import UnicornLike from "components/posts/unicorn-like" +import UnicornLike from "components/story/unicorn-like" import SvgButton from "components/svg-button" -import Bookmark from "components/posts/bookmark" -import PostSidebar from "components/posts/post-sidebar" +import Bookmark from "components/story/bookmark" +import PostSidebar from "components/story/post-sidebar" const PostPage = () => { const router = useRouter() diff --git a/pages/[username]/index.tsx b/pages/[username]/index.tsx index 6a78899e..7d3f1ba4 100644 --- a/pages/[username]/index.tsx +++ b/pages/[username]/index.tsx @@ -16,9 +16,9 @@ import { User } from "src/types/session" import { requestApi } from "utils/axios/request" import moment from 'moment' import { Post } from "src/types/posts" -import PostCard from "components/posts/post-card" +import PostCard from "components/story/post-card" import userCustomTheme from "theme/user-custom" -import Posts from "components/posts/posts" +import Posts from "components/story/posts" import Link from "next/link" import Empty from "components/empty" import Count from "components/count" @@ -111,12 +111,12 @@ const UserPage = () => { { user.rawSkills.map(skill => - + {skill.title} - ) + ) } } diff --git a/pages/bookmarks.tsx b/pages/bookmarks.tsx index 7c3f447f..1d3eeb30 100644 --- a/pages/bookmarks.tsx +++ b/pages/bookmarks.tsx @@ -21,7 +21,7 @@ import { import { requestApi } from "utils/axios/request" import TagCard from 'src/components/tags/tag-card' import { Post } from "src/types/posts" -import Posts from "components/posts/posts" +import Posts from "components/story/posts" import { find } from "lodash" import userCustomTheme from "theme/user-custom" import Empty from "components/empty" diff --git a/pages/editor/posts.tsx b/pages/editor/posts.tsx index 1ec9831e..38de0749 100644 --- a/pages/editor/posts.tsx +++ b/pages/editor/posts.tsx @@ -9,7 +9,7 @@ import { requestApi } from "utils/axios/request" import { useDisclosure } from "@chakra-ui/react" import { Field, Form, Formik } from "formik" import { config } from "configs/config" -import TextPostCard from "components/posts/text-post-card" +import TextPostCard from "components/story/text-post-card" import { Post } from "src/types/posts" import { FaExternalLinkAlt, FaRegEdit } from "react-icons/fa" import { useRouter } from "next/router" diff --git a/pages/index.tsx b/pages/index.tsx index 73c9a420..c193dc10 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -9,9 +9,9 @@ import { Divider } from "@chakra-ui/react" import Card from "components/card" -import PostCard from "components/posts/post-card" -import Posts from "components/posts/posts" -import SimplePostCard from "components/posts/simple-post-card" +import PostCard from "components/story/post-card" +import Posts from "components/story/posts" +import SimplePostCard from "components/story/simple-post-card" import SEO from "components/seo" import { getSvgIcon } from "components/svg-icon" import siteConfig from "configs/site-config" diff --git a/pages/tags/[name].tsx b/pages/tags/[name].tsx index c68d8f03..8ceed261 100644 --- a/pages/tags/[name].tsx +++ b/pages/tags/[name].tsx @@ -3,7 +3,7 @@ import Card from "components/card" import Container from "components/container" import Empty from "components/empty" import { MarkdownRender } from "components/markdown-editor/render" -import Posts from "components/posts/posts" +import Posts from "components/story/posts" import SEO from "components/seo" import siteConfig from "configs/site-config" import useSession from "hooks/use-session" diff --git a/server/internal/api/posts.go b/server/internal/api/posts.go index f8e12149..90386fcb 100644 --- a/server/internal/api/posts.go +++ b/server/internal/api/posts.go @@ -2,7 +2,6 @@ package api import ( "net/http" - "strconv" "github.com/gin-gonic/gin" "github.com/imdotdev/im.dev/server/internal/story" @@ -36,7 +35,7 @@ func GetUserPosts(c *gin.Context) { } func GetTagPosts(c *gin.Context) { - tagID, _ := strconv.ParseInt(c.Param("id"), 10, 64) + tagID := c.Param("id") user := user.CurrentUser(c) posts, err := story.TagPosts(user, tagID) if err != nil { diff --git a/server/internal/api/tags.go b/server/internal/api/tag.go similarity index 100% rename from server/internal/api/tags.go rename to server/internal/api/tag.go diff --git a/server/internal/api/users.go b/server/internal/api/user.go similarity index 100% rename from server/internal/api/users.go rename to server/internal/api/user.go diff --git a/server/internal/storage/sql_tables.go b/server/internal/storage/sql_tables.go index 1b669d90..aab10fb2 100644 --- a/server/internal/storage/sql_tables.go +++ b/server/internal/storage/sql_tables.go @@ -42,16 +42,6 @@ var sqlTables = map[string]string{ updated DATETIME );`, - "user_skills": `CREATE TABLE IF NOT EXISTS user_skills ( - user_id VARCHAR(255), - skill_id INTEGER - ); - CREATE INDEX IF NOT EXISTS user_skills_userid - ON user_skills (user_id); - CREATE INDEX IF NOT EXISTS user_skills_skillid - ON user_skills (skill_id); - `, - "sessions": `CREATE TABLE IF NOT EXISTS sessions ( sid VARCHAR(255) primary key, user_id VARCHAR(255) @@ -78,13 +68,11 @@ var sqlTables = map[string]string{ ON posts (creator); CREATE INDEX IF NOT EXISTS posts_created ON posts (created); - CREATE UNIQUE INDEX IF NOT EXISTS posts_creator_slug - ON posts (creator, slug); `, "likes": `CREATE TABLE IF NOT EXISTS likes ( - user_id VARCHAR(255), story_id VARCHAR(255), + user_id VARCHAR(255), created DATETIME NOT NULL ); CREATE INDEX IF NOT EXISTS likes_userid @@ -93,6 +81,29 @@ var sqlTables = map[string]string{ ON likes (story_id); `, + "likes_count": `CREATE TABLE IF NOT EXISTS likes_count ( + story_id VARCHAR(255) PRIMARY KEY, + count INTEGER + ); + `, + + "follows": `CREATE TABLE IF NOT EXISTS follows ( + user_id VARCHAR(255), + target_id VARCHAR(255), + created DATETIME NOT NULL + ); + CREATE INDEX IF NOT EXISTS follows_userid + ON follows (user_id); + CREATE INDEX IF NOT EXISTS follows_targetid + ON follows (target_id); + `, + + "follows_count": `CREATE TABLE IF NOT EXISTS follows_count ( + target_id VARCHAR(255) PRIMARY KEY, + count INTEGER + ); + `, + "tags": `CREATE TABLE IF NOT EXISTS tags ( id VARCHAR(255) PRIMARY KEY, creator VARCHAR(255) NOT NULL, @@ -111,14 +122,14 @@ var sqlTables = map[string]string{ ON tags (created); `, - "tag_post": `CREATE TABLE IF NOT EXISTS tag_post ( + "tags_using": `CREATE TABLE IF NOT EXISTS tags_using ( tag_id VARCHAR(255), - post_id VARCHAR(255) + target_id VARCHAR(255) ); - CREATE INDEX IF NOT EXISTS tag_post_tagid - ON tag_post (tag_id); - CREATE INDEX IF NOT EXISTS tag_post_postid - ON tag_post (post_id); + CREATE INDEX IF NOT EXISTS tags_using_tagid + ON tags_using (tag_id); + CREATE INDEX IF NOT EXISTS tags_using_targetid + ON tags_using (target_id); `, "comments": `CREATE TABLE IF NOT EXISTS comments ( diff --git a/server/internal/story/post.go b/server/internal/story/post.go index 02dd745f..fda15908 100644 --- a/server/internal/story/post.go +++ b/server/internal/story/post.go @@ -94,16 +94,10 @@ func SubmitPost(c *gin.Context) (map[string]string, *e.Error) { } //update tags - _, err = db.Conn.Exec("DELETE FROM tag_post WHERE post_id=?", post.ID) + err = tags.UpdateTargetTags(post.ID, post.Tags) if err != nil { - logger.Warn("delete post tags error", "error", err) - } - - for _, tag := range post.Tags { - _, err = db.Conn.Exec("INSERT INTO tag_post (tag_id,post_id) VALUES (?,?)", tag, post.ID) - if err != nil { - logger.Warn("add post tag error", "error", err) - } + logger.Warn("upate tags error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) } return map[string]string{ @@ -120,7 +114,7 @@ func DeletePost(id string) *e.Error { } // delete tags - _, err = db.Conn.Exec("DELETE FROM tag_post WHERE post_id=?", id) + err = tags.DeleteTargetTags(id) if err != nil { logger.Warn("delete post tags error", "error", err) } @@ -148,24 +142,12 @@ func GetPost(id string, slug string) (*models.Post, *e.Error) { err = ar.Creator.Query() // get tags - t := make([]string, 0) - rows, err := db.Conn.Query("SELECT tag_id FROM tag_post WHERE post_id=?", ar.ID) - if err != nil && err != sql.ErrNoRows { + t, rawTags, err := tags.GetTargetTags(ar.ID) + if err != nil { return nil, e.New(http.StatusInternalServerError, e.Internal) } - - ar.RawTags = make([]*models.Tag, 0) - for rows.Next() { - var tag string - err = rows.Scan(&tag) - t = append(t, tag) - - rawTag, err := tags.GetTag(tag, "") - if err == nil { - ar.RawTags = append(ar.RawTags, rawTag) - } - } ar.Tags = t + ar.RawTags = rawTags // add views count _, err = db.Conn.Exec("UPDATE posts SET views=? WHERE id=?", ar.Views+1, ar.ID) diff --git a/server/internal/story/posts.go b/server/internal/story/posts.go index 981e1e5f..795f5f95 100644 --- a/server/internal/story/posts.go +++ b/server/internal/story/posts.go @@ -40,25 +40,18 @@ func UserPosts(user *models.User, uid string) (models.Posts, *e.Error) { return posts, nil } -func TagPosts(user *models.User, tagID int64) (models.Posts, *e.Error) { +func TagPosts(user *models.User, tagID string) (models.Posts, *e.Error) { // get post ids - rows, err := db.Conn.Query("select post_id from tag_post where tag_id=?", tagID) + postIDs, err := tags.GetTargetIDs(tagID) if err != nil { logger.Warn("get user posts error", "error", err) return nil, e.New(http.StatusInternalServerError, e.Internal) } - postIDs := make([]string, 0) - for rows.Next() { - var id string - rows.Scan(&id) - postIDs = append(postIDs, id) - } - ids := strings.Join(postIDs, "','") q := fmt.Sprintf("select id,slug,title,url,cover,brief,likes,views,creator,created,updated from posts where id in ('%s')", ids) - rows, err = db.Conn.Query(q) + rows, err := db.Conn.Query(q) if err != nil && err != sql.ErrNoRows { logger.Warn("get user posts error", "error", err) return nil, e.New(http.StatusInternalServerError, e.Internal) @@ -97,13 +90,13 @@ func BookmarkPosts(user *models.User, filter string) (models.Posts, *e.Error) { posts := getPosts(user, rows) for _, post := range posts { - ts, err := tags.GetStoryTags(post.ID) + _, rawTags, err := tags.GetTargetTags(post.ID) if err != nil { logger.Warn("get story tags error", "error", err) continue } - post.RawTags = ts + post.RawTags = rawTags } sort.Sort(posts) diff --git a/server/internal/tags/tags.go b/server/internal/tags/tags.go index 9ad47331..894edd5f 100644 --- a/server/internal/tags/tags.go +++ b/server/internal/tags/tags.go @@ -94,7 +94,7 @@ func GetTags() (models.Tags, *e.Error) { tag.SetCover() tags = append(tags, tag) - db.Conn.QueryRow("SELECT count(*) FROM tag_post WHERE tag_id=?", tag.ID).Scan(&tag.PostCount) + db.Conn.QueryRow("SELECT count(*) FROM tags_using WHERE tag_id=?", tag.ID).Scan(&tag.PostCount) } sort.Sort(tags) @@ -129,16 +129,16 @@ func GetTag(id string, name string) (*models.Tag, *e.Error) { md, _ := utils.Uncompress(rawmd) tag.Md = string(md) - db.Conn.QueryRow("SELECT count(*) FROM tag_post WHERE tag_id=?", tag.ID).Scan(&tag.PostCount) + db.Conn.QueryRow("SELECT count(*) FROM tags_using WHERE tag_id=?", tag.ID).Scan(&tag.PostCount) tag.SetCover() return tag, nil } -func GetSimpleTag(id int64, name string) (*models.Tag, *e.Error) { +func GetSimpleTag(id string, name string) (*models.Tag, *e.Error) { tag := &models.Tag{} - err := db.Conn.QueryRow("SELECT id,title,icon from tags where id=? or name=?", id, name).Scan( - &tag.ID, &tag.Title, &tag.Icon, + err := db.Conn.QueryRow("SELECT id,name,title,icon from tags where id=? or name=?", id, name).Scan( + &tag.ID, &tag.Name, &tag.Title, &tag.Icon, ) if err != nil { if err == sql.ErrNoRows { @@ -151,16 +151,16 @@ func GetSimpleTag(id int64, name string) (*models.Tag, *e.Error) { return tag, nil } -func GetStoryTags(storyID string) ([]*models.Tag, error) { - ids := make([]int64, 0) - rows, err := db.Conn.Query("SELECT tag_id FROM tag_post WHERE post_id=?", storyID) +func GetTargetTags(targetID string) ([]string, []*models.Tag, error) { + ids := make([]string, 0) + rows, err := db.Conn.Query("SELECT tag_id FROM tags_using WHERE target_id=?", targetID) if err != nil { - return nil, err + return nil, nil, err } rawTags := make([]*models.Tag, 0) for rows.Next() { - var id int64 + var id string err = rows.Scan(&id) ids = append(ids, id) @@ -170,5 +170,46 @@ func GetStoryTags(storyID string) ([]*models.Tag, error) { } } - return rawTags, nil + return ids, rawTags, nil +} + +func UpdateTargetTags(targetID string, tags []string) error { + _, err := db.Conn.Exec("DELETE FROM tags_using WHERE target_id=?", targetID) + if err != nil { + return err + } + + for _, tag := range tags { + _, err = db.Conn.Exec("INSERT INTO tags_using (tag_id,target_id) VALUES (?,?)", tag, targetID) + if err != nil { + logger.Warn("add post tag error", "error", err) + } + } + + return nil +} + +func DeleteTargetTags(targetID string) error { + _, err := db.Conn.Exec("DELETE FROM tags_using WHERE target_id=?", targetID) + if err != nil { + return err + } + + return nil +} + +func GetTargetIDs(tagID string) ([]string, error) { + rows, err := db.Conn.Query("select target_id from tags_using where tag_id=?", tagID) + if err != nil { + return nil, err + } + + ids := make([]string, 0) + for rows.Next() { + var id string + rows.Scan(&id) + ids = append(ids, id) + } + + return ids, nil } diff --git a/server/internal/user/users.go b/server/internal/user/users.go index 5941e0fe..f297f36d 100644 --- a/server/internal/user/users.go +++ b/server/internal/user/users.go @@ -54,24 +54,13 @@ func GetUserDetail(id string, username string) (*models.User, *e.Error) { } // get user skills - user.Skills = make([]string, 0) - user.RawSkills = make([]*models.Tag, 0) - rows, err := db.Conn.Query("SELECT skill_id from user_skills WHERE user_id=?", user.ID) - if err != nil && err != sql.ErrNoRows { - logger.Warn("query user skills error", "error", err) - } - for rows.Next() { - var skill string - rows.Scan(&skill) - user.Skills = append(user.Skills, skill) - - rawTag, err := tags.GetTag(skill, "") - if err != nil { - logger.Warn("get tag error", "error", err) - continue - } - user.RawSkills = append(user.RawSkills, rawTag) + skills, rawSkills, err := tags.GetTargetTags(user.ID) + if err != nil { + logger.Warn("get user skills error", "error", err) + return nil, e.New(http.StatusInternalServerError, e.Internal) } + user.RawSkills = rawSkills + user.Skills = skills return user, nil } @@ -108,16 +97,10 @@ func UpdateUser(u *models.User) *e.Error { } //update user skills - _, err = db.Conn.Exec("DELETE FROM user_skills WHERE user_id=?", u.ID) + err = tags.UpdateTargetTags(u.ID, u.Skills) if err != nil { - logger.Warn("delete user skills error", "error", err) - } - - for _, skill := range u.Skills { - _, err = db.Conn.Exec("INSERT INTO user_skills (user_id,skill_id) VALUES (?,?)", u.ID, skill) - if err != nil { - logger.Warn("add user skill error", "error", err) - } + logger.Warn("upate tags error", "error", err) + return e.New(http.StatusInternalServerError, e.Internal) } return nil diff --git a/src/components/comments/comment.tsx b/src/components/comments/comment.tsx index cc5c64f3..0cf97441 100644 --- a/src/components/comments/comment.tsx +++ b/src/components/comments/comment.tsx @@ -5,7 +5,7 @@ import Card from "components/card" import { getUserName } from "utils/user" import moment from 'moment' import { MarkdownRender } from "components/markdown-editor/render" -import Like from "components/posts/like" +import Like from "components/story/like" import { FaRegEdit, FaRegFlag, FaRegTrashAlt, FaReply, FaTrash } from "react-icons/fa" import { User } from "src/types/session" import CommentEditor from "./editor" diff --git a/src/components/comments/reply.tsx b/src/components/comments/reply.tsx index 9ad32740..e0afb2e7 100644 --- a/src/components/comments/reply.tsx +++ b/src/components/comments/reply.tsx @@ -5,7 +5,7 @@ import Card from "components/card" import { getUserName } from "utils/user" import moment from 'moment' import { MarkdownRender } from "components/markdown-editor/render" -import Like from "components/posts/like" +import Like from "components/story/like" import { FaRegEdit, FaRegFlag, FaRegTrashAlt, FaReply, FaTrash } from "react-icons/fa" import { User } from "src/types/session" import CommentEditor from "./editor" diff --git a/src/components/posts/bookmark.tsx b/src/components/story/bookmark.tsx similarity index 100% rename from src/components/posts/bookmark.tsx rename to src/components/story/bookmark.tsx diff --git a/src/components/posts/like.tsx b/src/components/story/like.tsx similarity index 100% rename from src/components/posts/like.tsx rename to src/components/story/like.tsx diff --git a/src/components/posts/no-more-posts.tsx b/src/components/story/no-more-posts.tsx similarity index 100% rename from src/components/posts/no-more-posts.tsx rename to src/components/story/no-more-posts.tsx diff --git a/src/components/posts/post-author.tsx b/src/components/story/post-author.tsx similarity index 100% rename from src/components/posts/post-author.tsx rename to src/components/story/post-author.tsx diff --git a/src/components/posts/post-card.tsx b/src/components/story/post-card.tsx similarity index 100% rename from src/components/posts/post-card.tsx rename to src/components/story/post-card.tsx diff --git a/src/components/posts/post-sidebar.tsx b/src/components/story/post-sidebar.tsx similarity index 100% rename from src/components/posts/post-sidebar.tsx rename to src/components/story/post-sidebar.tsx diff --git a/src/components/posts/posts.tsx b/src/components/story/posts.tsx similarity index 100% rename from src/components/posts/posts.tsx rename to src/components/story/posts.tsx diff --git a/src/components/posts/simple-post-card.tsx b/src/components/story/simple-post-card.tsx similarity index 100% rename from src/components/posts/simple-post-card.tsx rename to src/components/story/simple-post-card.tsx diff --git a/src/components/posts/tag-list-card.tsx b/src/components/story/tag-list-card.tsx similarity index 100% rename from src/components/posts/tag-list-card.tsx rename to src/components/story/tag-list-card.tsx diff --git a/src/components/posts/tag-text-card.tsx b/src/components/story/tag-text-card.tsx similarity index 100% rename from src/components/posts/tag-text-card.tsx rename to src/components/story/tag-text-card.tsx diff --git a/src/components/posts/text-post-card.tsx b/src/components/story/text-post-card.tsx similarity index 100% rename from src/components/posts/text-post-card.tsx rename to src/components/story/text-post-card.tsx diff --git a/src/components/posts/unicorn-like.tsx b/src/components/story/unicorn-like.tsx similarity index 100% rename from src/components/posts/unicorn-like.tsx rename to src/components/story/unicorn-like.tsx diff --git a/src/components/tags/tag-input.tsx b/src/components/tags/tag-input.tsx index 35f992ab..0cf801ed 100644 --- a/src/components/tags/tag-input.tsx +++ b/src/components/tags/tag-input.tsx @@ -3,7 +3,7 @@ import { Box, Popover, PopoverTrigger, Button, PopoverContent, PopoverBody, Inpu import { Tag } from "src/types/tag" import { requestApi } from "utils/axios/request" import { cloneDeep, findIndex } from "lodash" -import TagCard from 'src/components/posts/tag-list-card' +import TagCard from 'components/story/tag-list-card' import { config } from "configs/config" interface Props { options: Tag[]