package controllers import ( "fmt" "net/url" "os" "path/filepath" "strconv" "strings" "server/models" "server/pkg/tagengine" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // TenantSiteController 租户官网前台渲染(Go 直出 HTML,无需登录) // 路由:GET / | /news | /news/:id | /page/:path type TenantSiteController struct { beego.Controller } // siteNotFoundPage 未匹配域名的提示页 const siteNotFoundPage = ` 站点不存在

站点不存在

当前域名未绑定任何租户官网,请检查域名配置。

` // renderErrorPage 渲染失败的兜底页 const renderErrorPage = ` 页面异常

页面暂时无法访问

%s

` // resolveTid 按访问 Host 识别租户 ID。 // 规则:dev 环境支持 X-Tenant-ID 头切换 → localhost/127.0.0.1 默认租户 1 // → 平台主域名池命中返回租户 1(平台站点)→ 租户域名表命中返回对应 tid // → 全部未命中返回 0,false(不再默认回落租户 1) func (c *TenantSiteController) resolveTid() (uint64, bool) { runmode, _ := beego.AppConfig.String("runmode") if runmode == "dev" { if h := strings.TrimSpace(c.Ctx.Request.Header.Get("X-Tenant-ID")); h != "" { if decoded, err := url.QueryUnescape(h); err == nil { h = decoded } if n, err := strconv.ParseUint(h, 10, 64); err == nil && n > 0 { return n, true } var tenant models.SystemTenant cond := orm.NewCondition().Or("tenant_name", h).Or("tenant_short_name", h) if err := models.Orm.QueryTable(new(models.SystemTenant)).SetCond(cond).One(&tenant); err == nil { return tenant.ID, true } } } host := c.Ctx.Request.Host if idx := strings.Index(host, ":"); idx > 0 { host = host[:idx] } if host == "localhost" || host == "127.0.0.1" { return 1, true } // 平台主域名池命中 → 平台站点(tid 1 + 默认模板) poolCnt, _ := models.Orm.QueryTable(new(models.SystemDomainPool)). Filter("main_domain", host). Filter("status", 1). Filter("delete_time__isnull", true). Count() if poolCnt > 0 { return 1, true } var td models.SystemTenantDomain err := models.Orm.QueryTable(new(models.SystemTenantDomain)). Filter("full_domain", host). Filter("status", 1). Filter("delete_time__isnull", true). One(&td) if err == nil && td.Tid != nil && *td.Tid > 0 { return *td.Tid, true } return 0, false } // resolveTemplateCode 解析租户选用模板编码,缺失/停用/目录不存在均回退 default func (c *TenantSiteController) resolveTemplateCode(tid uint64) string { code := strings.TrimSpace(cmsTenantTemplateCode(tid)) if code != "" && code != "default" { cnt, _ := models.Orm.QueryTable(new(models.CmsFrontendTemplate)). Filter("code", code). Filter("status", 1). Filter("delete_time__isnull", true). Count() if cnt == 0 { code = "" } } if code == "" { code = "default" } if !cmsTemplateDirExists(code) { code = "default" } return code } // writeHTML 直出 HTML func (c *TenantSiteController) writeHTML(status int, html string) { c.Ctx.Output.SetStatus(status) c.Ctx.Output.Header("Content-Type", "text/html; charset=utf-8") c.Ctx.WriteString(html) } // render 解析并输出模板文件 func (c *TenantSiteController) render(file string, mod func(ctx *tagengine.RenderCtx)) { tid, ok := c.resolveTid() if !ok { c.writeHTML(404, siteNotFoundPage) return } code := c.resolveTemplateCode(tid) dir := filepath.Join(cmsThemesRoot(), code) if _, err := os.Stat(filepath.Join(dir, file)); err != nil { c.writeHTML(404, fmt.Sprintf(renderErrorPage, "模板页面不存在")) return } ctx := &tagengine.RenderCtx{Tid: tid, Page: 1, PageSize: 10} if mod != nil { mod(ctx) } out, err := tagengine.Render(dir, file, ctx) if err != nil { c.writeHTML(500, fmt.Sprintf(renderErrorPage, "模板渲染失败")) return } c.writeHTML(200, out) } // Index GET / 首页 func (c *TenantSiteController) Index() { c.render("index.html", nil) } // News GET /news 新闻列表页(?page=N 分页) func (c *TenantSiteController) News() { page, _ := c.GetInt("page", 1) if page < 1 { page = 1 } c.render("news.html", func(ctx *tagengine.RenderCtx) { ctx.Page = page }) } // NewsDetail GET /news/:id 文章详情页 func (c *TenantSiteController) NewsDetail() { id, _ := c.GetUint64(":id") if id == 0 { c.writeHTML(404, fmt.Sprintf(renderErrorPage, "文章不存在")) return } c.render("news_detail.html", func(ctx *tagengine.RenderCtx) { ctx.ArticleID = id }) } // Page GET /page/:path 单页 func (c *TenantSiteController) Page() { path := strings.TrimSpace(c.GetString(":path")) if path == "" || strings.ContainsAny(path, "/\\.") { c.writeHTML(404, fmt.Sprintf(renderErrorPage, "页面不存在")) return } c.render("page.html", func(ctx *tagengine.RenderCtx) { ctx.PagePath = path }) }