go-platform/controllers/backend_site_settings.go
2026-06-02 21:15:13 +08:00

608 lines
16 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"
"fmt"
"io"
"strconv"
"strings"
"time"
"server/models"
"server/pkg/jwtutil"
beego "github.com/beego/beego/v2/server/web"
)
// BackendSiteSettingsController 租户站点设置(站点基本信息)
// 对应前端 normalSettings.vue 的:
// - GET /backend/normalInfos
// - POST /backend/saveNormalInfos
// - GET /platform/normalInfos
// - POST /platform/saveNormalInfos
type BackendSiteSettingsController struct {
beego.Controller
}
func (c *BackendSiteSettingsController) jsonErr(httpStatus, bizCode int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg}
_ = c.ServeJSON()
}
func (c *BackendSiteSettingsController) claimsByPath() (*jwtutil.Claims, error) {
auth := c.Ctx.Request.Header.Get("Authorization")
if auth == "" {
return nil, fmt.Errorf("未登录")
}
parts := strings.SplitN(auth, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
return nil, fmt.Errorf("认证信息格式错误")
}
claims, err := jwtutil.ParseToken(parts[1])
if err != nil {
return nil, fmt.Errorf("无效的token")
}
path := strings.ToLower(c.Ctx.Request.URL.Path)
if strings.HasPrefix(path, "/platform/") {
if claims.UserType != "platform" {
return nil, fmt.Errorf("无权访问")
}
} else if strings.HasPrefix(path, "/backend/") {
if claims.UserType != "backend" {
return nil, fmt.Errorf("无权访问")
}
}
return claims, nil
}
func parseBackendUint64Flexible(v interface{}) uint64 {
if v == nil {
return 0
}
switch x := v.(type) {
case float64:
if x <= 0 {
return 0
}
return uint64(x)
case string:
s := strings.TrimSpace(x)
if s == "" {
return 0
}
n, err := strconv.ParseUint(s, 10, 64)
if err != nil || n == 0 {
return 0
}
return n
default:
return 0
}
}
type backendNormalInfosOutput struct {
Sitename string `json:"sitename"`
Companyintroduction string `json:"companyintroduction"`
Description string `json:"description"`
Copyright string `json:"copyright"`
Companyname string `json:"companyname"`
Icp string `json:"icp"`
Logo string `json:"logo"`
Logow string `json:"logow"`
Ico string `json:"ico"`
}
// GetNormalInfos GET /backend/normalInfos 或 /platform/normalInfos
func (c *BackendSiteSettingsController) GetNormalInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
// 优先使用 token 中的租户 id若为 0则允许前端通过查询参数传入兼容历史/平台端)。
tid := uint64(claims.TenantId)
if tid == 0 {
tidStr := strings.TrimSpace(c.GetString("tid"))
if tidStr != "" {
if n, err := strconv.ParseUint(tidStr, 10, 64); err == nil {
tid = n
}
}
}
out := backendNormalInfosOutput{
Sitename: "",
Companyintroduction: "",
Description: "",
Copyright: "",
Companyname: "",
Icp: "",
Logo: "",
Logow: "",
Ico: "",
}
// tid 缺失时不报错,直接返回空对象给前端渲染(避免 UI 直接崩)。
if tid == 0 {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
return
}
var rows []models.TenantSiteSetting
_, err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Limit(1).
All(&rows)
if err != nil {
c.jsonErr(500, 500, "获取失败: "+err.Error())
return
}
if len(rows) > 0 {
r := rows[0]
out.Sitename = r.Sitename
out.Companyintroduction = r.Companyintroduction
out.Logo = r.Logo
out.Logow = r.Logow
out.Ico = r.Ico
out.Description = r.Description
out.Copyright = r.Copyright
out.Companyname = r.Companyname
out.Icp = r.Icp
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
}
type backendNormalInfosPayload struct {
// 前端会传 tid但我们仍优先使用 token 的 tenant_id
Tid interface{} `json:"tid"`
Sitename string `json:"sitename"`
Companyintroduction string `json:"companyintroduction"`
Logo string `json:"logo"`
Logow string `json:"logow"`
Ico string `json:"ico"`
Description string `json:"description"`
Copyright string `json:"copyright"`
Companyname string `json:"companyname"`
Icp string `json:"icp"`
}
// SaveNormalInfos POST /backend/saveNormalInfos 或 /platform/saveNormalInfos
func (c *BackendSiteSettingsController) SaveNormalInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var p backendNormalInfosPayload
if uerr := json.Unmarshal(raw, &p); uerr != nil {
c.jsonErr(400, 400, "参数错误")
return
}
tid := uint64(claims.TenantId)
if tid == 0 {
tid = parseBackendUint64Flexible(p.Tid)
}
if tid == 0 {
c.jsonErr(400, 400, "tid不能为空")
return
}
sitename := strings.TrimSpace(p.Sitename)
if sitename == "" {
c.jsonErr(400, 400, "站点名称不能为空")
return
}
now := time.Now()
up := map[string]interface{}{
"tid": tid,
"sitename": sitename,
"companyintroduction": strings.TrimSpace(p.Companyintroduction),
"logo": strings.TrimSpace(p.Logo),
"logow": strings.TrimSpace(p.Logow),
"ico": strings.TrimSpace(p.Ico),
"description": strings.TrimSpace(p.Description),
"copyright": strings.TrimSpace(p.Copyright),
"companyname": strings.TrimSpace(p.Companyname),
"icp": strings.TrimSpace(p.Icp),
"update_time": now,
}
cnt, err := models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Count()
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
if cnt == 0 {
row := &models.TenantSiteSetting{
Tid: tid,
Sitename: sitename,
Companyintroduction: strings.TrimSpace(p.Companyintroduction),
Logo: strings.TrimSpace(p.Logo),
Logow: strings.TrimSpace(p.Logow),
Ico: strings.TrimSpace(p.Ico),
Description: strings.TrimSpace(p.Description),
Copyright: strings.TrimSpace(p.Copyright),
Companyname: strings.TrimSpace(p.Companyname),
Icp: strings.TrimSpace(p.Icp),
CreateTime: now,
UpdateTime: &now,
}
_, err = models.Orm.Insert(row)
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
} else {
_, err = models.Orm.QueryTable(new(models.TenantSiteSetting)).
Filter("tid", tid).
Filter("delete_time__isnull", true).
Update(up)
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
func (c *BackendSiteSettingsController) resolveBackendTenantID(claims *jwtutil.Claims, payloadTid interface{}) uint64 {
tid := uint64(claims.TenantId)
if tid == 0 {
tid = parseBackendUint64Flexible(payloadTid)
}
if tid == 0 {
tidStr := strings.TrimSpace(c.GetString("tid"))
if tidStr != "" {
if n, err := strconv.ParseUint(tidStr, 10, 64); err == nil {
tid = n
}
}
}
return tid
}
func (c *BackendSiteSettingsController) ensureBackendSettingItemsTable() error {
_, err := models.Orm.Raw(`
CREATE TABLE IF NOT EXISTS yz_system_tenant_setting_items (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
tid BIGINT UNSIGNED NOT NULL DEFAULT 0,
setting_key VARCHAR(64) NOT NULL DEFAULT '',
setting_value LONGTEXT NULL,
create_time DATETIME NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
delete_time DATETIME NULL DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY uk_tid_key (tid, setting_key),
KEY idx_tid (tid),
KEY idx_delete_time (delete_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租户站点扩展设置';
`).Exec()
return err
}
func (c *BackendSiteSettingsController) getBackendSettingItems(tid uint64, keys []string) (map[string]string, error) {
out := make(map[string]string, len(keys))
for _, key := range keys {
out[key] = ""
}
if err := c.ensureBackendSettingItemsTable(); err != nil {
return out, err
}
type rowItem struct {
SettingKey string
SettingValue string
}
var rows []rowItem
_, err := models.Orm.Raw(
"SELECT setting_key, IFNULL(setting_value, '') AS setting_value FROM yz_system_tenant_setting_items WHERE tid = ? AND setting_key IN ('"+strings.Join(keys, "','")+"') AND delete_time IS NULL",
tid,
).QueryRows(&rows)
if err != nil {
return out, err
}
for _, row := range rows {
out[row.SettingKey] = row.SettingValue
}
return out, nil
}
func (c *BackendSiteSettingsController) saveBackendSettingItems(tid uint64, values map[string]string) error {
if err := c.ensureBackendSettingItemsTable(); err != nil {
return err
}
for key, value := range values {
_, err := models.Orm.Raw(`
INSERT INTO yz_system_tenant_setting_items (tid, setting_key, setting_value, create_time, update_time)
VALUES (?, ?, ?, NOW(), NOW())
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), update_time = NOW(), delete_time = NULL
`, tid, key, value).Exec()
if err != nil {
return err
}
}
return nil
}
// GetLegalInfos GET /backend/legalInfos
func (c *BackendSiteSettingsController) GetLegalInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
tid := c.resolveBackendTenantID(claims, nil)
if tid == 0 {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": []map[string]string{
{"label": "legalNotice", "value": ""},
{"label": "privacyTerms", "value": ""},
}}
_ = c.ServeJSON()
return
}
values, err := c.getBackendSettingItems(tid, []string{"legalNotice", "privacyTerms"})
if err != nil {
c.jsonErr(500, 500, "获取失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": []map[string]string{
{"label": "legalNotice", "value": values["legalNotice"]},
{"label": "privacyTerms", "value": values["privacyTerms"]},
}}
_ = c.ServeJSON()
}
type backendLegalInfosPayload struct {
Tid interface{} `json:"tid"`
LegalNotice string `json:"legalNotice"`
PrivacyTerms string `json:"privacyTerms"`
}
// SaveLegalInfos POST /backend/saveLegalInfos
func (c *BackendSiteSettingsController) SaveLegalInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var p backendLegalInfosPayload
if err := json.Unmarshal(raw, &p); err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
tid := c.resolveBackendTenantID(claims, p.Tid)
if tid == 0 {
c.jsonErr(400, 400, "tid不能为空")
return
}
err = c.saveBackendSettingItems(tid, map[string]string{
"legalNotice": strings.TrimSpace(p.LegalNotice),
"privacyTerms": strings.TrimSpace(p.PrivacyTerms),
})
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
// GetCompanyInfos GET /backend/companyInfos
func (c *BackendSiteSettingsController) GetCompanyInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
tid := c.resolveBackendTenantID(claims, nil)
out := map[string]interface{}{
"contact_phone": "",
"contact_email": "",
"address": "",
"worktime": "",
}
if tid == 0 {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
return
}
var row models.SystemTenant
err = models.Orm.QueryTable(new(models.SystemTenant)).
Filter("id", tid).
Filter("delete_time__isnull", true).
One(&row)
if err != nil {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
return
}
if row.ContactPhone != nil {
out["contact_phone"] = *row.ContactPhone
}
if row.ContactEmail != nil {
out["contact_email"] = *row.ContactEmail
}
if row.Address != nil {
out["address"] = *row.Address
}
if row.Worktime != nil {
out["worktime"] = *row.Worktime
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
}
type backendCompanyInfosPayload struct {
Tid interface{} `json:"tid"`
ContactPhone string `json:"contact_phone"`
ContactEmail string `json:"contact_email"`
Address string `json:"address"`
Worktime string `json:"worktime"`
}
// SaveCompanyInfos POST /backend/saveCompanyInfos
func (c *BackendSiteSettingsController) SaveCompanyInfos() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var p backendCompanyInfosPayload
if err := json.Unmarshal(raw, &p); err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
tid := c.resolveBackendTenantID(claims, p.Tid)
if tid == 0 {
c.jsonErr(400, 400, "tid不能为空")
return
}
_, err = models.Orm.QueryTable(new(models.SystemTenant)).
Filter("id", tid).
Filter("delete_time__isnull", true).
Update(map[string]interface{}{
"contact_phone": strings.TrimSpace(p.ContactPhone),
"contact_email": strings.TrimSpace(p.ContactEmail),
"address": strings.TrimSpace(p.Address),
"worktime": strings.TrimSpace(p.Worktime),
"update_time": time.Now(),
})
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}
// GetCompanySeo GET /backend/companySeo
func (c *BackendSiteSettingsController) GetCompanySeo() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
tid := c.resolveBackendTenantID(claims, nil)
out := map[string]string{
"seoTitle": "",
"seoKeywords": "",
"seoDescription": "",
}
if tid == 0 {
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
return
}
values, err := c.getBackendSettingItems(tid, []string{"seoTitle", "seoKeywords", "seoDescription"})
if err != nil {
c.jsonErr(500, 500, "获取失败: "+err.Error())
return
}
out["seoTitle"] = values["seoTitle"]
out["seoKeywords"] = values["seoKeywords"]
out["seoDescription"] = values["seoDescription"]
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": out}
_ = c.ServeJSON()
}
type backendCompanySeoPayload struct {
Tid interface{} `json:"tid"`
SeoTitle string `json:"seoTitle"`
SeoKeywords string `json:"seoKeywords"`
SeoDescription string `json:"seoDescription"`
}
// SaveCompanySeo POST /backend/saveCompanySeo
func (c *BackendSiteSettingsController) SaveCompanySeo() {
claims, err := c.claimsByPath()
if err != nil {
c.jsonErr(401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
var p backendCompanySeoPayload
if err := json.Unmarshal(raw, &p); err != nil {
c.jsonErr(400, 400, "参数错误")
return
}
tid := c.resolveBackendTenantID(claims, p.Tid)
if tid == 0 {
c.jsonErr(400, 400, "tid不能为空")
return
}
err = c.saveBackendSettingItems(tid, map[string]string{
"seoTitle": strings.TrimSpace(p.SeoTitle),
"seoKeywords": strings.TrimSpace(p.SeoKeywords),
"seoDescription": strings.TrimSpace(p.SeoDescription),
})
if err != nil {
c.jsonErr(500, 500, "保存失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "保存成功"}
_ = c.ServeJSON()
}