151 lines
3.6 KiB
Go
151 lines
3.6 KiB
Go
package storage
|
||
|
||
import (
|
||
"strconv"
|
||
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
|
||
"mengyastore-backend/internal/database"
|
||
)
|
||
|
||
type SiteStore struct {
|
||
db *gorm.DB
|
||
}
|
||
|
||
func NewSiteStore(db *gorm.DB) (*SiteStore, error) {
|
||
return &SiteStore{db: db}, nil
|
||
}
|
||
|
||
func (s *SiteStore) get(key string) (string, error) {
|
||
var row database.SiteSettingRow
|
||
// `key` 是 MySQL 保留字,需用反引号转义以避免 SQL 语法错误。
|
||
// 使用 Find+Limit 而不是 First:缺键时不产生 ErrRecordNotFound,避免 GORM 默认 logger 刷「record not found」。
|
||
if err := s.db.Where("`key` = ?", key).Limit(1).Find(&row).Error; err != nil {
|
||
return "", err
|
||
}
|
||
if row.Key == "" {
|
||
return "", nil
|
||
}
|
||
return row.Value, nil
|
||
}
|
||
|
||
func (s *SiteStore) set(key, value string) error {
|
||
return s.db.Clauses(clause.OnConflict{
|
||
Columns: []clause.Column{{Name: "key"}},
|
||
DoUpdates: clause.AssignmentColumns([]string{"value"}),
|
||
}).Create(&database.SiteSettingRow{Key: key, Value: value}).Error
|
||
}
|
||
|
||
func (s *SiteStore) GetTotalVisits() (int, error) {
|
||
v, err := s.get("totalVisits")
|
||
if err != nil || v == "" {
|
||
return 0, err
|
||
}
|
||
n, _ := strconv.Atoi(v)
|
||
return n, nil
|
||
}
|
||
|
||
func (s *SiteStore) IncrementVisits() (int, error) {
|
||
current, err := s.GetTotalVisits()
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
current++
|
||
if err := s.set("totalVisits", strconv.Itoa(current)); err != nil {
|
||
return 0, err
|
||
}
|
||
return current, nil
|
||
}
|
||
|
||
func (s *SiteStore) GetMaintenance() (enabled bool, reason string, err error) {
|
||
v, err := s.get("maintenance")
|
||
if err != nil {
|
||
return false, "", err
|
||
}
|
||
enabled = v == "true"
|
||
reason, err = s.get("maintenanceReason")
|
||
return enabled, reason, err
|
||
}
|
||
|
||
func (s *SiteStore) SetMaintenance(enabled bool, reason string) error {
|
||
v := "false"
|
||
if enabled {
|
||
v = "true"
|
||
}
|
||
if err := s.set("maintenance", v); err != nil {
|
||
return err
|
||
}
|
||
return s.set("maintenanceReason", reason)
|
||
}
|
||
|
||
// RecordVisit 递增访问计数,返回 (总访问量, 是否计入, 错误)。
|
||
// 去重逻辑由上层 handler 的内存指纹完成,此处无条件累加。
|
||
func (s *SiteStore) RecordVisit(_ string) (int, bool, error) {
|
||
total, err := s.IncrementVisits()
|
||
return total, true, err
|
||
}
|
||
|
||
// SMTPConfig 存储数据库中的发件 SMTP 配置。
|
||
type SMTPConfig struct {
|
||
Enabled bool `json:"enabled"`
|
||
Email string `json:"email"`
|
||
Password string `json:"password"`
|
||
FromName string `json:"fromName"`
|
||
Host string `json:"host"`
|
||
Port string `json:"port"`
|
||
}
|
||
|
||
// IsConfiguredEmail 判断邮件通知是否已启用且 SMTP 配置完整。
|
||
func (c SMTPConfig) IsConfiguredEmail() bool {
|
||
return c.Enabled && c.Email != "" && c.Password != "" && c.Host != ""
|
||
}
|
||
|
||
func (s *SiteStore) GetSMTPConfig() (SMTPConfig, error) {
|
||
cfg := SMTPConfig{
|
||
Enabled: true, // 默认启用
|
||
Host: "smtp.qq.com",
|
||
Port: "465",
|
||
}
|
||
if v, _ := s.get("smtpEnabled"); v == "false" {
|
||
cfg.Enabled = false
|
||
}
|
||
if v, _ := s.get("smtpEmail"); v != "" {
|
||
cfg.Email = v
|
||
}
|
||
if v, _ := s.get("smtpPassword"); v != "" {
|
||
cfg.Password = v
|
||
}
|
||
if v, _ := s.get("smtpFromName"); v != "" {
|
||
cfg.FromName = v
|
||
}
|
||
if v, _ := s.get("smtpHost"); v != "" {
|
||
cfg.Host = v
|
||
}
|
||
if v, _ := s.get("smtpPort"); v != "" {
|
||
cfg.Port = v
|
||
}
|
||
return cfg, nil
|
||
}
|
||
|
||
func (s *SiteStore) SetSMTPConfig(cfg SMTPConfig) error {
|
||
enabledVal := "true"
|
||
if !cfg.Enabled {
|
||
enabledVal = "false"
|
||
}
|
||
pairs := [][2]string{
|
||
{"smtpEnabled", enabledVal},
|
||
{"smtpEmail", cfg.Email},
|
||
{"smtpPassword", cfg.Password},
|
||
{"smtpFromName", cfg.FromName},
|
||
{"smtpHost", cfg.Host},
|
||
{"smtpPort", cfg.Port},
|
||
}
|
||
for _, p := range pairs {
|
||
if err := s.set(p[0], p[1]); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|