更新前后端代码

This commit is contained in:
2026-08-23 00:48:10 +08:00
parent 1b5468995f
commit 5c1313e4f6
74 changed files with 9995 additions and 306 deletions
+354
View File
@@ -0,0 +1,354 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/repository"
"filestoragesystem/internal/utils"
)
// AdminController 系统管理控制器
type AdminController struct {
*Base
}
// NewAdminController 创建系统管理控制器
func NewAdminController(b *Base) *AdminController { return &AdminController{Base: b} }
// ---- 用户管理 ----
// ListUsers 用户列表
func (ctl *AdminController) ListUsers(c *gin.Context) {
page, pageSize := utils.ParsePage(c)
users, total, err := ctl.svc.User.AdminListUsers(
c.Query("keyword"), c.Query("role_id"), c.Query("status"), page, pageSize)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OKPage(c, users, total, page, pageSize)
}
// GetUser 用户详情
func (ctl *AdminController) GetUser(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
u, err := ctl.svc.User.AdminGetUser(id)
if err != nil {
utils.NotFound(c, "用户不存在")
return
}
utils.OK(c, u)
}
type adminCreateUserReq struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
RoleID uint `json:"role_id"`
StorageLimit int64 `json:"storage_limit"`
}
// CreateUser 创建用户
func (ctl *AdminController) CreateUser(c *gin.Context) {
var req adminCreateUserReq
if !bindJSON(c, &req) {
return
}
u, err := ctl.svc.User.AdminCreateUser(req.Username, req.Email, req.Password, req.RoleID, req.StorageLimit)
if err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", 0, req.Username, false, err.Error())
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", u.ID, u.Username, true, "")
utils.OK(c, u)
}
type adminUpdateUserReq struct {
Email string `json:"email"`
RoleID uint `json:"role_id"`
StorageLimit int64 `json:"storage_limit"`
}
// UpdateUser 编辑用户
func (ctl *AdminController) UpdateUser(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req adminUpdateUserReq
if !bindJSON(c, &req) {
return
}
u, err := ctl.svc.User.AdminUpdateUser(id, req.Email, req.RoleID, req.StorageLimit)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, u.Username, true, "")
utils.OK(c, u)
}
// DeleteUser 删除用户
func (ctl *AdminController) DeleteUser(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
if err := ctl.svc.User.AdminDeleteUser(id); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "user", id, "", true, "")
utils.OKMsg(c, "用户已删除")
}
type setUserStatusReq struct {
Status int8 `json:"status"` // 1启用 0禁用(不加required,0为合法值)
}
// SetUserStatus 启用/禁用用户
func (ctl *AdminController) SetUserStatus(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req setUserStatusReq
if !bindJSON(c, &req) {
return
}
if req.Status != 0 && req.Status != 1 {
utils.FailMsg(c, "status仅支持0或1")
return
}
if err := ctl.svc.User.AdminSetUserStatus(id, req.Status); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "status", true, "")
utils.OKMsg(c, "状态已更新")
}
type assignRoleReq struct {
RoleID uint `json:"role_id" binding:"required"`
}
// AssignRole 修改用户角色
func (ctl *AdminController) AssignRole(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req assignRoleReq
if !bindJSON(c, &req) {
return
}
if err := ctl.svc.User.AdminAssignRole(id, req.RoleID); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "role", true, "")
utils.OKMsg(c, "角色已更新")
}
type resetPasswordReq struct {
Password string `json:"password" binding:"required"`
}
// ResetPassword 重置用户密码
func (ctl *AdminController) ResetPassword(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req resetPasswordReq
if !bindJSON(c, &req) {
return
}
if err := ctl.svc.User.AdminResetPassword(id, req.Password); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", id, "password", true, "")
utils.OKMsg(c, "密码已重置")
}
// ---- 系统设置 ----
// GetSettings 获取全部设置
func (ctl *AdminController) GetSettings(c *gin.Context) {
groups, err := ctl.svc.Admin.GetSettings()
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, groups)
}
// GetSettingsByGroup 按分组获取设置
func (ctl *AdminController) GetSettingsByGroup(c *gin.Context) {
group := c.Param("group")
settings, err := ctl.svc.Admin.GetSettingsByGroup(group)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, settings)
}
// UpdateSettings 批量更新设置
func (ctl *AdminController) UpdateSettings(c *gin.Context) {
var req map[string]string
if !bindJSON(c, &req) {
return
}
if err := ctl.svc.Admin.UpdateSettings(req); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "system", 0, "settings", true, "")
utils.OKMsg(c, "设置已更新")
}
// UpdateSettingsByGroup 按分组更新设置
func (ctl *AdminController) UpdateSettingsByGroup(c *gin.Context) {
var req map[string]string
if !bindJSON(c, &req) {
return
}
if err := ctl.svc.Admin.UpdateSettings(req); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "system", 0, "settings:"+c.Param("group"), true, "")
utils.OKMsg(c, "设置已更新")
}
// ---- 日志管理 ----
// ListOperationLogs 操作日志列表
func (ctl *AdminController) ListOperationLogs(c *gin.Context) {
page, pageSize := utils.ParsePage(c)
f := repository.OpLogFilter{
Username: c.Query("username"),
Action: c.Query("action"),
Status: c.Query("status"),
Days: parseIntQuery(c, "days", 0),
Page: page,
PageSize: pageSize,
}
if uid := parseUintQuery(c, "user_id"); uid > 0 {
f.UserID = &uid
}
logs, total, err := ctl.svc.Admin.ListOperationLogs(f)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OKPage(c, logs, total, page, pageSize)
}
// GetOperationLog 操作日志详情
func (ctl *AdminController) GetOperationLog(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
l, err := ctl.svc.Admin.GetOperationLog(id)
if err != nil {
utils.NotFound(c, "日志不存在")
return
}
utils.OK(c, l)
}
// ListSystemLogs 系统日志列表
func (ctl *AdminController) ListSystemLogs(c *gin.Context) {
page, pageSize := utils.ParsePage(c)
f := repository.SysLogFilter{
Level: c.Query("level"),
Module: c.Query("module"),
Days: parseIntQuery(c, "days", 0),
Page: page,
PageSize: pageSize,
}
logs, total, err := ctl.svc.Admin.ListSystemLogs(f)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OKPage(c, logs, total, page, pageSize)
}
// GetSystemLog 系统日志详情
func (ctl *AdminController) GetSystemLog(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
l, err := ctl.svc.Admin.GetSystemLog(id)
if err != nil {
utils.NotFound(c, "日志不存在")
return
}
utils.OK(c, l)
}
// CleanupLogs 清理历史日志
func (ctl *AdminController) CleanupLogs(c *gin.Context) {
days := parseIntQuery(c, "days", 30)
opDeleted, sysDeleted, err := ctl.svc.Admin.CleanupLogs(days)
if err != nil {
utils.ServerError(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "system", 0, "logs", true, "")
utils.OK(c, gin.H{
"days": days,
"operation_logs_deleted": opDeleted,
"system_logs_deleted": sysDeleted,
})
}
// ---- 数据备份 ----
// Backup 手动备份数据库
func (ctl *AdminController) Backup(c *gin.Context) {
name, err := ctl.svc.Admin.BackupDatabase()
if err != nil {
utils.ServerError(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "system", 0, "backup:"+name, true, "")
utils.OK(c, gin.H{"name": name})
}
// ListBackups 备份列表
func (ctl *AdminController) ListBackups(c *gin.Context) {
list, err := ctl.svc.Admin.ListBackups()
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, list)
}
// DownloadBackup 下载备份文件
func (ctl *AdminController) DownloadBackup(c *gin.Context) {
name := c.Query("name")
path, err := ctl.svc.Admin.BackupPath(name)
if err != nil {
utils.NotFound(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "download", "system", 0, "backup:"+name, true, "")
c.FileAttachment(path, name)
}
// SiteStatus 公开站点状态接口
func (ctl *AdminController) SiteStatus(c *gin.Context) {
utils.OK(c, ctl.svc.Admin.SiteStatus())
}
+127
View File
@@ -0,0 +1,127 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/utils"
)
// AuthController 认证控制器
type AuthController struct {
*Base
}
// NewAuthController 创建认证控制器
func NewAuthController(b *Base) *AuthController { return &AuthController{Base: b} }
type registerReq struct {
Username string `json:"username" binding:"required"`
Email string `json:"email" binding:"required"`
Password string `json:"password" binding:"required"`
}
// Register 用户注册
func (ctl *AuthController) Register(c *gin.Context) {
var req registerReq
if !bindJSON(c, &req) {
return
}
u, err := ctl.svc.Auth.Register(req.Username, req.Email, req.Password)
if err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", 0, req.Username, false, err.Error())
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "user", u.ID, u.Username, true, "")
utils.OK(c, gin.H{"id": u.ID, "username": u.Username, "email": u.Email})
}
type loginReq struct {
Account string `json:"account" binding:"required"` // 用户名或邮箱
Password string `json:"password" binding:"required"`
}
// Login 用户登录
func (ctl *AuthController) Login(c *gin.Context) {
var req loginReq
if !bindJSON(c, &req) {
return
}
ip := utils.ClientIP(c)
u, token, err := ctl.svc.Auth.Login(req.Account, req.Password, ip)
if err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "login", "user", 0, req.Account, false, err.Error())
utils.Unauthorized(c, err.Error())
return
}
oc := ctl.opCtx(c)
uid := u.ID
oc.UserID = &uid
oc.Username = u.Username
ctl.svc.RecordOpLog(oc, "login", "user", u.ID, u.Username, true, "")
utils.OK(c, gin.H{
"token": token,
"user": gin.H{
"id": u.ID,
"username": u.Username,
"email": u.Email,
"role": u.Role,
"storage_used": u.StorageUsed,
"storage_limit": u.StorageLimit,
},
})
}
type createAPIKeyReq struct {
Name string `json:"name"`
}
// CreateAPIKey 生成API密钥
func (ctl *AuthController) CreateAPIKey(c *gin.Context) {
var req createAPIKeyReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
key, secret, err := ctl.svc.Auth.CreateAPIKey(userID, req.Name)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "apikey", key.ID, key.Name, true, "")
utils.OK(c, gin.H{
"id": key.ID,
"access_key": key.AccessKey,
"secret_key": secret, // 仅创建时返回一次
"name": key.Name,
"notice": "secret_key仅在创建时展示,请妥善保存",
})
}
// ListAPIKeys 密钥列表
func (ctl *AuthController) ListAPIKeys(c *gin.Context) {
userID := middleware.CurrentUserID(c)
keys, err := ctl.svc.Auth.ListAPIKeys(userID)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, keys)
}
// DeleteAPIKey 删除密钥
func (ctl *AuthController) DeleteAPIKey(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
if err := ctl.svc.Auth.DeleteAPIKey(userID, id); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "apikey", id, "", true, "")
utils.OKMsg(c, "密钥已删除")
}
+71
View File
@@ -0,0 +1,71 @@
package controller
import (
"strconv"
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/service"
"filestoragesystem/internal/utils"
)
// Base 控制器基类
type Base struct {
svc *service.Services
}
// NewBase 创建基类
func NewBase(svc *service.Services) *Base { return &Base{svc: svc} }
// opCtx 从请求上下文提取操作日志上下文
func (b *Base) opCtx(c *gin.Context) *service.OpLogContext {
ctx := &service.OpLogContext{
IP: utils.ClientIP(c),
UserAgent: c.GetHeader("User-Agent"),
Path: c.Request.URL.Path,
Method: c.Request.Method,
Username: c.GetString(middleware.CtxUsername),
}
if uid := middleware.CurrentUserID(c); uid > 0 {
ctx.UserID = &uid
}
return ctx
}
// bindJSON 绑定JSON请求体
func bindJSON(c *gin.Context, obj interface{}) bool {
if err := c.ShouldBindJSON(obj); err != nil {
utils.FailMsg(c, "参数错误: "+err.Error())
return false
}
return true
}
// paramID 解析路径参数ID
func paramID(c *gin.Context) (uint, bool) {
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
if err != nil || id == 0 {
utils.FailMsg(c, "无效的资源ID")
return 0, false
}
return uint(id), true
}
// parseUintQuery 解析整型query参数
func parseUintQuery(c *gin.Context, key string) uint {
v, err := strconv.ParseUint(c.Query(key), 10, 64)
if err != nil {
return 0
}
return uint(v)
}
// parseIntQuery 解析整型query参数(可为负)
func parseIntQuery(c *gin.Context, key string, def int) int {
n, err := strconv.Atoi(c.Query(key))
if err != nil {
return def
}
return n
}
+194
View File
@@ -0,0 +1,194 @@
package controller
import (
"fmt"
"net/http"
"net/url"
"path/filepath"
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/repository"
"filestoragesystem/internal/utils"
)
// FileController 文件控制器
type FileController struct {
*Base
}
// NewFileController 创建文件控制器
func NewFileController(b *Base) *FileController { return &FileController{Base: b} }
// Upload 上传文件
func (ctl *FileController) Upload(c *gin.Context) {
fh, err := c.FormFile("file")
if err != nil {
utils.FailMsg(c, "缺少上传文件字段file")
return
}
userID := middleware.CurrentUserID(c)
f, err := ctl.svc.File.Upload(c, userID, fh)
if err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "upload", "file", 0, fh.Filename, false, err.Error())
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "upload", "file", f.ID, f.Filename, true, "")
utils.OK(c, f)
}
// List 文件列表(支持project_id/filename/prefix/trash过滤)
func (ctl *FileController) List(c *gin.Context) {
userID := middleware.CurrentUserID(c)
page, pageSize := utils.ParsePage(c)
filter := repository.FileListFilter{
ProjectID: parseUintQuery(c, "project_id"),
Filename: c.Query("filename"),
Prefix: utils.SanitizePath(c.Query("prefix")),
Trashed: c.Query("trash") == "true",
Page: page,
PageSize: pageSize,
}
if v := parseUintQuery(c, "visibility"); v > 0 {
filter.Visibility = int8(v)
}
files, total, err := ctl.svc.File.List(userID, middleware.IsAdmin(c), filter)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OKPage(c, files, total, page, pageSize)
}
// GetInfo 获取文件信息
func (ctl *FileController) GetInfo(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
f, err := ctl.svc.File.GetInfo(userID, id, middleware.IsAdmin(c))
if err != nil {
utils.FailMsg(c, err.Error())
return
}
utils.OK(c, f)
}
// Download 下载文件
func (ctl *FileController) Download(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
f, fp, err := ctl.svc.File.Open(c, userID, id, middleware.IsAdmin(c))
if err != nil {
utils.FailMsg(c, err.Error())
return
}
defer fp.Close()
ctl.svc.File.RecordDownload(c, f)
ctl.svc.RecordOpLog(ctl.opCtx(c), "download", "file", f.ID, f.Filename, true, "")
c.Header("Content-Disposition",
fmt.Sprintf(`attachment; filename*=UTF-8''%s`, url.PathEscape(filepath.Base(f.Filename))))
c.Header("Content-Type", f.MimeType)
http.ServeContent(c.Writer, c.Request, filepath.Base(f.Filename), f.UpdatedAt, fp)
}
// Delete 删除文件(permanent=true硬删除,否则进入回收站)
func (ctl *FileController) Delete(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
permanent := c.Query("permanent") == "true"
f, _ := ctl.svc.File.GetInfo(userID, id, middleware.IsAdmin(c))
if err := ctl.svc.File.Delete(userID, id, middleware.IsAdmin(c), permanent); err != nil {
utils.FailMsg(c, err.Error())
return
}
name := ""
if f != nil {
name = f.Filename
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "file", id, name, true, "")
if permanent {
utils.OKMsg(c, "文件已彻底删除")
} else {
utils.OKMsg(c, "文件已移入回收站")
}
}
// Restore 恢复回收站文件
func (ctl *FileController) Restore(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
if err := ctl.svc.File.Restore(userID, id, middleware.IsAdmin(c)); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "file", id, "restore", true, "")
utils.OKMsg(c, "文件已恢复")
}
type tempLinkReq struct {
Password string `json:"password"`
MaxCount int `json:"max_count"`
ExpiresIn int64 `json:"expires_in"` // 秒,0使用默认
}
// CreateTempLink 生成临时访问链接
func (ctl *FileController) CreateTempLink(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req tempLinkReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
link, err := ctl.svc.File.CreateTempLink(userID, id, middleware.IsAdmin(c), req.Password, req.MaxCount, req.ExpiresIn)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "file", id, "temp_link", true, "")
utils.OK(c, gin.H{
"token": link.Token,
"url": "/api/v1/files/temp/" + link.Token,
"expires_at": link.ExpiresAt,
"has_password": link.HasPwd,
"max_count": link.MaxCount,
})
}
// TempAccess 通过临时链接访问文件(公开接口,支持password参数)
func (ctl *FileController) TempAccess(c *gin.Context) {
token := c.Param("token")
password := c.Query("password")
if password == "" {
password = c.GetHeader("X-Link-Password")
}
f, fp, err := ctl.svc.File.ResolveTempLink(token, password)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
defer fp.Close()
ctl.svc.File.RecordDownload(c, f)
c.Header("Content-Disposition",
fmt.Sprintf(`inline; filename*=UTF-8''%s`, url.PathEscape(filepath.Base(f.Filename))))
c.Header("Content-Type", f.MimeType)
http.ServeContent(c.Writer, c.Request, filepath.Base(f.Filename), f.UpdatedAt, fp)
}
+113
View File
@@ -0,0 +1,113 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/utils"
)
// ProjectController 项目控制器
type ProjectController struct {
*Base
}
// NewProjectController 创建项目控制器
func NewProjectController(b *Base) *ProjectController { return &ProjectController{Base: b} }
type createProjectReq struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
StorageLimit int64 `json:"storage_limit"` // 0使用默认
}
// Create 创建项目
func (ctl *ProjectController) Create(c *gin.Context) {
var req createProjectReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
p, err := ctl.svc.Project.Create(userID, req.Name, req.Description, req.StorageLimit)
if err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "project", 0, req.Name, false, err.Error())
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "project", p.ID, p.Name, true, "")
utils.OK(c, p)
}
// List 项目列表
func (ctl *ProjectController) List(c *gin.Context) {
userID := middleware.CurrentUserID(c)
page, pageSize := utils.ParsePage(c)
all := c.Query("all") == "true" && middleware.IsAdmin(c)
projects, total, err := ctl.svc.Project.List(userID, c.Query("keyword"), all, page, pageSize)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OKPage(c, projects, total, page, pageSize)
}
// Get 项目详情
func (ctl *ProjectController) Get(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
p, err := ctl.svc.Project.Get(userID, id, middleware.IsAdmin(c))
if err != nil {
utils.FailMsg(c, err.Error())
return
}
utils.OK(c, p)
}
type updateProjectReq struct {
Name string `json:"name"`
Description string `json:"description"`
StorageLimit int64 `json:"storage_limit"`
}
// Update 更新项目
func (ctl *ProjectController) Update(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req updateProjectReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
p, err := ctl.svc.Project.Update(userID, id, middleware.IsAdmin(c), req.Name, req.Description, req.StorageLimit)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "project", id, p.Name, true, "")
utils.OK(c, p)
}
// Delete 删除项目
func (ctl *ProjectController) Delete(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
p, _ := ctl.svc.Project.Get(userID, id, middleware.IsAdmin(c))
if err := ctl.svc.Project.Delete(userID, id, middleware.IsAdmin(c)); err != nil {
utils.FailMsg(c, err.Error())
return
}
name := ""
if p != nil {
name = p.Name
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "project", id, name, true, "")
utils.OKMsg(c, "项目已删除")
}
+136
View File
@@ -0,0 +1,136 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/utils"
)
// RoleController 角色权限控制器
type RoleController struct {
*Base
}
// NewRoleController 创建角色控制器
func NewRoleController(b *Base) *RoleController { return &RoleController{Base: b} }
// ListRoles 角色列表
func (ctl *RoleController) ListRoles(c *gin.Context) {
roles, err := ctl.svc.Role.ListRoles()
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, roles)
}
// GetRole 角色详情
func (ctl *RoleController) GetRole(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
role, err := ctl.svc.Role.GetRole(id)
if err != nil {
utils.NotFound(c, "角色不存在")
return
}
utils.OK(c, role)
}
type createRoleReq struct {
Name string `json:"name" binding:"required"`
Code string `json:"code" binding:"required"`
Description string `json:"description"`
Permissions []uint `json:"permissions"`
}
// CreateRole 创建角色
func (ctl *RoleController) CreateRole(c *gin.Context) {
var req createRoleReq
if !bindJSON(c, &req) {
return
}
role, err := ctl.svc.Role.CreateRole(req.Name, req.Code, req.Description, req.Permissions)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "role", role.ID, role.Name, true, "")
utils.OK(c, role)
}
type updateRoleReq struct {
Name string `json:"name"`
Description string `json:"description"`
Permissions []uint `json:"permissions"`
}
// UpdateRole 编辑角色
func (ctl *RoleController) UpdateRole(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req updateRoleReq
if !bindJSON(c, &req) {
return
}
role, err := ctl.svc.Role.UpdateRole(id, req.Name, req.Description, req.Permissions)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "role", id, role.Name, true, "")
utils.OK(c, role)
}
// DeleteRole 删除角色
func (ctl *RoleController) DeleteRole(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
if err := ctl.svc.Role.DeleteRole(id); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "role", id, "", true, "")
utils.OKMsg(c, "角色已删除")
}
type setPermissionsReq struct {
Permissions []uint `json:"permissions"`
}
// SetPermissions 配置角色权限
func (ctl *RoleController) SetPermissions(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req setPermissionsReq
if !bindJSON(c, &req) {
return
}
if err := ctl.svc.Role.SetPermissions(id, req.Permissions); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "role", id, "permissions", true, "")
utils.OKMsg(c, "角色权限已更新")
}
// ListPermissions 权限列表(按模块分组)
func (ctl *RoleController) ListPermissions(c *gin.Context) {
groups, order, err := ctl.svc.Role.PermissionsGrouped()
if err != nil {
utils.ServerError(c, err.Error())
return
}
result := make([]gin.H, 0, len(order))
for _, module := range order {
result = append(result, gin.H{"module": module, "permissions": groups[module]})
}
utils.OK(c, result)
}
+54
View File
@@ -0,0 +1,54 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/utils"
)
// StatsController 统计控制器
type StatsController struct {
*Base
}
// NewStatsController 创建统计控制器
func NewStatsController(b *Base) *StatsController { return &StatsController{Base: b} }
// UserStats 用户存储统计
func (ctl *StatsController) UserStats(c *gin.Context) {
userID := middleware.CurrentUserID(c)
stats, err := ctl.svc.Stats.UserStats(userID)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, stats)
}
// ProjectStats 项目存储统计
func (ctl *StatsController) ProjectStats(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
stats, err := ctl.svc.Stats.ProjectStats(userID, id, middleware.IsAdmin(c))
if err != nil {
utils.FailMsg(c, err.Error())
return
}
utils.OK(c, stats)
}
// TrafficStats 流量统计
func (ctl *StatsController) TrafficStats(c *gin.Context) {
userID := middleware.CurrentUserID(c)
days := parseIntQuery(c, "days", 30)
stats, err := ctl.svc.Stats.TrafficStats(userID, days)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, stats)
}
+67
View File
@@ -0,0 +1,67 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/utils"
)
// UserController 用户管理控制器(个人资料)
type UserController struct {
*Base
}
// NewUserController 创建用户控制器
func NewUserController(b *Base) *UserController { return &UserController{Base: b} }
// GetProfile 获取个人资料
func (ctl *UserController) GetProfile(c *gin.Context) {
userID := middleware.CurrentUserID(c)
u, err := ctl.svc.User.GetProfile(userID)
if err != nil {
utils.NotFound(c, "用户不存在")
return
}
utils.OK(c, u)
}
type updateProfileReq struct {
Email string `json:"email"`
}
// UpdateProfile 更新个人资料
func (ctl *UserController) UpdateProfile(c *gin.Context) {
var req updateProfileReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
if err := ctl.svc.User.UpdateProfile(userID, req.Email); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "", true, "")
utils.OKMsg(c, "资料已更新")
}
type changePasswordReq struct {
OldPassword string `json:"old_password" binding:"required"`
NewPassword string `json:"new_password" binding:"required"`
}
// ChangePassword 修改密码
func (ctl *UserController) ChangePassword(c *gin.Context) {
var req changePasswordReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
if err := ctl.svc.User.ChangePassword(userID, req.OldPassword, req.NewPassword); err != nil {
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "password", false, err.Error())
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "user", userID, "password", true, "")
utils.OKMsg(c, "密码已修改")
}
+92
View File
@@ -0,0 +1,92 @@
package controller
import (
"github.com/gin-gonic/gin"
"filestoragesystem/internal/middleware"
"filestoragesystem/internal/utils"
)
// WebhookController Webhook控制器
type WebhookController struct {
*Base
}
// NewWebhookController 创建Webhook控制器
func NewWebhookController(b *Base) *WebhookController { return &WebhookController{Base: b} }
type createWebhookReq struct {
URL string `json:"url" binding:"required"`
ProjectID *uint `json:"project_id"`
Secret string `json:"secret"`
Events string `json:"events" binding:"required"` // 如 file.upload,file.delete
Status int8 `json:"status"`
}
// Create 创建webhook
func (ctl *WebhookController) Create(c *gin.Context) {
var req createWebhookReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
w, err := ctl.svc.Webhook.Create(userID, req.ProjectID, req.URL, req.Secret, req.Events, req.Status)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "create", "webhook", w.ID, w.URL, true, "")
utils.OK(c, w)
}
// List webhook列表
func (ctl *WebhookController) List(c *gin.Context) {
userID := middleware.CurrentUserID(c)
hooks, err := ctl.svc.Webhook.List(userID)
if err != nil {
utils.ServerError(c, err.Error())
return
}
utils.OK(c, hooks)
}
type updateWebhookReq struct {
URL string `json:"url"`
Events string `json:"events"`
Status int8 `json:"status"`
}
// Update 更新webhook
func (ctl *WebhookController) Update(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
var req updateWebhookReq
if !bindJSON(c, &req) {
return
}
userID := middleware.CurrentUserID(c)
w, err := ctl.svc.Webhook.Update(userID, id, req.URL, req.Events, req.Status)
if err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "update", "webhook", id, w.URL, true, "")
utils.OK(c, w)
}
// Delete 删除webhook
func (ctl *WebhookController) Delete(c *gin.Context) {
id, ok := paramID(c)
if !ok {
return
}
userID := middleware.CurrentUserID(c)
if err := ctl.svc.Webhook.Delete(userID, id); err != nil {
utils.FailMsg(c, err.Error())
return
}
ctl.svc.RecordOpLog(ctl.opCtx(c), "delete", "webhook", id, "", true, "")
utils.OKMsg(c, "webhook已删除")
}