初始化
This commit is contained in:
298
server/controller/admin_controller.go
Normal file
298
server/controller/admin_controller.go
Normal 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)
|
||||
}
|
||||
100
server/controller/auth_controller.go
Normal file
100
server/controller/auth_controller.go
Normal 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)
|
||||
}
|
||||
356
server/controller/file_controller.go
Normal file
356
server/controller/file_controller.go
Normal 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)
|
||||
}
|
||||
120
server/controller/folder_controller.go
Normal file
120
server/controller/folder_controller.go
Normal 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)
|
||||
}
|
||||
285
server/controller/permission_controller.go
Normal file
285
server/controller/permission_controller.go
Normal 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,
|
||||
})
|
||||
}
|
||||
201
server/controller/share_controller.go
Normal file
201
server/controller/share_controller.go
Normal 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)
|
||||
}
|
||||
297
server/controller/sso_controller.go
Normal file
297
server/controller/sso_controller.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user