♻️ Interactive v2 #1
| @@ -14,14 +14,9 @@ func RunMigration(source *gorm.DB) error { | |||||||
| 		&models.Category{}, | 		&models.Category{}, | ||||||
| 		&models.Tag{}, | 		&models.Tag{}, | ||||||
| 		&models.Moment{}, | 		&models.Moment{}, | ||||||
| 		&models.MomentLike{}, |  | ||||||
| 		&models.MomentDislike{}, |  | ||||||
| 		&models.Article{}, | 		&models.Article{}, | ||||||
| 		&models.ArticleLike{}, |  | ||||||
| 		&models.ArticleDislike{}, |  | ||||||
| 		&models.Comment{}, | 		&models.Comment{}, | ||||||
| 		&models.CommentLike{}, | 		&models.Reaction{}, | ||||||
| 		&models.CommentDislike{}, |  | ||||||
| 		&models.Attachment{}, | 		&models.Attachment{}, | ||||||
| 	); err != nil { | 	); err != nil { | ||||||
| 		return err | 		return err | ||||||
|   | |||||||
| @@ -17,12 +17,7 @@ type Account struct { | |||||||
| 	Moments         []Moment      `json:"moments" gorm:"foreignKey:AuthorID"` | 	Moments         []Moment      `json:"moments" gorm:"foreignKey:AuthorID"` | ||||||
| 	Articles        []Article     `json:"articles" gorm:"foreignKey:AuthorID"` | 	Articles        []Article     `json:"articles" gorm:"foreignKey:AuthorID"` | ||||||
| 	Attachments     []Attachment  `json:"attachments" gorm:"foreignKey:AuthorID"` | 	Attachments     []Attachment  `json:"attachments" gorm:"foreignKey:AuthorID"` | ||||||
| 	LikedMoments     []MomentLike     `json:"liked_moments"` | 	Reactions       []Reaction    `json:"reactions"` | ||||||
| 	DislikedMoments  []MomentDislike  `json:"disliked_moments"` |  | ||||||
| 	LikedArticles    []ArticleLike    `json:"liked_articles"` |  | ||||||
| 	DislikedArticles []ArticleDislike `json:"disliked_articles"` |  | ||||||
| 	LikedComments    []CommentLike    `json:"liked_comments"` |  | ||||||
| 	DislikedComments []CommentDislike `json:"disliked_comments"` |  | ||||||
| 	RealmIdentities []RealmMember `json:"identities"` | 	RealmIdentities []RealmMember `json:"identities"` | ||||||
| 	Realms          []Realm       `json:"realms"` | 	Realms          []Realm       `json:"realms"` | ||||||
| 	ExternalID      uint          `json:"external_id"` | 	ExternalID      uint          `json:"external_id"` | ||||||
|   | |||||||
| @@ -6,8 +6,7 @@ type Article struct { | |||||||
| 	Title       string     `json:"title"` | 	Title       string     `json:"title"` | ||||||
| 	Hashtags    []Tag      `json:"tags" gorm:"many2many:article_tags"` | 	Hashtags    []Tag      `json:"tags" gorm:"many2many:article_tags"` | ||||||
| 	Categories  []Category `json:"categories" gorm:"many2many:article_categories"` | 	Categories  []Category `json:"categories" gorm:"many2many:article_categories"` | ||||||
| 	LikedAccounts    []ArticleLike    `json:"liked_accounts"` | 	Reactions   []Reaction `json:"reactions"` | ||||||
| 	DislikedAccounts []ArticleDislike `json:"disliked_accounts"` |  | ||||||
| 	Description string     `json:"description"` | 	Description string     `json:"description"` | ||||||
| 	Content     string     `json:"content"` | 	Content     string     `json:"content"` | ||||||
| 	RealmID     *uint      `json:"realm_id"` | 	RealmID     *uint      `json:"realm_id"` | ||||||
|   | |||||||
| @@ -6,8 +6,7 @@ type Comment struct { | |||||||
| 	Content    string     `json:"content"` | 	Content    string     `json:"content"` | ||||||
| 	Hashtags   []Tag      `json:"tags" gorm:"many2many:comment_tags"` | 	Hashtags   []Tag      `json:"tags" gorm:"many2many:comment_tags"` | ||||||
| 	Categories []Category `json:"categories" gorm:"many2many:comment_categories"` | 	Categories []Category `json:"categories" gorm:"many2many:comment_categories"` | ||||||
| 	LikedAccounts    []CommentLike    `json:"liked_accounts"` | 	Reactions  []Reaction `json:"reactions"` | ||||||
| 	DislikedAccounts []CommentDislike `json:"disliked_accounts"` |  | ||||||
| 	ReplyID    *uint      `json:"reply_id"` | 	ReplyID    *uint      `json:"reply_id"` | ||||||
| 	ReplyTo    *Comment   `json:"reply_to" gorm:"foreignKey:ReplyID"` | 	ReplyTo    *Comment   `json:"reply_to" gorm:"foreignKey:ReplyID"` | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,8 +6,7 @@ type Moment struct { | |||||||
| 	Content    string     `json:"content"` | 	Content    string     `json:"content"` | ||||||
| 	Hashtags   []Tag      `json:"tags" gorm:"many2many:moment_tags"` | 	Hashtags   []Tag      `json:"tags" gorm:"many2many:moment_tags"` | ||||||
| 	Categories []Category `json:"categories" gorm:"many2many:moment_categories"` | 	Categories []Category `json:"categories" gorm:"many2many:moment_categories"` | ||||||
| 	LikedAccounts    []MomentLike    `json:"liked_accounts"` | 	Reactions  []Reaction `json:"reactions"` | ||||||
| 	DislikedAccounts []MomentDislike `json:"disliked_accounts"` |  | ||||||
| 	RealmID    *uint      `json:"realm_id"` | 	RealmID    *uint      `json:"realm_id"` | ||||||
| 	RepostID   *uint      `json:"repost_id"` | 	RepostID   *uint      `json:"repost_id"` | ||||||
| 	Realm      *Realm     `json:"realm"` | 	Realm      *Realm     `json:"realm"` | ||||||
|   | |||||||
| @@ -22,11 +22,7 @@ type PostBase struct { | |||||||
| 	AuthorID uint    `json:"author_id"` | 	AuthorID uint    `json:"author_id"` | ||||||
| 	Author   Account `json:"author"` | 	Author   Account `json:"author"` | ||||||
|  |  | ||||||
| 	// Dynamic Calculating Values | 	// TODO Give the reactions & replies & reposts info back | ||||||
| 	LikeCount    int64 `json:"like_count" gorm:"-"` |  | ||||||
| 	DislikeCount int64 `json:"dislike_count" gorm:"-"` |  | ||||||
| 	ReplyCount   int64 `json:"reply_count" gorm:"-"` |  | ||||||
| 	RepostCount  int64 `json:"repost_count" gorm:"-"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p PostBase) GetID() uint { | func (p PostBase) GetID() uint { | ||||||
| @@ -49,13 +45,6 @@ func (p PostBase) GetRealm() *Realm { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (p PostBase) SetReactInfo(info PostReactInfo) { |  | ||||||
| 	p.LikeCount = info.LikeCount |  | ||||||
| 	p.DislikeCount = info.DislikeCount |  | ||||||
| 	p.ReplyCount = info.ReplyCount |  | ||||||
| 	p.RepostCount = info.RepostCount |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type PostInterface interface { | type PostInterface interface { | ||||||
| 	GetID() uint | 	GetID() uint | ||||||
| 	GetHashtags() []Tag | 	GetHashtags() []Tag | ||||||
| @@ -67,5 +56,4 @@ type PostInterface interface { | |||||||
|  |  | ||||||
| 	SetHashtags([]Tag) | 	SetHashtags([]Tag) | ||||||
| 	SetCategories([]Category) | 	SetCategories([]Category) | ||||||
| 	SetReactInfo(PostReactInfo) |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,61 +1,25 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
| import "time" | import ( | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
| type CommentLike struct { | type ReactionAttitude = uint8 | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` |  | ||||||
| 	CreatedAt time.Time `json:"created_at"` | const ( | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` | 	AttitudeNeutral = ReactionAttitude(iota) | ||||||
| 	ArticleID *uint     `json:"article_id"` | 	AttitudePositive | ||||||
| 	MomentID  *uint     `json:"moment_id"` | 	AttitudeNegative | ||||||
| 	CommentID *uint     `json:"comment_id"` | ) | ||||||
| 	AccountID uint      `json:"account_id"` |  | ||||||
| } | type Reaction struct { | ||||||
|  |  | ||||||
| type CommentDislike struct { |  | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` |  | ||||||
| 	CreatedAt time.Time `json:"created_at"` |  | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` |  | ||||||
| 	ArticleID *uint     `json:"article_id"` |  | ||||||
| 	MomentID  *uint     `json:"moment_id"` |  | ||||||
| 	CommentID *uint     `json:"comment_id"` |  | ||||||
| 	AccountID uint      `json:"account_id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type MomentLike struct { |  | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` |  | ||||||
| 	CreatedAt time.Time `json:"created_at"` |  | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` |  | ||||||
| 	ArticleID *uint     `json:"article_id"` |  | ||||||
| 	MomentID  *uint     `json:"moment_id"` |  | ||||||
| 	CommentID *uint     `json:"comment_id"` |  | ||||||
| 	AccountID uint      `json:"account_id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type MomentDislike struct { |  | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` |  | ||||||
| 	CreatedAt time.Time `json:"created_at"` |  | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` |  | ||||||
| 	ArticleID *uint     `json:"article_id"` |  | ||||||
| 	MomentID  *uint     `json:"moment_id"` |  | ||||||
| 	CommentID *uint     `json:"comment_id"` |  | ||||||
| 	AccountID uint      `json:"account_id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ArticleLike struct { |  | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` |  | ||||||
| 	CreatedAt time.Time `json:"created_at"` |  | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` |  | ||||||
| 	ArticleID *uint     `json:"article_id"` |  | ||||||
| 	MomentID  *uint     `json:"moment_id"` |  | ||||||
| 	CommentID *uint     `json:"comment_id"` |  | ||||||
| 	AccountID uint      `json:"account_id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ArticleDislike struct { |  | ||||||
| 	ID        uint      `json:"id" gorm:"primaryKey"` | 	ID        uint      `json:"id" gorm:"primaryKey"` | ||||||
| 	CreatedAt time.Time `json:"created_at"` | 	CreatedAt time.Time `json:"created_at"` | ||||||
| 	UpdatedAt time.Time `json:"updated_at"` | 	UpdatedAt time.Time `json:"updated_at"` | ||||||
|  |  | ||||||
|  | 	Symbol   string           `json:"symbol"` | ||||||
|  | 	Attitude ReactionAttitude `json:"attitude"` | ||||||
|  |  | ||||||
| 	ArticleID *uint `json:"article_id"` | 	ArticleID *uint `json:"article_id"` | ||||||
| 	MomentID  *uint `json:"moment_id"` | 	MomentID  *uint `json:"moment_id"` | ||||||
| 	CommentID *uint `json:"comment_id"` | 	CommentID *uint `json:"comment_id"` | ||||||
|   | |||||||
| @@ -164,7 +164,7 @@ func editMoment(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	mx := getMomentContext().FilterAuthor(user.ID) | 	mx := getMomentContext().FilterAuthor(user.ID) | ||||||
|  |  | ||||||
| 	item, err := mx.Get(uint(id), true) | 	item, err := mx.Get(uint(id)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||||
| 	} | 	} | ||||||
| @@ -188,29 +188,35 @@ func reactMoment(c *fiber.Ctx) error { | |||||||
| 	user := c.Locals("principal").(models.Account) | 	user := c.Locals("principal").(models.Account) | ||||||
| 	id, _ := c.ParamsInt("momentId", 0) | 	id, _ := c.ParamsInt("momentId", 0) | ||||||
|  |  | ||||||
|  | 	var data struct { | ||||||
|  | 		Symbol   string                  `json:"symbol" validate:"required"` | ||||||
|  | 		Attitude models.ReactionAttitude `json:"attitude" validate:"required"` | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := BindAndValidate(c, &data); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	mx := getMomentContext() | 	mx := getMomentContext() | ||||||
|  |  | ||||||
| 	item, err := mx.Get(uint(id), true) | 	item, err := mx.Get(uint(id)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch strings.ToLower(c.Params("reactType")) { | 	reaction := models.Reaction{ | ||||||
| 	case "like": | 		Symbol:    data.Symbol, | ||||||
| 		if positive, err := mx.ReactLike(user, item.ID); err != nil { | 		Attitude:  data.Attitude, | ||||||
|  | 		AccountID: user.ID, | ||||||
|  | 		MomentID:  &item.ID, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if positive, reaction, err := mx.React(reaction); err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) | ||||||
| 	} else { | 	} else { | ||||||
| 			return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent)) | 		return c.Status(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent)).JSON(reaction) | ||||||
| 		} |  | ||||||
| 	case "dislike": |  | ||||||
| 		if positive, err := mx.ReactDislike(user, item.ID); err != nil { |  | ||||||
| 			return fiber.NewError(fiber.StatusBadRequest, err.Error()) |  | ||||||
| 		} else { |  | ||||||
| 			return c.SendStatus(lo.Ternary(positive, fiber.StatusCreated, fiber.StatusNoContent)) |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, "unsupported reaction") |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func deleteMoment(c *fiber.Ctx) error { | func deleteMoment(c *fiber.Ctx) error { | ||||||
| @@ -219,7 +225,7 @@ func deleteMoment(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	mx := getMomentContext().FilterAuthor(user.ID) | 	mx := getMomentContext().FilterAuthor(user.ID) | ||||||
|  |  | ||||||
| 	item, err := mx.Get(uint(id), true) | 	item, err := mx.Get(uint(id)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -76,7 +76,7 @@ func NewServer() { | |||||||
| 			moments.Get("/", listMoment) | 			moments.Get("/", listMoment) | ||||||
| 			moments.Get("/:momentId", getMoment) | 			moments.Get("/:momentId", getMoment) | ||||||
| 			moments.Post("/", authMiddleware, createMoment) | 			moments.Post("/", authMiddleware, createMoment) | ||||||
| 			moments.Post("/:momentId/react/:reactType", authMiddleware, reactMoment) | 			moments.Post("/:momentId/react", authMiddleware, reactMoment) | ||||||
| 			moments.Put("/:momentId", authMiddleware, editMoment) | 			moments.Put("/:momentId", authMiddleware, editMoment) | ||||||
| 			moments.Delete("/:momentId", authMiddleware, deleteMoment) | 			moments.Delete("/:momentId", authMiddleware, deleteMoment) | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -7,12 +7,16 @@ import ( | |||||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func LinkAccount(userinfo *proto.Userinfo) (models.Account, error) { | func LinkAccount(userinfo *proto.Userinfo) (models.Account, error) { | ||||||
| 	var account models.Account | 	var account models.Account | ||||||
|  | 	if userinfo == nil { | ||||||
|  | 		return account, fmt.Errorf("remote userinfo was not found") | ||||||
|  | 	} | ||||||
| 	if err := database.C.Where(&models.Account{ | 	if err := database.C.Where(&models.Account{ | ||||||
| 		ExternalID: uint(userinfo.Id), | 		ExternalID: uint(userinfo.Id), | ||||||
| 	}).First(&account).Error; err != nil { | 	}).First(&account).Error; err != nil { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import ( | |||||||
| 	"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto" | 	"code.smartsheep.studio/hydrogen/identity/pkg/grpc/proto" | ||||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/database" | 	"code.smartsheep.studio/hydrogen/interactive/pkg/database" | ||||||
| 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | 	"code.smartsheep.studio/hydrogen/interactive/pkg/models" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	pluralize "github.com/gertd/go-pluralize" | 	pluralize "github.com/gertd/go-pluralize" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| @@ -14,35 +15,6 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	reactUnionSelect = `SELECT t.id AS post_id, |  | ||||||
|        COALESCE(l.like_count, 0)    AS like_count, |  | ||||||
|        COALESCE(d.dislike_count, 0) AS dislike_count--!COMMA!-- |  | ||||||
|        --!REPLY_UNION_COLUMN!-- --!BOTH_COMMA!-- |  | ||||||
|        --!REPOST_UNION_COLUMN!--  |  | ||||||
| 	FROM %s t |  | ||||||
|          LEFT JOIN (SELECT %s_id, COUNT(*) AS like_count |  | ||||||
|                     FROM %s_likes |  | ||||||
|                     GROUP BY %s_id) l ON t.id = l.%s_id |  | ||||||
|          LEFT JOIN (SELECT %s_id, COUNT(*) AS dislike_count |  | ||||||
|                     FROM %s_likes |  | ||||||
|                     GROUP BY %s_id) d ON t.id = d.%s_id |  | ||||||
|         --!REPLY_UNION_SELECT!-- |  | ||||||
| 		--!REPOST_UNION_SELECT!-- |  | ||||||
| 	WHERE t.id = ?` |  | ||||||
| 	// TODO Solve for the cross table query(like articles -> comments) |  | ||||||
| 	replyUnionColumn = `COALESCE(r.reply_count, 0) AS reply_count` |  | ||||||
| 	replyUnionSelect = `LEFT JOIN (SELECT reply_id, COUNT(*) AS reply_count |  | ||||||
| 		FROM %s |  | ||||||
| 		WHERE reply_id IS NOT NULL |  | ||||||
|         GROUP BY reply_id) r ON t.id = r.reply_id` |  | ||||||
| 	repostUnionColumn = `COALESCE(rp.repost_count, 0) AS repost_count` |  | ||||||
| 	repostUnionSelect = `LEFT JOIN (SELECT repost_id, COUNT(*) AS repost_count |  | ||||||
| 		FROM %s |  | ||||||
| 		WHERE repost_id IS NOT NULL |  | ||||||
| 		GROUP BY repost_id) rp ON t.id = rp.repost_id` |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type PostTypeContext[T models.PostInterface] struct { | type PostTypeContext[T models.PostInterface] struct { | ||||||
| 	Tx *gorm.DB | 	Tx *gorm.DB | ||||||
|  |  | ||||||
| @@ -62,7 +34,11 @@ func (v *PostTypeContext[T]) GetTableName(plural ...bool) string { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (v *PostTypeContext[T]) Preload() *PostTypeContext[T] { | func (v *PostTypeContext[T]) Preload() *PostTypeContext[T] { | ||||||
| 	v.Tx.Preload("Author").Preload("Attachments").Preload("Categories").Preload("Hashtags") | 	v.Tx.Preload("Author"). | ||||||
|  | 		Preload("Attachments"). | ||||||
|  | 		Preload("Categories"). | ||||||
|  | 		Preload("Hashtags"). | ||||||
|  | 		Preload("Reactions") | ||||||
|  |  | ||||||
| 	if v.CanReply { | 	if v.CanReply { | ||||||
| 		v.Tx.Preload("ReplyTo") | 		v.Tx.Preload("ReplyTo") | ||||||
| @@ -123,45 +99,12 @@ func (v *PostTypeContext[T]) SortCreatedAt(order string) *PostTypeContext[T] { | |||||||
| 	return v | 	return v | ||||||
| } | } | ||||||
|  |  | ||||||
| func (v *PostTypeContext[T]) BuildReactInfoSql() string { | func (v *PostTypeContext[T]) Get(id uint) (T, error) { | ||||||
| 	column := strings.ToLower(v.TypeName) |  | ||||||
| 	table := viper.GetString("database.prefix") + v.GetTableName() |  | ||||||
| 	pluralTable := viper.GetString("database.prefix") + v.GetTableName(true) |  | ||||||
| 	sql := fmt.Sprintf(reactUnionSelect, pluralTable, column, table, column, column, column, table, column, column) |  | ||||||
|  |  | ||||||
| 	if v.CanReply { |  | ||||||
| 		sql = strings.Replace(sql, "--!REPLY_UNION_COLUMN!--", replyUnionColumn, 1) |  | ||||||
| 		sql = strings.Replace(sql, "--!REPLY_UNION_SELECT!--", fmt.Sprintf(replyUnionSelect, pluralTable), 1) |  | ||||||
| 	} |  | ||||||
| 	if v.CanRepost { |  | ||||||
| 		sql = strings.Replace(sql, "--!REPOST_UNION_COLUMN!--", repostUnionColumn, 1) |  | ||||||
| 		sql = strings.Replace(sql, "--!REPOST_UNION_SELECT!--", fmt.Sprintf(repostUnionSelect, pluralTable), 1) |  | ||||||
| 	} |  | ||||||
| 	if v.CanReply || v.CanRepost { |  | ||||||
| 		sql = strings.ReplaceAll(sql, "--!COMMA!--", ",") |  | ||||||
| 	} |  | ||||||
| 	if v.CanReply && v.CanRepost { |  | ||||||
| 		sql = strings.ReplaceAll(sql, "--!BOTH_COMMA!--", ",") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return sql |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (v *PostTypeContext[T]) Get(id uint, noReact ...bool) (T, error) { |  | ||||||
| 	var item T | 	var item T | ||||||
| 	if err := v.Preload().Tx.Where("id = ?", id).First(&item).Error; err != nil { | 	if err := v.Preload().Tx.Where("id = ?", id).First(&item).Error; err != nil { | ||||||
| 		return item, err | 		return item, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var reactInfo models.PostReactInfo |  | ||||||
|  |  | ||||||
| 	if len(noReact) <= 0 || !noReact[0] { |  | ||||||
| 		sql := v.BuildReactInfoSql() |  | ||||||
| 		database.C.Raw(sql, item.GetID()).Scan(&reactInfo) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	item.SetReactInfo(reactInfo) |  | ||||||
|  |  | ||||||
| 	return item, nil | 	return item, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -185,33 +128,6 @@ func (v *PostTypeContext[T]) List(take int, offset int, noReact ...bool) ([]T, e | |||||||
| 		return items, err | 		return items, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	idx := lo.Map(items, func(item T, _ int) uint { |  | ||||||
| 		return item.GetID() |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	var reactInfo []struct { |  | ||||||
| 		PostID       uint  `json:"post_id"` |  | ||||||
| 		LikeCount    int64 `json:"like_count"` |  | ||||||
| 		DislikeCount int64 `json:"dislike_count"` |  | ||||||
| 		ReplyCount   int64 `json:"reply_count"` |  | ||||||
| 		RepostCount  int64 `json:"repost_count"` |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(noReact) <= 0 || !noReact[0] { |  | ||||||
| 		sql := v.BuildReactInfoSql() |  | ||||||
| 		database.C.Raw(sql, idx).Scan(&reactInfo) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	itemMap := lo.SliceToMap(items, func(item T) (uint, T) { |  | ||||||
| 		return item.GetID(), item |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	for _, info := range reactInfo { |  | ||||||
| 		if item, ok := itemMap[info.PostID]; ok { |  | ||||||
| 			item.SetReactInfo(info) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return items, nil | 	return items, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -320,32 +236,14 @@ func (v *PostTypeContext[T]) Delete(item T) error { | |||||||
| 	return database.C.Delete(&item).Error | 	return database.C.Delete(&item).Error | ||||||
| } | } | ||||||
|  |  | ||||||
| func (v *PostTypeContext[T]) ReactLike(user models.Account, id uint) (bool, error) { | func (v *PostTypeContext[T]) React(reaction models.Reaction) (bool, models.Reaction, error) { | ||||||
| 	var count int64 | 	if err := database.C.Where(reaction).First(&reaction).Error; err != nil { | ||||||
| 	table := viper.GetString("database.prefix") + v.GetTableName() + "_likes" | 		if errors.Is(err, gorm.ErrRecordNotFound) { | ||||||
| 	tx := database.C.Where("account_id = ?", user.ID).Where(v.GetTableName()+"id = ?", id) | 			return true, reaction, database.C.Save(&reaction).Error | ||||||
| 	if tx.Count(&count); count <= 0 { |  | ||||||
| 		return true, database.C.Table(table).Create(map[string]any{ |  | ||||||
| 			"AccountID":       user.ID, |  | ||||||
| 			v.TypeName + "ID": id, |  | ||||||
| 		}).Error |  | ||||||
| 		} else { | 		} else { | ||||||
| 		column := strings.ToLower(v.TypeName) | 			return true, reaction, err | ||||||
| 		return false, tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE account_id = ? AND %s_id = ?", table, column), user.ID, id).Error |  | ||||||
| 		} | 		} | ||||||
| } |  | ||||||
|  |  | ||||||
| func (v *PostTypeContext[T]) ReactDislike(user models.Account, id uint) (bool, error) { |  | ||||||
| 	var count int64 |  | ||||||
| 	table := viper.GetString("database.prefix") + v.GetTableName() + "_dislikes" |  | ||||||
| 	tx := database.C.Where("account_id = ?", user.ID).Where(v.GetTableName()+"id = ?", id) |  | ||||||
| 	if tx.Count(&count); count <= 0 { |  | ||||||
| 		return true, database.C.Table(table).Create(map[string]any{ |  | ||||||
| 			"AccountID":       user.ID, |  | ||||||
| 			v.TypeName + "ID": id, |  | ||||||
| 		}).Error |  | ||||||
| 	} else { | 	} else { | ||||||
| 		column := strings.ToLower(v.TypeName) | 		return false, reaction, database.C.Delete(&reaction).Error | ||||||
| 		return false, tx.Raw(fmt.Sprintf("DELETE FROM %s WHERE account_id = ? AND %s_id = ?", table, column), user.ID, id).Error |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user