package handler import ( "net/http" "strconv" "strings" "soul-api/internal/database" "soul-api/internal/model" "github.com/gin-gonic/gin" ) 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 } // 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"` } 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 == "" || req.Title == "" || req.Content == "" { c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题和正文不能为空"}) return } if len([]rune(req.Title)) > 40 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "标题最多 40 字"}) return } if len([]rune(req.Content)) > 5000 { c.JSON(http.StatusOK, gin.H{"success": false, "error": "正文最多 5000 字"}) return } 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, } 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).Count(&total) var rows []model.SuperArticle if err := db.Where("user_id = ?", authorUserID). 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, "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, }) } // 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 } 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 } 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, "authorNickname": strings.TrimSpace(author.Nickname), "authorAvatar": strings.TrimSpace(author.Avatar), "createdAt": row.CreatedAt, }, }) }