初始化

This commit is contained in:
2026-07-03 15:58:29 +08:00
parent 3a942c882e
commit dc90e889e1
77 changed files with 12912 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
package controller
import (
"strconv"
"seeyon-filesystem/dto"
"seeyon-filesystem/model"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// AdminController 管理后台控制器
type AdminController struct {
adminService *service.AdminService
storageService *service.StorageTestService
}
func NewAdminController() *AdminController {
return &AdminController{
adminService: service.NewAdminService(),
storageService: service.NewStorageTestService(),
}
}
// ========== 用户管理 ==========
// ListUsers 用户列表
// GET /api/admin/users?page=1&size=20
func (c *AdminController) ListUsers(ctx *gin.Context) {
page, _ := strconv.Atoi(ctx.DefaultQuery("page", "1"))
size, _ := strconv.Atoi(ctx.DefaultQuery("size", "20"))
if page < 1 { page = 1 }
if size < 1 || size > 100 { size = 20 }
users, total, err := c.adminService.ListUsers(page, size)
if err != nil {
utils.ServerError(ctx, "查询用户失败")
return
}
utils.PageSuccess(ctx, users, total, page, size)
}
// CreateUser 创建用户
// POST /api/admin/users
func (c *AdminController) CreateUser(ctx *gin.Context) {
var req dto.UserCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.CreateUser(&req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "用户创建成功", nil)
}
// UpdateUser 更新用户
// PUT /api/admin/users/:id
func (c *AdminController) UpdateUser(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的用户ID")
return
}
var req dto.UserUpdateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.UpdateUser(id, &req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "更新成功", nil)
}
// DeleteUser 删除用户
// DELETE /api/admin/users/:id
func (c *AdminController) DeleteUser(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的用户ID")
return
}
if err := c.adminService.DeleteUser(id); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}
// ResetPassword 重置密码
// PUT /api/admin/users/:id/password
func (c *AdminController) ResetPassword(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的用户ID")
return
}
var req struct {
Password string `json:"password" binding:"required,min=6"`
}
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.ResetPassword(id, req.Password); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "密码重置成功", nil)
}
// ========== 存储策略管理 ==========
// ListStoragePolicies 存储策略列表
// GET /api/admin/storage-policies
func (c *AdminController) ListStoragePolicies(ctx *gin.Context) {
policies, err := c.adminService.ListStoragePolicies()
if err != nil {
utils.ServerError(ctx, "查询失败")
return
}
utils.Success(ctx, policies)
}
// CreateStoragePolicy 创建存储策略
// POST /api/admin/storage-policies
func (c *AdminController) CreateStoragePolicy(ctx *gin.Context) {
var req dto.StoragePolicyCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.CreateStoragePolicy(&req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "创建成功", nil)
}
// UpdateStoragePolicy 更新存储策略
// PUT /api/admin/storage-policies/:id
func (c *AdminController) UpdateStoragePolicy(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的策略ID")
return
}
var req dto.StoragePolicyUpdateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.UpdateStoragePolicy(id, &req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "更新成功", nil)
}
// DeleteStoragePolicy 删除存储策略
// DELETE /api/admin/storage-policies/:id
func (c *AdminController) DeleteStoragePolicy(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的策略ID")
return
}
if err := c.adminService.DeleteStoragePolicy(id); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}
// TestStoragePolicy 测试存储策略连通性
// POST /api/admin/storage-policies/:id/test
func (c *AdminController) TestStoragePolicy(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的策略ID")
return
}
result, err := c.storageService.TestPolicy(id)
if err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// TestNewStoragePolicy 测试新配置(未保存)
// POST /api/admin/storage-policies/test
func (c *AdminController) TestNewStoragePolicy(ctx *gin.Context) {
var req struct {
Type string `json:"type" binding:"required"`
Config dto.PolicyConfigDTO `json:"config" binding:"required"`
}
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
cfg := model.PolicyConfig{
BasePath: req.Config.BasePath,
Endpoint: req.Config.Endpoint,
Bucket: req.Config.Bucket,
AccessKey: req.Config.AccessKey,
SecretKey: req.Config.SecretKey,
Region: req.Config.Region,
UseSSL: req.Config.UseSSL,
}
result := c.storageService.TestNewPolicy(req.Type, cfg)
utils.Success(ctx, result)
}
// ========== 组织架构管理 ==========
// ListDepartments 部门列表(树形)
// GET /api/admin/departments
func (c *AdminController) ListDepartments(ctx *gin.Context) {
depts, err := c.adminService.ListDepartments()
if err != nil {
utils.ServerError(ctx, "查询失败")
return
}
utils.Success(ctx, depts)
}
// CreateDepartment 创建部门
// POST /api/admin/departments
func (c *AdminController) CreateDepartment(ctx *gin.Context) {
var req dto.DeptCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.CreateDepartment(&req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "创建成功", nil)
}
// UpdateDepartment 更新部门
// PUT /api/admin/departments/:id
func (c *AdminController) UpdateDepartment(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的部门ID")
return
}
var req dto.DeptUpdateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.adminService.UpdateDepartment(id, &req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "更新成功", nil)
}
// DeleteDepartment 删除部门
// DELETE /api/admin/departments/:id
func (c *AdminController) DeleteDepartment(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的部门ID")
return
}
if err := c.adminService.DeleteDepartment(id); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}

View File

@@ -0,0 +1,100 @@
// Package controller 提供HTTP请求处理层
// 负责参数绑定、校验, 调用Service层, 返回响应
package controller
import (
"seeyon-filesystem/dto"
"seeyon-filesystem/middleware"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// AuthController 认证控制器
type AuthController struct {
authService *service.AuthService
}
// NewAuthController 创建认证控制器实例
func NewAuthController() *AuthController {
return &AuthController{
authService: service.NewAuthService(),
}
}
// Login 用户登录
// POST /api/auth/login
func (c *AuthController) Login(ctx *gin.Context) {
var req dto.LoginRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
resp, err := c.authService.Login(&req)
if err != nil {
utils.Unauthorized(ctx, err.Error())
return
}
utils.Success(ctx, resp)
}
// Register 用户注册
// POST /api/auth/register
func (c *AuthController) Register(ctx *gin.Context) {
var req dto.RegisterRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.authService.Register(&req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "注册成功", nil)
}
// GetProfile 获取当前用户信息
// GET /api/auth/profile
func (c *AuthController) GetProfile(ctx *gin.Context) {
userID := middleware.GetCurrentUserID(ctx)
if userID == 0 {
utils.Unauthorized(ctx, "未登录")
return
}
role, _ := ctx.Get("role")
utils.Success(ctx, gin.H{
"user_id": userID,
"username": middleware.GetCurrentUsername(ctx),
"role": role,
})
}
// ChangePassword 修改密码
// PUT /api/auth/password
func (c *AuthController) ChangePassword(ctx *gin.Context) {
userID := middleware.GetCurrentUserID(ctx)
if userID == 0 {
utils.Unauthorized(ctx, "未登录")
return
}
var req dto.ChangePasswordRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.authService.ChangePassword(userID, &req); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "密码修改成功", nil)
}

View File

@@ -0,0 +1,356 @@
package controller
import (
"net/http"
"strconv"
"strings"
"seeyon-filesystem/dto"
"seeyon-filesystem/middleware"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// FileController 文件控制器
type FileController struct {
fileService *service.FileService
}
// NewFileController 创建文件控制器实例
func NewFileController() *FileController {
return &FileController{
fileService: service.NewFileService(),
}
}
// Upload 上传文件
// POST /api/file/upload?folder_id=1&storage_policy_id=1
func (c *FileController) Upload(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
// 获取文件
fileHeader, err := ctx.FormFile("file")
if err != nil {
utils.BadRequest(ctx, "请选择要上传的文件")
return
}
// 获取目标文件夹ID
folderIDStr := ctx.DefaultQuery("folder_id", "0")
folderID, _ := strconv.ParseUint(folderIDStr, 10, 64)
// 获取存储策略ID(可选, 0=使用默认策略)
policyIDStr := ctx.DefaultQuery("storage_policy_id", "0")
storagePolicyID, _ := strconv.ParseUint(policyIDStr, 10, 64)
// 调用服务层
result, err := c.fileService.Upload(fileHeader, folderID, ownerID, storagePolicyID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// GetByID 获取文件信息
// GET /api/file/:id
func (c *FileController) GetByID(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
result, err := c.fileService.GetByID(id, ownerID)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// Download 下载文件
// GET /api/file/:id/download
func (c *FileController) Download(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
reader, file, err := c.fileService.Download(id, ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
defer reader.Close()
// 设置下载响应头
ctx.Header("Content-Disposition", "attachment; filename=\""+file.Name+"\"")
ctx.Header("Content-Type", file.MimeType)
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
}
// GetDownloadURL 获取文件下载链接
// GET /api/file/:id/download-url
func (c *FileController) GetDownloadURL(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
downloadURL, err := c.fileService.GetDownloadURL(id)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
utils.Success(ctx, gin.H{"download_url": downloadURL})
}
// GetPreviewURL 获取文件预览链接
// GET /api/file/:id/preview-url
func (c *FileController) GetPreviewURL(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
previewURL, err := c.fileService.GetPreviewURL(id)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
utils.Success(ctx, gin.H{"preview_url": previewURL})
}
// Preview 文件预览(直接返回文件流, 浏览器内联展示)
// GET /api/file/:id/preview
func (c *FileController) Preview(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
reader, file, err := c.fileService.Download(id, ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
defer reader.Close()
// 内联展示而非下载
ctx.Header("Content-Disposition", "inline; filename=\""+file.Name+"\"")
ctx.Header("Content-Type", file.MimeType)
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
}
// GetByKey 根据fileKey查询文件信息
// GET /api/file/info?key=xxx
func (c *FileController) GetByKey(ctx *gin.Context) {
key := ctx.Query("key")
if key == "" {
utils.BadRequest(ctx, "请提供fileKey参数")
return
}
result, err := c.fileService.GetByKey(key)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// Rename 重命名文件
// PUT /api/file/:id/rename
func (c *FileController) Rename(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
var req struct {
NewName string `json:"new_name" binding:"required"`
Version int `json:"version"` // 乐观锁版本号
}
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.fileService.Rename(id, req.NewName, ownerID, req.Version); err != nil {
if strings.Contains(err.Error(), "已被其他人修改") {
utils.Error(ctx, 409, 409, err.Error())
return
}
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "重命名成功", nil)
}
// Move 移动文件
// POST /api/file/:id/move
func (c *FileController) Move(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
var req struct {
TargetFolderID uint64 `json:"target_folder_id" binding:"required"`
}
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.fileService.Move(id, req.TargetFolderID, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "移动成功", nil)
}
// SoftDelete 移入回收站
// DELETE /api/file/:id
func (c *FileController) SoftDelete(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
if err := c.fileService.SoftDelete(id, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "已移入回收站", nil)
}
// Restore 从回收站恢复
// POST /api/file/:id/restore
func (c *FileController) Restore(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
if err := c.fileService.Restore(id, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "已恢复", nil)
}
// HardDelete 永久删除
// DELETE /api/file/:id/permanent
func (c *FileController) HardDelete(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件ID")
return
}
if err := c.fileService.HardDelete(id, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "已永久删除", nil)
}
// Search 搜索文件
// GET /api/file/search?keyword=xxx
func (c *FileController) Search(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
keyword := ctx.Query("keyword")
typeFilter := ctx.Query("type") // 可选: image/video/document/audio/archive/code
if keyword == "" && typeFilter == "" {
utils.BadRequest(ctx, "请输入搜索关键词或指定类型")
return
}
var results []dto.FileVO
var err error
if typeFilter != "" {
// 按类型搜索
exts := getExtensionsByType(typeFilter)
results, err = c.fileService.SearchByType(ownerID, exts)
} else {
// 按关键词搜索(支持|分隔多关键词)
results, err = c.fileService.Search(ownerID, keyword)
}
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, results)
}
// getExtensionsByType 根据类型获取扩展名列表
func getExtensionsByType(fileType string) []string {
typeMap := map[string][]string{
"image": {"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "ico"},
"video": {"mp4", "avi", "mov", "wmv", "flv", "mkv", "webm"},
"audio": {"mp3", "wav", "ogg", "aac", "flac", "wma"},
"document": {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "md", "csv"},
"archive": {"zip", "rar", "7z", "tar", "gz", "bz2"},
"code": {"js", "ts", "py", "go", "java", "c", "cpp", "h", "css", "html", "sql", "json", "xml", "yaml", "yml"},
}
if exts, ok := typeMap[fileType]; ok {
return exts
}
return []string{fileType}
}
// ListTrashed 查询回收站
// GET /api/file/trash
func (c *FileController) ListTrashed(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
results, err := c.fileService.ListTrashed(ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, results)
}

View File

@@ -0,0 +1,120 @@
package controller
import (
"strconv"
"strings"
"seeyon-filesystem/dto"
"seeyon-filesystem/middleware"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// FolderController 文件夹控制器
type FolderController struct {
folderService *service.FolderService
}
// NewFolderController 创建文件夹控制器实例
func NewFolderController() *FolderController {
return &FolderController{
folderService: service.NewFolderService(),
}
}
// Create 创建文件夹
// POST /api/folder
func (c *FolderController) Create(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
var req dto.CreateFolderRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
result, err := c.folderService.Create(&req, ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// ListChildren 获取文件夹子项
// GET /api/folder/:id/children 或 GET /api/folder/root
func (c *FolderController) ListChildren(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
idStr := ctx.Param("id")
var folderID uint64
if idStr == "root" {
folderID = 0
} else {
var err error
folderID, err = strconv.ParseUint(idStr, 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件夹ID")
return
}
}
result, err := c.folderService.ListChildren(folderID, ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// Rename 重命名文件夹
// PUT /api/folder/:id/rename
func (c *FolderController) Rename(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件夹ID")
return
}
var req dto.RenameRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.folderService.Rename(id, req.NewName, ownerID, req.Version); err != nil {
if strings.Contains(err.Error(), "已被其他人修改") {
utils.Error(ctx, 409, 409, err.Error())
return
}
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "重命名成功", nil)
}
// Delete 删除文件夹
// DELETE /api/folder/:id
func (c *FolderController) Delete(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的文件夹ID")
return
}
if err := c.folderService.Delete(id, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "已移入回收站", nil)
}

View File

@@ -0,0 +1,285 @@
package controller
import (
"strconv"
"seeyon-filesystem/dto"
"seeyon-filesystem/middleware"
"seeyon-filesystem/model"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// PermissionController 权限管理控制器
type PermissionController struct {
permService *service.PermissionService
}
func NewPermissionController() *PermissionController {
return &PermissionController{
permService: service.NewPermissionService(),
}
}
// ========== 角色管理 ==========
// ListRoles 获取所有角色
// GET /api/admin/roles
func (c *PermissionController) ListRoles(ctx *gin.Context) {
roles, err := c.permService.ListRoles()
if err != nil {
utils.ServerError(ctx, "获取角色列表失败")
return
}
vos := make([]dto.RoleVO, 0, len(roles))
for _, r := range roles {
perms := make([]dto.PermissionVO, 0, len(r.Permissions))
for _, p := range r.Permissions {
perms = append(perms, dto.PermissionVO{
ID: p.ID,
Code: p.Code,
Name: p.Name,
Resource: p.Resource,
Action: p.Action,
Description: p.Description,
})
}
vos = append(vos, dto.RoleVO{
ID: r.ID,
Name: r.Name,
DisplayName: r.DisplayName,
Description: r.Description,
IsSystem: r.IsSystem,
Status: r.Status,
Permissions: perms,
})
}
utils.Success(ctx, vos)
}
// CreateRole 创建角色
// POST /api/admin/roles
func (c *PermissionController) CreateRole(ctx *gin.Context) {
var req dto.RoleCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
role := &model.Role{
Name: req.Name,
DisplayName: req.DisplayName,
Description: req.Description,
}
if err := c.permService.CreateRole(role); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "角色创建成功", nil)
}
// UpdateRole 更新角色
// PUT /api/admin/roles/:id
func (c *PermissionController) UpdateRole(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的角色ID")
return
}
var req dto.RoleUpdateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
role := &model.Role{
ID: id,
DisplayName: req.DisplayName,
Description: req.Description,
}
if err := c.permService.UpdateRole(role); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "更新成功", nil)
}
// DeleteRole 删除角色
// DELETE /api/admin/roles/:id
func (c *PermissionController) DeleteRole(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的角色ID")
return
}
if err := c.permService.DeleteRole(id); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}
// SetRolePermissions 设置角色权限
// PUT /api/admin/roles/:id/permissions
func (c *PermissionController) SetRolePermissions(ctx *gin.Context) {
roleID, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的角色ID")
return
}
var req dto.SetRolePermissionsRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.permService.SetRolePermissions(roleID, req.PermissionIDs); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "权限设置成功", nil)
}
// ========== 权限管理 ==========
// ListPermissions 获取所有权限
// GET /api/admin/permissions
func (c *PermissionController) ListPermissions(ctx *gin.Context) {
perms, err := c.permService.ListPermissions()
if err != nil {
utils.ServerError(ctx, "获取权限列表失败")
return
}
vos := make([]dto.PermissionVO, 0, len(perms))
for _, p := range perms {
vos = append(vos, dto.PermissionVO{
ID: p.ID,
Code: p.Code,
Name: p.Name,
Resource: p.Resource,
Action: p.Action,
Description: p.Description,
})
}
utils.Success(ctx, vos)
}
// CreatePermission 创建权限
// POST /api/admin/permissions
func (c *PermissionController) CreatePermission(ctx *gin.Context) {
var req dto.PermissionCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
perm := &model.Permission{
Code: req.Code,
Name: req.Name,
Resource: req.Resource,
Action: req.Action,
Description: req.Description,
}
if err := c.permService.CreatePermission(perm); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "权限创建成功", nil)
}
// DeletePermission 删除权限
// DELETE /api/admin/permissions/:id
func (c *PermissionController) DeletePermission(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的权限ID")
return
}
if err := c.permService.DeletePermission(id); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}
// ========== 用户角色管理 ==========
// SetUserRoles 设置用户角色
// PUT /api/admin/users/:id/roles
func (c *PermissionController) SetUserRoles(ctx *gin.Context) {
userID, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的用户ID")
return
}
var req dto.SetUserRolesRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
if err := c.permService.SetUserRoles(userID, req.RoleIDs); err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "角色设置成功", nil)
}
// GetUserPermissions 获取当前用户的权限信息
// GET /api/auth/permissions
func (c *PermissionController) GetUserPermissions(ctx *gin.Context) {
userID := middleware.GetCurrentUserID(ctx)
if userID == 0 {
utils.Unauthorized(ctx, "未登录")
return
}
// 获取用户角色
roles, err := c.permService.GetUserRoles(userID)
if err != nil {
utils.ServerError(ctx, "获取角色失败")
return
}
// 获取用户权限
permissions, err := c.permService.GetUserPermissions(userID)
if err != nil {
utils.ServerError(ctx, "获取权限失败")
return
}
roleVOs := make([]dto.RoleVO, 0, len(roles))
for _, r := range roles {
roleVOs = append(roleVOs, dto.RoleVO{
ID: r.ID,
Name: r.Name,
DisplayName: r.DisplayName,
})
}
utils.Success(ctx, dto.UserPermissionVO{
Roles: roleVOs,
Permissions: permissions,
})
}

View File

@@ -0,0 +1,201 @@
package controller
import (
"net/http"
"strconv"
"seeyon-filesystem/dto"
"seeyon-filesystem/middleware"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// ShareController 分享控制器
type ShareController struct {
shareService *service.ShareService
fileService *service.FileService
}
// NewShareController 创建分享控制器实例
func NewShareController() *ShareController {
return &ShareController{
shareService: service.NewShareService(),
fileService: service.NewFileService(),
}
}
// Create 创建分享
// POST /api/share
func (c *ShareController) Create(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
var req dto.ShareCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
result, err := c.shareService.Create(&req, ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// ListMyShares 查看我的分享
// GET /api/share
func (c *ShareController) ListMyShares(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
results, err := c.shareService.ListByOwner(ownerID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.Success(ctx, results)
}
// Cancel 取消分享
// DELETE /api/share/:id
func (c *ShareController) Cancel(ctx *gin.Context) {
ownerID := middleware.GetCurrentUserID(ctx)
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的分享ID")
return
}
if err := c.shareService.Cancel(id, ownerID); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "已取消分享", nil)
}
// GetShareInfo 获取分享信息(公开)
// GET /api/s/:code
func (c *ShareController) GetShareInfo(ctx *gin.Context) {
code := ctx.Param("code")
fileInfo, share, err := c.shareService.GetByCode(code)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
utils.Success(ctx, gin.H{
"file": fileInfo,
"has_password": share.Password != "",
"expire_at": share.ExpireAt,
})
}
// VerifySharePassword 验证分享密码
// POST /api/s/:code/verify
func (c *ShareController) VerifySharePassword(ctx *gin.Context) {
code := ctx.Param("code")
var req dto.ShareVerifyRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "请输入密码")
return
}
_, share, err := c.shareService.GetByCode(code)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
if !c.shareService.VerifyPassword(share.ID, req.Password) {
utils.Unauthorized(ctx, "密码错误")
return
}
utils.Success(ctx, gin.H{"verified": true})
}
// DownloadShareFile 下载分享文件
// GET /api/s/:code/download
func (c *ShareController) DownloadShareFile(ctx *gin.Context) {
code := ctx.Param("code")
_, share, err := c.shareService.GetByCode(code)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
// 检查下载权限
if share.AllowDownload != 1 {
utils.Forbidden(ctx, "此分享不允许下载")
return
}
// 验证密码
if share.Password != "" {
password := ctx.Query("password")
if !c.shareService.VerifyPassword(share.ID, password) {
utils.Unauthorized(ctx, "需要密码")
return
}
}
reader, file, err := c.fileService.DownloadByFileID(share.FileID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
defer reader.Close()
ctx.Header("Content-Disposition", "attachment; filename=\""+file.Name+"\"")
ctx.Header("Content-Type", file.MimeType)
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
}
// PreviewShareFile 预览分享文件(内联展示)
// GET /api/s/:code/preview
func (c *ShareController) PreviewShareFile(ctx *gin.Context) {
code := ctx.Param("code")
_, share, err := c.shareService.GetByCode(code)
if err != nil {
utils.NotFound(ctx, err.Error())
return
}
// 检查预览权限
if share.AllowPreview != 1 {
utils.Forbidden(ctx, "此分享不允许预览")
return
}
// 验证密码
if share.Password != "" {
password := ctx.Query("password")
if !c.shareService.VerifyPassword(share.ID, password) {
utils.Unauthorized(ctx, "需要密码")
return
}
}
reader, file, err := c.fileService.DownloadByFileID(share.FileID)
if err != nil {
utils.ServerError(ctx, err.Error())
return
}
defer reader.Close()
// 内联展示
ctx.Header("Content-Disposition", "inline; filename=\""+file.Name+"\"")
ctx.Header("Content-Type", file.MimeType)
ctx.Header("Content-Length", strconv.FormatInt(file.Size, 10))
ctx.DataFromReader(http.StatusOK, file.Size, file.MimeType, reader, nil)
}

View File

@@ -0,0 +1,297 @@
package controller
import (
"crypto/rand"
"encoding/hex"
"fmt"
"net/http"
"strconv"
"seeyon-filesystem/dto"
"seeyon-filesystem/model"
"seeyon-filesystem/service"
"seeyon-filesystem/utils"
"github.com/gin-gonic/gin"
)
// SSOController SSO控制器
type SSOController struct {
ssoService *service.SSOService
}
func NewSSOController() *SSOController {
return &SSOController{
ssoService: service.NewSSOService(),
}
}
// ========== 公开接口 (无需登录) ==========
// GetProviders 获取启用的SSO提供商列表(登录页展示)
// GET /api/sso/providers
func (c *SSOController) GetProviders(ctx *gin.Context) {
providers, err := c.ssoService.ListActiveProviders()
if err != nil {
utils.ServerError(ctx, "获取SSO提供商失败")
return
}
vos := make([]dto.SSOProviderVO, 0, len(providers))
for _, p := range providers {
vos = append(vos, dto.SSOProviderVO{
ID: p.ID,
Name: p.Name,
DisplayName: p.DisplayName,
Icon: p.Icon,
Type: p.Type,
})
}
utils.Success(ctx, vos)
}
// GetAuthURL 获取SSO授权跳转URL
// GET /api/sso/auth/url?provider_id=1
func (c *SSOController) GetAuthURL(ctx *gin.Context) {
var req dto.SSOAuthURLRequest
if err := ctx.ShouldBindQuery(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
state, _ := generateState()
authURL, err := c.ssoService.GetAuthURL(req.ProviderID, state)
if err != nil {
utils.BadRequest(ctx, err.Error())
return
}
utils.Success(ctx, gin.H{
"auth_url": authURL,
"state": state,
})
}
// Callback 处理SSO回调(重定向到前端)
// GET /api/sso/callback?provider_id=1&code=xxx&state=xxx
func (c *SSOController) Callback(ctx *gin.Context) {
var req dto.SSOCallbackRequest
if err := ctx.ShouldBindQuery(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
result, err := c.ssoService.HandleCallback(req.ProviderID, req.Code)
if err != nil {
// 回调失败, 重定向到登录页并携带错误信息
ctx.Redirect(http.StatusFound, "/login?sso_error="+err.Error())
return
}
ctx.Redirect(http.StatusFound, fmt.Sprintf("/login?sso=success&token=%s&username=%s", result.Token, result.Username))
}
// CallbackJSON 处理SSO回调(JSON版本, 供前端AJAX调用)
// POST /api/sso/callback
func (c *SSOController) CallbackJSON(ctx *gin.Context) {
var req dto.SSOCallbackRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
result, err := c.ssoService.HandleCallback(req.ProviderID, req.Code)
if err != nil {
utils.Unauthorized(ctx, err.Error())
return
}
utils.Success(ctx, result)
}
// ========== 管理员接口 (需要登录 + admin权限) ==========
// AdminListProviders 管理员获取所有SSO提供商
// GET /api/admin/sso/providers
func (c *SSOController) AdminListProviders(ctx *gin.Context) {
providers, err := c.ssoService.ListProviders()
if err != nil {
utils.ServerError(ctx, "获取提供商列表失败")
return
}
utils.Success(ctx, providers)
}
// AdminGetProvider 获取单个SSO提供商详情
// GET /api/admin/sso/providers/:id
func (c *SSOController) AdminGetProvider(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的提供商ID")
return
}
providers, _ := c.ssoService.ListProviders()
for _, p := range providers {
if p.ID == id {
utils.Success(ctx, p)
return
}
}
utils.NotFound(ctx, "提供商不存在")
}
// AdminCreateProvider 管理员创建SSO提供商
// POST /api/admin/sso/providers
func (c *SSOController) AdminCreateProvider(ctx *gin.Context) {
var req dto.SSOProviderCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
provider := &model.SSOProvider{
Name: req.Name,
Type: req.Type,
DisplayName: req.DisplayName,
Icon: req.Icon,
AutoCreate: req.AutoCreate,
DefaultRole: req.DefaultRole,
Status: 1,
Config: model.SSOConfig{
ClientID: req.Config.ClientID,
ClientSecret: req.Config.ClientSecret,
AuthURL: req.Config.AuthURL,
TokenURL: req.Config.TokenURL,
UserInfoURL: req.Config.UserInfoURL,
RedirectURI: req.Config.RedirectURI,
Scopes: req.Config.Scopes,
CasServerURL: req.Config.CasServerURL,
LDAPServer: req.Config.LDAPServer,
LDAPPort: req.Config.LDAPPort,
LDAPBaseDN: req.Config.LDAPBaseDN,
LDAPBindUser: req.Config.LDAPBindUser,
LDAPBindPass: req.Config.LDAPBindPass,
LDAPFilter: req.Config.LDAPFilter,
MappingUsername: req.Config.MappingUsername,
MappingEmail: req.Config.MappingEmail,
MappingNickname: req.Config.MappingNickname,
},
}
if err := c.ssoService.CreateProvider(provider); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "创建成功", nil)
}
// AdminUpdateProvider 管理员更新SSO提供商
// PUT /api/admin/sso/providers/:id
func (c *SSOController) AdminUpdateProvider(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的提供商ID")
return
}
// 获取现有提供商
providers, _ := c.ssoService.ListProviders()
var existing *model.SSOProvider
for i, p := range providers {
if p.ID == id {
existing = &providers[i]
break
}
}
if existing == nil {
utils.NotFound(ctx, "提供商不存在")
return
}
var req dto.SSOProviderCreateRequest
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误: "+err.Error())
return
}
// 更新字段
if req.Name != "" { existing.Name = req.Name }
if req.DisplayName != "" { existing.DisplayName = req.DisplayName }
if req.Icon != "" { existing.Icon = req.Icon }
if req.DefaultRole != "" { existing.DefaultRole = req.DefaultRole }
if req.AutoCreate != 0 { existing.AutoCreate = req.AutoCreate }
// 更新配置
if req.Config.ClientID != "" { existing.Config.ClientID = req.Config.ClientID }
if req.Config.ClientSecret != "" { existing.Config.ClientSecret = req.Config.ClientSecret }
if req.Config.AuthURL != "" { existing.Config.AuthURL = req.Config.AuthURL }
if req.Config.TokenURL != "" { existing.Config.TokenURL = req.Config.TokenURL }
if req.Config.UserInfoURL != "" { existing.Config.UserInfoURL = req.Config.UserInfoURL }
if req.Config.RedirectURI != "" { existing.Config.RedirectURI = req.Config.RedirectURI }
if req.Config.Scopes != "" { existing.Config.Scopes = req.Config.Scopes }
if req.Config.CasServerURL != "" { existing.Config.CasServerURL = req.Config.CasServerURL }
if err := c.ssoService.UpdateProvider(existing); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "更新成功", nil)
}
// AdminDeleteProvider 管理员删除SSO提供商
// DELETE /api/admin/sso/providers/:id
func (c *SSOController) AdminDeleteProvider(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的提供商ID")
return
}
if err := c.ssoService.DeleteProvider(id); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "删除成功", nil)
}
// AdminToggleProvider 启用/禁用SSO提供商
// PUT /api/admin/sso/providers/:id/toggle
func (c *SSOController) AdminToggleProvider(ctx *gin.Context) {
id, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
if err != nil {
utils.BadRequest(ctx, "无效的提供商ID")
return
}
var req struct {
Status int8 `json:"status"`
}
if err := ctx.ShouldBindJSON(&req); err != nil {
utils.BadRequest(ctx, "参数错误")
return
}
providers, _ := c.ssoService.ListProviders()
for i, p := range providers {
if p.ID == id {
providers[i].Status = req.Status
if err := c.ssoService.UpdateProvider(&providers[i]); err != nil {
utils.ServerError(ctx, err.Error())
return
}
utils.SuccessWithMessage(ctx, "操作成功", nil)
return
}
}
utils.NotFound(ctx, "提供商不存在")
}
// generateState 生成随机state参数(防CSRF)
func generateState() (string, error) {
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}