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 }