// Package handlers 实现API请求处理器 // 包含工作簿(Excel文档)的增删改查操作 // 工作簿通过 documents 表存储元数据,workbooks 表存储Excel特化数据 package handlers import ( "excel-api/models" "excel-api/utils" "net/http" "strconv" "github.com/gin-gonic/gin" ) // GetWorkbooks 获取工作簿列表 // 支持分页查询,通过JOIN关联文档表获取完整信息 // 请求方式: GET // 路径: /api/workbooks // 查询参数: page(页码,默认1), pageSize(每页条数,默认20) func GetWorkbooks(c *gin.Context) { // 解析分页参数 page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20")) if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } var workbooks []models.Workbook var total int64 // 查询总数(关联文档表) models.DB.Model(&models.Workbook{}).Joins("JOIN documents ON documents.id = workbooks.document_id").Count(&total) // 分页查询工作簿列表,预加载关联的工作表 result := models.DB. Joins("JOIN documents ON documents.id = workbooks.document_id"). Preload("Sheets"). Offset((page - 1) * pageSize).Limit(pageSize). Order("documents.created_at DESC"). Find(&workbooks) if result.Error != nil { utils.Error(c, 500, "查询工作簿列表失败") return } utils.SuccessPage(c, workbooks, total, page, pageSize) } // CreateWorkbook 创建新工作簿 // 同时创建对应的文档记录和默认工作表 // 请求方式: POST // 路径: /api/workbooks // 请求体: { "title": "文档标题", "description": "描述" } func CreateWorkbook(c *gin.Context) { var req struct { Title string `json:"title" binding:"required"` // 文档标题,必填 Description string `json:"description"` // 文档描述,可选 } if err := c.ShouldBindJSON(&req); err != nil { utils.Error(c, 400, "参数错误: "+err.Error()) return } // 创建文档记录(excel类型) document := models.Document{ Type: "excel", Title: req.Title, Description: req.Description, CreatorID: 1, // 前期默认用户ID Status: 1, } result := models.DB.Create(&document) if result.Error != nil { utils.Error(c, 500, "创建文档失败") return } // 创建工作簿记录 workbook := models.Workbook{ DocumentID: document.ID, } result = models.DB.Create(&workbook) if result.Error != nil { utils.Error(c, 500, "创建工作簿失败") return } // 为新工作簿创建默认工作表 defaultSheet := models.Sheet{ WorkbookID: workbook.ID, Name: "Sheet1", Index: 0, ColCount: 26, RowCount: 100, } models.DB.Create(&defaultSheet) // 返回完整信息 workbook.Document = document utils.SuccessWithMessage(c, "创建工作簿成功", workbook) } // GetWorkbook 获取工作簿详情 // 请求方式: GET // 路径: /api/workbooks/:id // 路径参数: id - 工作簿ID func GetWorkbook(c *gin.Context) { id := c.Param("id") var workbook models.Workbook // 查询工作簿并预加载关联的工作表和文档 result := models.DB.Preload("Sheets").Preload("Document").First(&workbook, id) if result.Error != nil { utils.Error(c, 404, "工作簿不存在") return } utils.Success(c, workbook) } // UpdateWorkbook 更新工作簿信息 // 更新关联的文档表信息 // 请求方式: PUT // 路径: /api/workbooks/:id // 路径参数: id - 工作簿ID // 请求体: { "title": "新标题", "description": "新描述" } func UpdateWorkbook(c *gin.Context) { id := c.Param("id") var workbook models.Workbook // 查找要更新的工作簿 result := models.DB.First(&workbook, id) if result.Error != nil { utils.Error(c, 404, "工作簿不存在") return } var req struct { Title string `json:"title"` // 文档标题 Description string `json:"description"` // 文档描述 } if err := c.ShouldBindJSON(&req); err != nil { utils.Error(c, 400, "参数错误: "+err.Error()) return } // 更新关联的文档表 var document models.Document if err := models.DB.First(&document, workbook.DocumentID).Error; err == nil { if req.Title != "" { document.Title = req.Title } document.Description = req.Description models.DB.Save(&document) } utils.SuccessWithMessage(c, "更新工作簿成功", workbook) } // DeleteWorkbook 删除工作簿(软删除) // 同时删除关联的文档、工作表和单元格 // 请求方式: DELETE // 路径: /api/workbooks/:id // 路径参数: id - 工作簿ID func DeleteWorkbook(c *gin.Context) { id := c.Param("id") var workbook models.Workbook // 查找要删除的工作簿 result := models.DB.First(&workbook, id) if result.Error != nil { utils.Error(c, 404, "工作簿不存在") return } // 获取关联的工作表IDs var sheetIDs []uint models.DB.Model(&models.Sheet{}).Where("workbook_id = ?", id).Pluck("id", &sheetIDs) // 软删除单元格 if len(sheetIDs) > 0 { models.DB.Where("sheet_id IN ?", sheetIDs).Delete(&models.Cell{}) } // 软删除工作表 models.DB.Where("workbook_id = ?", id).Delete(&models.Sheet{}) // 软删除工作簿 models.DB.Delete(&workbook) // 软删除关联的文档 models.DB.Delete(&models.Document{}, workbook.DocumentID) utils.SuccessWithMessage(c, "删除工作簿成功", nil) } // HandleOptions 处理OPTIONS预检请求 // 用于CORS跨域请求的预检 func HandleOptions(c *gin.Context) { c.Status(http.StatusNoContent) }