package handlers import ( "context" "encoding/json" "net/http" "strings" "time" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" "yh_web/server/config" "yh_web/server/models" "github.com/gin-gonic/gin" ) const homepageSlug = "index" const officialSiteConfigID = "official_site_id" type officialSiteDoc struct { ID string `bson:"_id"` SiteID string `bson:"site_id"` } // getOfficialSiteID 从 system_config 读取官网站点 ID;未设置则返回第一个站点的 ID func getOfficialSiteID(ctx context.Context) string { coll := config.GetDB(config.DBName).Collection("system_config") var doc officialSiteDoc err := coll.FindOne(ctx, bson.M{"_id": officialSiteConfigID}).Decode(&doc) if err == nil && doc.SiteID != "" { return doc.SiteID } sitesColl := config.GetDB(config.DBName).Collection("sites") opts := options.FindOne().SetSort(bson.D{{Key: "created_at", Value: -1}}) var site models.Site if err := sitesColl.FindOne(ctx, bson.M{}, opts).Decode(&site); err == nil { return site.ID.Hex() } return "" } // GetWebHomepage 前台:获取官网站点首页数据(无需鉴权) func GetWebHomepage(c *gin.Context) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() siteID := getOfficialSiteID(ctx) if siteID == "" { c.JSON(http.StatusOK, defaultHomepageData()) return } coll := config.GetDB(config.DBName).Collection("pages") var page models.Page err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusOK, defaultHomepageData()) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var data models.HomepageData if page.Content != "" { if err := json.Unmarshal([]byte(page.Content), &data); err != nil { c.JSON(http.StatusOK, defaultHomepageData()) return } } else { data = defaultHomepageData() } c.JSON(http.StatusOK, data) } // GetHomepage 获取站点首页数据 func GetHomepage(c *gin.Context) { siteID := c.Param("site_id") if siteID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"}) return } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() coll := config.GetDB(config.DBName).Collection("pages") var page models.Page err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page) if err != nil { if err == mongo.ErrNoDocuments { c.JSON(http.StatusOK, defaultHomepageData()) return } c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } var data models.HomepageData if page.Content != "" { if err := json.Unmarshal([]byte(page.Content), &data); err != nil { c.JSON(http.StatusOK, defaultHomepageData()) return } } else { data = defaultHomepageData() } c.JSON(http.StatusOK, data) } // UpdateHomepage 更新站点首页数据 func UpdateHomepage(c *gin.Context) { siteID := c.Param("site_id") if siteID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"}) return } var data models.HomepageData if err := c.ShouldBindJSON(&data); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } body, err := json.Marshal(data) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() coll := config.GetDB(config.DBName).Collection("pages") now := time.Now().Format(time.RFC3339) filter := bson.M{"site_id": siteID, "slug": homepageSlug} update := bson.M{ "$set": bson.M{ "site_id": siteID, "slug": homepageSlug, "title": data.Title, "type": "homepage", "content": string(body), "updated_at": now, }, } opts := options.UpdateOne().SetUpsert(true) _, err = coll.UpdateOne(ctx, filter, update, opts) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "保存成功"}) } // DownloadHomepage 下载首页 HTML func DownloadHomepage(c *gin.Context) { siteID := c.Param("site_id") if siteID == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "请提供 site_id"}) return } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() coll := config.GetDB(config.DBName).Collection("pages") var page models.Page var data models.HomepageData err := coll.FindOne(ctx, bson.M{"site_id": siteID, "slug": homepageSlug}).Decode(&page) if err == nil && page.Content != "" { _ = json.Unmarshal([]byte(page.Content), &data) } if err != nil || page.Content == "" { data = defaultHomepageData() } html := renderHomepageHTML(&data) c.Header("Content-Disposition", "attachment; filename=index.html") c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(html)) } func defaultHomepageData() models.HomepageData { return models.HomepageData{ LogoText: "宇恒一号", NavLinks: []models.NavLink{{Label: "产品简介", URL: "#intro"}, {Label: "产品视频", URL: "#videos"}, {Label: "联系我们", URL: "#contact"}}, Title: "宇恒一号", Subtitle: "", Description: "", DownloadText: "下载", DownloadURL: "#", Platforms: []models.PlatformItem{}, Version: "", LaunchYear: "发布日期:以官网为准", BadgeText: "完全免费", DownloadWindowsURL: "/promotion/downloads/yuheng-windows.zip", DownloadAndroidURL: "/promotion/downloads/yuheng-android.apk", Features: []models.FeatureItem{ {Title: "星际导航", Desc: "先进的 AI 导航系统,精准定位您的需求,引领探索之旅"}, {Title: "量子同步", Desc: "跨维度数据同步技术,您的数据在多宇宙中保持一致"}, {Title: "星际防护", Desc: "来自未来的安全加密协议,守护您的数字资产安全"}, }, FooterText: "© 2024 宇恒一号 · 成都宇信达智能科技有限公司", LiveRoomURL: "", LiveRoomTitle: "视频直播", } } // renderHomepageHTML 根据数据生成首页 HTML(简化版,保留原样式与结构) func renderHomepageHTML(d *models.HomepageData) string { if d == nil { d = &models.HomepageData{} } titleChars := splitTitle(d.Title) navHTML := "" for _, l := range d.NavLinks { navHTML += `` + escape(l.Label) + `` } platformsHTML := "" for _, p := range d.Platforms { platformsHTML += `
` + escape(f.Desc) + `
") sb.WriteString(escape(d.Subtitle)) sb.WriteString("
\n")
sb.WriteString(strings.ReplaceAll(escape(d.Description), "\n", "
\n"))
sb.WriteString("