- Introduced member business tabs for profile and dynamics, improving navigation and user experience. - Implemented image parsing and normalization functions to handle article images effectively. - Updated the UI to display article thumbnails and manage loading states, enhancing visual feedback. - Refactored data synchronization methods to ensure member business tabs reflect current content accurately. This update aims to streamline member profile interactions and improve the overall presentation of member-related content.
618 lines
18 KiB
Go
618 lines
18 KiB
Go
package handler
|
||
|
||
import (
|
||
"encoding/json"
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"soul-api/internal/database"
|
||
"soul-api/internal/model"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type superArticleAuthorRow struct {
|
||
ID string `gorm:"column:id"`
|
||
Nickname string `gorm:"column:nickname"`
|
||
Avatar string `gorm:"column:avatar"`
|
||
}
|
||
|
||
func loadSuperArticleAuthorMap(ids []string) map[string]superArticleAuthorRow {
|
||
out := map[string]superArticleAuthorRow{}
|
||
if len(ids) == 0 {
|
||
return out
|
||
}
|
||
var rows []superArticleAuthorRow
|
||
db := database.DB()
|
||
_ = db.Table("users").Select("id", "nickname", "avatar").Where("id IN ?", ids).Find(&rows).Error
|
||
for _, r := range rows {
|
||
key := strings.TrimSpace(r.ID)
|
||
if key == "" {
|
||
continue
|
||
}
|
||
out[key] = r
|
||
}
|
||
return out
|
||
}
|
||
|
||
const (
|
||
superArticleMaxImages = 9
|
||
superArticleMaxImageURLLen = 2048
|
||
)
|
||
|
||
func parseSuperArticleImagesJSON(raw string) []string {
|
||
raw = strings.TrimSpace(raw)
|
||
if raw == "" {
|
||
return nil
|
||
}
|
||
var arr []string
|
||
if err := json.Unmarshal([]byte(raw), &arr); err != nil {
|
||
return nil
|
||
}
|
||
out := make([]string, 0, len(arr))
|
||
for _, u := range arr {
|
||
u = strings.TrimSpace(u)
|
||
if u != "" {
|
||
out = append(out, u)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
// superArticleFeedAllowedAuthorIDs 文章中出现过、且当前仍为超级个体的用户 ID(用于动态广场)
|
||
func superArticleFeedAllowedAuthorIDs(db *gorm.DB) []string {
|
||
var raw []string
|
||
_ = db.Model(&model.SuperArticle{}).
|
||
Where("audit_status = ?", model.SuperArticleAuditApproved).
|
||
Distinct("user_id").
|
||
Pluck("user_id", &raw).Error
|
||
out := make([]string, 0, len(raw))
|
||
seen := map[string]bool{}
|
||
for _, uid := range raw {
|
||
uid = strings.TrimSpace(uid)
|
||
if uid == "" || seen[uid] {
|
||
continue
|
||
}
|
||
seen[uid] = true
|
||
if _, ok := miniprogramSuperIndividualPerson(db, uid); ok {
|
||
out = append(out, uid)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
// MiniprogramSuperArticleFeed GET /api/miniprogram/super/articles/feed?page=&pageSize=
|
||
// 动态广场:当前仍为超级个体的作者所发文章,按创建时间倒序分页。
|
||
func MiniprogramSuperArticleFeed(c *gin.Context) {
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 30 {
|
||
pageSize = 10
|
||
}
|
||
|
||
db := database.DB()
|
||
allowed := superArticleFeedAllowedAuthorIDs(db)
|
||
if len(allowed) == 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "list": []gin.H{}, "total": int64(0), "page": page, "pageSize": pageSize})
|
||
return
|
||
}
|
||
|
||
q := db.Model(&model.SuperArticle{}).
|
||
Where("user_id IN ?", allowed).
|
||
Where("audit_status = ?", model.SuperArticleAuditApproved)
|
||
var total int64
|
||
if err := q.Count(&total).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
|
||
var rows []model.SuperArticle
|
||
if err := db.Where("user_id IN ?", allowed).
|
||
Where("audit_status = ?", model.SuperArticleAuditApproved).
|
||
Order("created_at DESC").
|
||
Offset((page - 1) * pageSize).
|
||
Limit(pageSize).
|
||
Find(&rows).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
|
||
idSet := map[string]bool{}
|
||
var authorIDs []string
|
||
for _, r := range rows {
|
||
uid := strings.TrimSpace(r.UserID)
|
||
if uid == "" || idSet[uid] {
|
||
continue
|
||
}
|
||
idSet[uid] = true
|
||
authorIDs = append(authorIDs, uid)
|
||
}
|
||
authorMap := loadSuperArticleAuthorMap(authorIDs)
|
||
|
||
list := make([]gin.H, 0, len(rows))
|
||
for _, r := range rows {
|
||
content := strings.TrimSpace(r.Content)
|
||
preview := content
|
||
if len([]rune(preview)) > 120 {
|
||
preview = string([]rune(preview)[:120]) + "..."
|
||
}
|
||
a := authorMap[strings.TrimSpace(r.UserID)]
|
||
list = append(list, gin.H{
|
||
"id": r.ID,
|
||
"userId": r.UserID,
|
||
"title": r.Title,
|
||
"content": r.Content,
|
||
"preview": preview,
|
||
"images": parseSuperArticleImagesJSON(r.Images),
|
||
"authorNickname": strings.TrimSpace(a.Nickname),
|
||
"authorAvatar": strings.TrimSpace(a.Avatar),
|
||
"createdAt": r.CreatedAt,
|
||
})
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"list": list,
|
||
"total": total,
|
||
"page": page,
|
||
"pageSize": pageSize,
|
||
})
|
||
}
|
||
|
||
func deriveSuperArticleTitleFromContent(content string) string {
|
||
content = strings.TrimSpace(content)
|
||
if content == "" {
|
||
return ""
|
||
}
|
||
parts := strings.SplitN(content, "\n", 2)
|
||
line := strings.TrimSpace(parts[0])
|
||
line = strings.TrimRight(line, "\r")
|
||
if line == "" {
|
||
return ""
|
||
}
|
||
rs := []rune(line)
|
||
if len(rs) > 40 {
|
||
return string(rs[:40])
|
||
}
|
||
return line
|
||
}
|
||
|
||
// finalizeSuperArticleTitleAndBody 发动态:标题可省略(由正文首行或默认文案补齐);正文与配图至少一项。
|
||
func finalizeSuperArticleTitleAndBody(title, content string, imageCount int) (outTitle, outBody, errMsg string) {
|
||
title = strings.TrimSpace(title)
|
||
content = strings.TrimSpace(content)
|
||
if content == "" && imageCount == 0 {
|
||
return "", "", "请填写文字或添加配图"
|
||
}
|
||
if len([]rune(content)) > 5000 {
|
||
return "", "", "正文最多 5000 字"
|
||
}
|
||
outTitle = title
|
||
if outTitle != "" && len([]rune(outTitle)) > 40 {
|
||
outTitle = string([]rune(outTitle)[:40])
|
||
}
|
||
if outTitle == "" {
|
||
outTitle = deriveSuperArticleTitleFromContent(content)
|
||
}
|
||
if outTitle == "" && imageCount > 0 {
|
||
outTitle = "分享图片"
|
||
}
|
||
if outTitle == "" {
|
||
outTitle = "动态"
|
||
}
|
||
if len([]rune(outTitle)) > 40 {
|
||
outTitle = string([]rune(outTitle)[:40])
|
||
}
|
||
return outTitle, content, ""
|
||
}
|
||
|
||
func normalizeSuperArticleImageURLs(in []string) ([]string, string) {
|
||
if len(in) == 0 {
|
||
return []string{}, ""
|
||
}
|
||
if len(in) > superArticleMaxImages {
|
||
return nil, "配图最多 9 张"
|
||
}
|
||
out := make([]string, 0, len(in))
|
||
for _, u := range in {
|
||
u = strings.TrimSpace(u)
|
||
if u == "" {
|
||
continue
|
||
}
|
||
if len(u) > superArticleMaxImageURLLen {
|
||
return nil, "图片链接过长"
|
||
}
|
||
low := strings.ToLower(u)
|
||
if !strings.HasPrefix(low, "http://") && !strings.HasPrefix(low, "https://") {
|
||
return nil, "图片链接须以 http(s):// 开头"
|
||
}
|
||
out = append(out, u)
|
||
}
|
||
if len(out) > superArticleMaxImages {
|
||
return nil, "配图最多 9 张"
|
||
}
|
||
return out, ""
|
||
}
|
||
|
||
// MiniprogramSuperArticleCreate POST /api/miniprogram/super/articles
|
||
// 仅超级个体本人可发文章。
|
||
func MiniprogramSuperArticleCreate(c *gin.Context) {
|
||
var req struct {
|
||
UserID string `json:"userId"`
|
||
Title string `json:"title"`
|
||
Content string `json:"content"`
|
||
Images []string `json:"images"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
req.UserID = strings.TrimSpace(req.UserID)
|
||
req.Title = strings.TrimSpace(req.Title)
|
||
req.Content = strings.TrimSpace(req.Content)
|
||
if req.UserID == "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
|
||
imgURLs, imgErr := normalizeSuperArticleImageURLs(req.Images)
|
||
if imgErr != "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": imgErr})
|
||
return
|
||
}
|
||
imgJSONBytes, jerr := json.Marshal(imgURLs)
|
||
if jerr != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "配图数据无效"})
|
||
return
|
||
}
|
||
|
||
title, body, verr := finalizeSuperArticleTitleAndBody(req.Title, req.Content, len(imgURLs))
|
||
if verr != "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": verr})
|
||
return
|
||
}
|
||
req.Title, req.Content = title, body
|
||
|
||
db := database.DB()
|
||
if _, ok := miniprogramSuperIndividualPerson(db, req.UserID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可发文章", "forbidden": true})
|
||
return
|
||
}
|
||
|
||
row := model.SuperArticle{
|
||
UserID: req.UserID,
|
||
Title: req.Title,
|
||
Content: req.Content,
|
||
Images: string(imgJSONBytes),
|
||
AuditStatus: model.SuperArticleAuditPending,
|
||
RejectReason: "",
|
||
}
|
||
if !SuperArticleAuditEnabled() {
|
||
row.AuditStatus = model.SuperArticleAuditApproved
|
||
row.RejectReason = ""
|
||
}
|
||
if err := db.Create(&row).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "data": gin.H{"id": row.ID}})
|
||
}
|
||
|
||
// MiniprogramSuperArticleList GET /api/miniprogram/super/articles?authorUserId=&page=&pageSize=
|
||
func MiniprogramSuperArticleList(c *gin.Context) {
|
||
authorUserID := strings.TrimSpace(c.Query("authorUserId"))
|
||
if authorUserID == "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 authorUserId"})
|
||
return
|
||
}
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 30 {
|
||
pageSize = 10
|
||
}
|
||
|
||
db := database.DB()
|
||
if _, ok := miniprogramSuperIndividualPerson(db, authorUserID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": true, "list": []gin.H{}, "total": 0, "page": page, "pageSize": pageSize})
|
||
return
|
||
}
|
||
|
||
var total int64
|
||
db.Model(&model.SuperArticle{}).
|
||
Where("user_id = ?", authorUserID).
|
||
Where("audit_status = ?", model.SuperArticleAuditApproved).
|
||
Count(&total)
|
||
var rows []model.SuperArticle
|
||
if err := db.Where("user_id = ?", authorUserID).
|
||
Where("audit_status = ?", model.SuperArticleAuditApproved).
|
||
Order("created_at DESC").
|
||
Offset((page - 1) * pageSize).
|
||
Limit(pageSize).
|
||
Find(&rows).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
|
||
authorMap := loadSuperArticleAuthorMap([]string{authorUserID})
|
||
author := authorMap[authorUserID]
|
||
list := make([]gin.H, 0, len(rows))
|
||
for _, r := range rows {
|
||
content := strings.TrimSpace(r.Content)
|
||
preview := content
|
||
if len([]rune(preview)) > 88 {
|
||
preview = string([]rune(preview)[:88]) + "..."
|
||
}
|
||
list = append(list, gin.H{
|
||
"id": r.ID,
|
||
"userId": r.UserID,
|
||
"title": r.Title,
|
||
"content": r.Content,
|
||
"preview": preview,
|
||
"images": parseSuperArticleImagesJSON(r.Images),
|
||
"authorNickname": strings.TrimSpace(author.Nickname),
|
||
"authorAvatar": strings.TrimSpace(author.Avatar),
|
||
"createdAt": r.CreatedAt,
|
||
})
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"list": list,
|
||
"total": total,
|
||
"page": page,
|
||
"pageSize": pageSize,
|
||
})
|
||
}
|
||
|
||
// MiniprogramSuperArticleMine GET /api/miniprogram/super/articles/mine?userId=&page=&pageSize=
|
||
// 本人全部动态(含待审/通过/驳回),仅超级个体可查。
|
||
func MiniprogramSuperArticleMine(c *gin.Context) {
|
||
userID := strings.TrimSpace(c.Query("userId"))
|
||
if userID == "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"})
|
||
return
|
||
}
|
||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||
if page < 1 {
|
||
page = 1
|
||
}
|
||
if pageSize < 1 || pageSize > 30 {
|
||
pageSize = 10
|
||
}
|
||
|
||
db := database.DB()
|
||
if _, ok := miniprogramSuperIndividualPerson(db, userID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可查看", "forbidden": true})
|
||
return
|
||
}
|
||
|
||
var total int64
|
||
db.Model(&model.SuperArticle{}).Where("user_id = ?", userID).Count(&total)
|
||
var rows []model.SuperArticle
|
||
if err := db.Where("user_id = ?", userID).
|
||
Order("created_at DESC").
|
||
Offset((page - 1) * pageSize).
|
||
Limit(pageSize).
|
||
Find(&rows).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
|
||
authorMap := loadSuperArticleAuthorMap([]string{userID})
|
||
author := authorMap[userID]
|
||
list := make([]gin.H, 0, len(rows))
|
||
for _, r := range rows {
|
||
content := strings.TrimSpace(r.Content)
|
||
preview := content
|
||
if len([]rune(preview)) > 88 {
|
||
preview = string([]rune(preview)[:88]) + "..."
|
||
}
|
||
ast := strings.TrimSpace(strings.ToLower(r.AuditStatus))
|
||
if ast == "" {
|
||
ast = model.SuperArticleAuditApproved
|
||
}
|
||
list = append(list, gin.H{
|
||
"id": r.ID,
|
||
"userId": r.UserID,
|
||
"title": r.Title,
|
||
"content": r.Content,
|
||
"preview": preview,
|
||
"images": parseSuperArticleImagesJSON(r.Images),
|
||
"authorNickname": strings.TrimSpace(author.Nickname),
|
||
"authorAvatar": strings.TrimSpace(author.Avatar),
|
||
"createdAt": r.CreatedAt,
|
||
"auditStatus": ast,
|
||
"rejectReason": strings.TrimSpace(r.RejectReason),
|
||
})
|
||
}
|
||
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"list": list,
|
||
"total": total,
|
||
"page": page,
|
||
"pageSize": pageSize,
|
||
})
|
||
}
|
||
|
||
// MiniprogramSuperArticleDetail GET /api/miniprogram/super/articles/:id
|
||
func MiniprogramSuperArticleDetail(c *gin.Context) {
|
||
idRaw := strings.TrimSpace(c.Param("id"))
|
||
id, err := strconv.ParseUint(idRaw, 10, 64)
|
||
if err != nil || id == 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
viewer := strings.TrimSpace(c.Query("viewerUserId"))
|
||
db := database.DB()
|
||
var row model.SuperArticle
|
||
if err := db.First(&row, id).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "文章不存在"})
|
||
return
|
||
}
|
||
if _, ok := miniprogramSuperIndividualPerson(db, row.UserID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "文章不可见"})
|
||
return
|
||
}
|
||
if !model.SuperArticlePublicApproved(row.AuditStatus) {
|
||
if viewer == "" || viewer != strings.TrimSpace(row.UserID) {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "文章审核中或不可见"})
|
||
return
|
||
}
|
||
}
|
||
ast := strings.TrimSpace(strings.ToLower(row.AuditStatus))
|
||
if ast == "" {
|
||
ast = model.SuperArticleAuditApproved
|
||
}
|
||
authorMap := loadSuperArticleAuthorMap([]string{row.UserID})
|
||
author := authorMap[row.UserID]
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"success": true,
|
||
"data": gin.H{
|
||
"id": row.ID,
|
||
"userId": row.UserID,
|
||
"title": row.Title,
|
||
"content": row.Content,
|
||
"images": parseSuperArticleImagesJSON(row.Images),
|
||
"authorNickname": strings.TrimSpace(author.Nickname),
|
||
"authorAvatar": strings.TrimSpace(author.Avatar),
|
||
"createdAt": row.CreatedAt,
|
||
"auditStatus": ast,
|
||
"rejectReason": strings.TrimSpace(row.RejectReason),
|
||
},
|
||
})
|
||
}
|
||
|
||
// MiniprogramSuperArticleUpdate PUT /api/miniprogram/super/articles/:id
|
||
// 仅作者本人;状态须为待审核或已驳回;提交后重置为待审核。
|
||
func MiniprogramSuperArticleUpdate(c *gin.Context) {
|
||
idRaw := strings.TrimSpace(c.Param("id"))
|
||
id, err := strconv.ParseUint(idRaw, 10, 64)
|
||
if err != nil || id == 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
var req struct {
|
||
UserID string `json:"userId"`
|
||
Title string `json:"title"`
|
||
Content string `json:"content"`
|
||
Images []string `json:"images"`
|
||
}
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
req.UserID = strings.TrimSpace(req.UserID)
|
||
req.Title = strings.TrimSpace(req.Title)
|
||
req.Content = strings.TrimSpace(req.Content)
|
||
if req.UserID == "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
|
||
imgURLs, imgErr := normalizeSuperArticleImageURLs(req.Images)
|
||
if imgErr != "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": imgErr})
|
||
return
|
||
}
|
||
imgJSONBytes, jerr := json.Marshal(imgURLs)
|
||
if jerr != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "配图数据无效"})
|
||
return
|
||
}
|
||
|
||
title, body, verr := finalizeSuperArticleTitleAndBody(req.Title, req.Content, len(imgURLs))
|
||
if verr != "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": verr})
|
||
return
|
||
}
|
||
req.Title, req.Content = title, body
|
||
|
||
db := database.DB()
|
||
var row model.SuperArticle
|
||
if err := db.First(&row, id).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "文章不存在"})
|
||
return
|
||
}
|
||
if strings.TrimSpace(row.UserID) != req.UserID {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "无权修改"})
|
||
return
|
||
}
|
||
if _, ok := miniprogramSuperIndividualPerson(db, req.UserID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可修改", "forbidden": true})
|
||
return
|
||
}
|
||
st := strings.TrimSpace(strings.ToLower(row.AuditStatus))
|
||
if st == "" {
|
||
st = model.SuperArticleAuditApproved
|
||
}
|
||
if st != model.SuperArticleAuditPending && st != model.SuperArticleAuditRejected {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅待审核或已驳回的动态可修改"})
|
||
return
|
||
}
|
||
|
||
row.Title = req.Title
|
||
row.Content = req.Content
|
||
row.Images = string(imgJSONBytes)
|
||
if SuperArticleAuditEnabled() {
|
||
row.AuditStatus = model.SuperArticleAuditPending
|
||
row.RejectReason = ""
|
||
} else {
|
||
row.AuditStatus = model.SuperArticleAuditApproved
|
||
row.RejectReason = ""
|
||
}
|
||
if err := db.Save(&row).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
}
|
||
|
||
// MiniprogramSuperArticleDelete 删除动态(作者本人 + 超级个体)。
|
||
// DELETE /api/miniprogram/super/articles/:id?userId=
|
||
// POST /api/miniprogram/super/articles/:id/delete?userId= (与 DELETE 等价,便于不支持 DELETE 的客户端调试)
|
||
func MiniprogramSuperArticleDelete(c *gin.Context) {
|
||
idRaw := strings.TrimSpace(c.Param("id"))
|
||
id, err := strconv.ParseUint(idRaw, 10, 64)
|
||
if err != nil || id == 0 {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "参数错误"})
|
||
return
|
||
}
|
||
userID := strings.TrimSpace(c.Query("userId"))
|
||
if userID == "" {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "缺少 userId"})
|
||
return
|
||
}
|
||
|
||
db := database.DB()
|
||
if _, ok := miniprogramSuperIndividualPerson(db, userID); !ok {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "仅超级个体可操作", "forbidden": true})
|
||
return
|
||
}
|
||
|
||
var row model.SuperArticle
|
||
if err := db.First(&row, id).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "动态不存在"})
|
||
return
|
||
}
|
||
if strings.TrimSpace(row.UserID) != userID {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": "无权删除"})
|
||
return
|
||
}
|
||
|
||
if err := db.Delete(&row).Error; err != nil {
|
||
c.JSON(http.StatusOK, gin.H{"success": false, "error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
}
|