Files
yunzerwebsiteallinone/go/controllers/backend_theme.go
T
2026-08-19 23:34:28 +08:00

340 lines
8.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package controllers
import (
"encoding/json"
"io"
"regexp"
"strings"
"time"
"server/models"
"server/pkg/jwtutil"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
)
// BackendThemeController 官网模板选用(租户端只读选用,模板管理在 platform 端)
type BackendThemeController struct {
beego.Controller
}
func (c *BackendThemeController) cmsClaims() (*jwtutil.Claims, error) {
return cmsBackendClaims(&c.Controller)
}
func (c *BackendThemeController) cmsJSONErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
_ = c.ServeJSON()
}
var reThemeCode = regexp.MustCompile(`^[a-zA-Z0-9_\-]+$`)
// List GET /backend/theme 仅返回启用中的模板(含内置 default)
func (c *BackendThemeController) List() {
claims, err := c.cmsClaims()
if err != nil {
c.cmsJSONErr(401, 401, err.Error())
return
}
tid := cmsEffectiveTid(&c.Controller, claims)
if tid == 0 {
c.cmsJSONErr(400, 400, "tid不能为空")
return
}
var rows []models.CmsFrontendTemplate
_, err = models.Orm.QueryTable(new(models.CmsFrontendTemplate)).
Filter("status", 1).
Filter("delete_time__isnull", true).
OrderBy("sort", "id").
All(&rows)
if err != nil && err != orm.ErrNoRows {
c.cmsJSONErr(500, 500, "获取模板列表失败")
return
}
list := make([]map[string]interface{}, 0, len(rows)+1)
hasDefault := false
for _, r := range rows {
if r.Code == "default" {
hasDefault = true
}
list = append(list, map[string]interface{}{
"key": r.Code,
"name": r.Name,
"description": r.Description,
"preview": r.Preview,
"version": "1.0",
"author": "",
})
}
// 内置 default 模板始终可选,即使未登记入库
if !hasDefault {
list = append([]map[string]interface{}{{
"key": "default",
"name": "默认模板",
"description": "系统内置默认模板",
"preview": "/themes/default/static/preview.png",
"version": "1.0",
"author": "",
}}, list...)
}
current := cmsTenantTemplateCode(tid)
if current == "" {
current = "default"
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": map[string]interface{}{"list": list, "currentTheme": current},
}
_ = c.ServeJSON()
}
// cmsTenantTemplateCode 读取租户当前选用的模板编码
func cmsTenantTemplateCode(tid uint64) string {
var row models.TenantSiteSetting
err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
One(&row)
if err != nil {
return ""
}
return row.TemplateCode
}
// Switch POST /backend/theme/switch 切换租户使用的模板(写入 template_code)
func (c *BackendThemeController) Switch() {
claims, err := c.cmsClaims()
if err != nil {
c.cmsJSONErr(401, 401, err.Error())
return
}
tid := cmsEffectiveTid(&c.Controller, claims)
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.cmsJSONErr(400, 400, "参数错误")
return
}
var body struct {
Tid uint64 `json:"tid"`
ThemeKey string `json:"theme_key"`
}
if err := json.Unmarshal(raw, &body); err != nil {
c.cmsJSONErr(400, 400, "参数错误")
return
}
if tid == 0 {
tid = body.Tid
}
if tid == 0 {
c.cmsJSONErr(400, 400, "tid不能为空")
return
}
code := strings.TrimSpace(body.ThemeKey)
if code == "" || !reThemeCode.MatchString(code) {
c.cmsJSONErr(400, 400, "模板编码不合法")
return
}
// 非内置 default 模板必须已登记且启用中
if code != "default" {
cnt, _ := models.Orm.QueryTable(new(models.CmsFrontendTemplate)).
Filter("code", code).
Filter("status", 1).
Filter("delete_time__isnull", true).
Count()
if cnt == 0 {
c.cmsJSONErr(400, 400, "模板不存在或已停用")
return
}
}
if err := cmsUpsertTenantTemplate(tid, code); err != nil {
c.cmsJSONErr(500, 500, "切换失败")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "切换成功", "data": map[string]interface{}{"currentTheme": code}}
_ = c.ServeJSON()
}
// cmsUpsertTenantTemplate 写入租户模板编码(站点设置不存在则创建)
func cmsUpsertTenantTemplate(tid uint64, code string) error {
now := time.Now()
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Count()
if err != nil {
return err
}
if cnt == 0 {
_, err = models.Orm.Insert(&models.TenantSiteSetting{
Tid: tid,
TemplateCode: code,
CreateTime: now,
UpdateTime: &now,
})
return err
}
_, err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Update(map[string]interface{}{"template_code": code, "update_time": now})
return err
}
// GetData GET /backend/theme/data 返回站点设置(供模板配置展示)
func (c *BackendThemeController) GetData() {
claims, err := c.cmsClaims()
if err != nil {
c.cmsJSONErr(401, 401, err.Error())
return
}
tid := cmsEffectiveTid(&c.Controller, claims)
if tid == 0 {
c.cmsJSONErr(400, 400, "tid不能为空")
return
}
var row models.TenantSiteSetting
err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
One(&row)
if err != nil && err != orm.ErrNoRows {
c.cmsJSONErr(500, 500, "获取失败")
return
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"tid": tid,
"sitename": row.Sitename,
"logo": row.Logo,
"logow": row.Logow,
"ico": row.Ico,
"description": row.Description,
"copyright": row.Copyright,
"companyname": row.Companyname,
"icp": row.Icp,
"companyintroduction": row.Companyintroduction,
"template_code": row.TemplateCode,
},
}
_ = c.ServeJSON()
}
// SaveData POST /backend/theme/data 保存站点设置(模板配置用,白名单字段)
func (c *BackendThemeController) SaveData() {
claims, err := c.cmsClaims()
if err != nil {
c.cmsJSONErr(401, 401, err.Error())
return
}
tid := cmsEffectiveTid(&c.Controller, claims)
if tid == 0 {
c.cmsJSONErr(400, 400, "tid不能为空")
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.cmsJSONErr(400, 400, "参数错误")
return
}
var body map[string]interface{}
if err := json.Unmarshal(raw, &body); err != nil {
c.cmsJSONErr(400, 400, "参数错误")
return
}
fields := map[string]interface{}{}
allowed := []string{"sitename", "logo", "logow", "ico", "description", "copyright", "companyname", "icp", "companyintroduction"}
for _, k := range allowed {
if v, ok := body[k]; ok {
fields[k] = strings.TrimSpace(toString(v))
}
}
if len(fields) == 0 {
c.cmsJSONErr(400, 400, "没有可保存的字段")
return
}
fields["update_time"] = time.Now()
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Count()
if err != nil {
c.cmsJSONErr(500, 500, "保存失败")
return
}
if cnt == 0 {
now := time.Now()
row := &models.TenantSiteSetting{Tid: tid, CreateTime: now, UpdateTime: &now}
if v, ok := fields["sitename"]; ok {
row.Sitename = v.(string)
}
if v, ok := fields["logo"]; ok {
row.Logo = v.(string)
}
if v, ok := fields["logow"]; ok {
row.Logow = v.(string)
}
if v, ok := fields["ico"]; ok {
row.Ico = v.(string)
}
if v, ok := fields["description"]; ok {
row.Description = v.(string)
}
if v, ok := fields["copyright"]; ok {
row.Copyright = v.(string)
}
if v, ok := fields["companyname"]; ok {
row.Companyname = v.(string)
}
if v, ok := fields["icp"]; ok {
row.Icp = v.(string)
}
if v, ok := fields["companyintroduction"]; ok {
row.Companyintroduction = v.(string)
}
if _, err := models.Orm.Insert(row); err != nil {
c.cmsJSONErr(500, 500, "保存失败")
return
}
} else {
if _, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Update(fields); err != nil {
c.cmsJSONErr(500, 500, "保存失败")
return
}
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
// toString 宽松地把 JSON 值转成字符串
func toString(v interface{}) string {
if v == nil {
return ""
}
if s, ok := v.(string); ok {
return s
}
return ""
}