Files
filestoragesystem/go/internal/service/admin.go
T
2026-08-23 00:48:10 +08:00

215 lines
5.7 KiB
Go

package service
import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"
"time"
"gorm.io/gorm"
"filestoragesystem/internal/config"
"filestoragesystem/internal/model"
"filestoragesystem/internal/repository"
"filestoragesystem/internal/utils"
"filestoragesystem/pkg/apperr"
)
// AdminService 系统管理服务(设置/日志/备份)
type AdminService struct {
cfg *config.Config
db *gorm.DB
settingRepo *repository.SettingRepo
opLogRepo *repository.OpLogRepo
sysLogRepo *repository.SysLogRepo
}
// ---- 系统设置 ----
// GetSettings 获取全部设置(按分组组织)
func (s *AdminService) GetSettings() (map[string][]model.SystemSetting, error) {
settings, err := s.settingRepo.GetAll()
if err != nil {
return nil, err
}
groups := make(map[string][]model.SystemSetting)
for _, st := range settings {
groups[st.GroupName] = append(groups[st.GroupName], st)
}
return groups, nil
}
// GetSettingsByGroup 按分组获取设置
func (s *AdminService) GetSettingsByGroup(group string) ([]model.SystemSetting, error) {
return s.settingRepo.GetByGroup(group)
}
// UpdateSettings 批量更新设置(仅更新已存在的键)
func (s *AdminService) UpdateSettings(values map[string]string) error {
if len(values) == 0 {
return nil
}
existing := make(map[string]bool)
all, err := s.settingRepo.GetAll()
if err != nil {
return err
}
for _, st := range all {
existing[st.Key] = true
}
valid := make(map[string]string)
for k, v := range values {
if existing[k] {
valid[k] = v
}
}
if len(valid) == 0 {
return fmt.Errorf("没有可更新的有效设置项")
}
return s.settingRepo.UpdateValues(valid)
}
// ---- 日志 ----
// ListOperationLogs 操作日志列表
func (s *AdminService) ListOperationLogs(f repository.OpLogFilter) ([]model.OperationLog, int64, error) {
if f.Page <= 0 {
f.Page = 1
}
if f.PageSize <= 0 {
f.PageSize = 20
}
return s.opLogRepo.List(f)
}
// GetOperationLog 操作日志详情
func (s *AdminService) GetOperationLog(id uint) (*model.OperationLog, error) {
l, err := s.opLogRepo.FindByID(id)
if err != nil {
return nil, apperr.ErrNotFound
}
return l, nil
}
// ListSystemLogs 系统日志列表
func (s *AdminService) ListSystemLogs(f repository.SysLogFilter) ([]model.SystemLog, int64, error) {
if f.Page <= 0 {
f.Page = 1
}
if f.PageSize <= 0 {
f.PageSize = 20
}
return s.sysLogRepo.List(f)
}
// GetSystemLog 系统日志详情
func (s *AdminService) GetSystemLog(id uint) (*model.SystemLog, error) {
l, err := s.sysLogRepo.FindByID(id)
if err != nil {
return nil, apperr.ErrNotFound
}
return l, nil
}
// CleanupLogs 清理days天前的日志
func (s *AdminService) CleanupLogs(days int) (opDeleted, sysDeleted int64, err error) {
if days <= 0 {
days = 30
}
opDeleted, err = s.opLogRepo.DeleteBefore(days)
if err != nil {
return
}
sysDeleted, err = s.sysLogRepo.DeleteBefore(days)
utils.Info("system", "清理%d天前日志:操作日志%d条,系统日志%d条", days, opDeleted, sysDeleted)
return
}
// ---- 备份 ----
// BackupDatabase 使用VACUUM INTO备份数据库
func (s *AdminService) BackupDatabase() (string, error) {
if err := os.MkdirAll(s.cfg.Storage.Backups, 0o755); err != nil {
return "", err
}
name := fmt.Sprintf("backup-%s.db", time.Now().Format("20060102-150405"))
target := filepath.Join(s.cfg.Storage.Backups, name)
// SQLite安全备份:VACUUM INTO
quoted := strings.ReplaceAll(target, "'", "''")
if err := s.db.Exec(fmt.Sprintf("VACUUM INTO '%s'", quoted)).Error; err != nil {
utils.Error("system", "数据库备份失败: %v", err)
return "", fmt.Errorf("备份失败: %w", err)
}
utils.Info("system", "数据库备份完成: %s", name)
return name, nil
}
// ListBackups 备份列表
func (s *AdminService) ListBackups() ([]map[string]interface{}, error) {
entries, err := os.ReadDir(s.cfg.Storage.Backups)
if err != nil {
return []map[string]interface{}{}, nil
}
var list []map[string]interface{}
for _, e := range entries {
if e.IsDir() || !strings.HasSuffix(e.Name(), ".db") {
continue
}
info, _ := e.Info()
list = append(list, map[string]interface{}{
"name": e.Name(),
"size": info.Size(),
"time": info.ModTime().Format(time.RFC3339),
})
}
sort.Slice(list, func(i, j int) bool {
return list[i]["name"].(string) > list[j]["name"].(string)
})
return list, nil
}
// BackupPath 校验备份文件名并返回其物理路径(供下载)
func (s *AdminService) BackupPath(name string) (string, error) {
// 只允许 backup-*.db 格式,防止路径穿越
if strings.ContainsAny(name, "/\\") || strings.Contains(name, "..") || !strings.HasPrefix(name, "backup-") || !strings.HasSuffix(name, ".db") {
return "", fmt.Errorf("非法的备份文件名")
}
p := filepath.Join(s.cfg.Storage.Backups, name)
if _, err := os.Stat(p); err != nil {
return "", fmt.Errorf("备份文件不存在")
}
return p, nil
}
// ---- 站点状态 ----
// SiteStatus 公开的站点状态信息
func (s *AdminService) SiteStatus() map[string]interface{} {
get := func(k, def string) string {
if v, err := s.settingRepo.GetValue(k); err == nil {
return v
}
return def
}
return map[string]interface{}{
"site_name": get("site_name", "云泽文件存储云平台"),
"site_description": get("site_description", ""),
"site_logo": get("site_logo", ""),
"site_icp": get("site_icp", ""),
"site_copyright": get("site_copyright", ""),
"site_enabled": get("site_enabled", "true") == "true",
"register_enabled": get("register_enabled", "true") == "true",
"maintenance_msg": get("site_maintenance_msg", ""),
}
}
// IsSiteEnabled 站点是否开启
func (s *AdminService) IsSiteEnabled() bool {
if v, err := s.settingRepo.GetValue("site_enabled"); err == nil {
return v == "true" || v == "1"
}
return true
}