123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- package upload
- import (
- "fmt"
- "kpt-pasture/config"
- "kpt-pasture/http/middleware"
- "mime/multipart"
- "net/http"
- "os"
- "path/filepath"
- "strings"
- "time"
- "github.com/xuri/excelize/v2"
- operationPb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/operation"
- "gitee.com/xuyiping_admin/pkg/ginutil"
- "gitee.com/xuyiping_admin/pkg/apierr"
- "gitee.com/xuyiping_admin/pkg/xerr"
- "github.com/gin-gonic/gin"
- )
- func Photos(c *gin.Context) {
- form, err := c.MultipartForm()
- if err != nil {
- apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("No multipartForm: %s", err.Error()))
- return
- }
- files := form.File["uploads"]
- // 验证文件数量
- if len(files) == 0 {
- apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("No files selected"))
- return
- }
- res, err := middleware.BackendOperation(c).OpsService.Photos(c, files)
- if err != nil {
- apierr.AbortBadRequest(c, http.StatusBadRequest, err)
- return
- }
- c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "Msg": "ok", "data": res})
- }
- func Files(c *gin.Context) {
- // 1. 获取上传的文件
- file, err := c.FormFile("file")
- if err != nil {
- apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("获取文件失败: %s", err.Error()))
- return
- }
- cf := GetExcelImportConfig(c.GetHeader("farmid"))
- // 2. 验证文件基本属性
- if err = validateFile(file, cf); err != nil {
- apierr.AbortBadRequest(c, http.StatusBadRequest, err)
- return
- }
- // 3. 保存文件到临时目录
- filePath, err := saveUploadedFile(c, file)
- if err != nil {
- apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
- return
- }
- defer os.Remove(filePath) // 处理完成后删除临时文件
- // 4. 读取并验证Excel文件
- excelData, err := readExcelFile(filePath, filepath.Ext(file.Filename))
- if err != nil {
- apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
- return
- }
- // 5. 验证表头
- if err = validateHeaders(excelData[0], cf.RequiredHeaders); err != nil {
- apierr.AbortBadRequest(c, http.StatusInternalServerError, err)
- return
- }
- // 实现处理逻辑
- if err = middleware.BackendOperation(c).OpsService.ImportExcel2(c, excelData, cf.RequiredHeaders); err != nil {
- apierr.AbortBadRequest(c, http.StatusBadRequest, err)
- return
- }
- ginutil.JSONResp(c, &operationPb.CommonOK{
- Code: http.StatusOK,
- Msg: "ok",
- Data: &operationPb.Success{Success: true},
- })
- }
- // ExcelImportConfig 导入配置
- type ExcelImportConfig struct {
- AllowedExtensions []string
- MaxFileSize int64
- RequiredHeaders []string // 必须包含的表头
- HeadOfField []string
- }
- // readExcelFile 读取Excel文件
- func readExcelFile(filePath, ext string) ([][]string, error) {
- // 尝试使用excelize读取
- f, err := excelize.OpenFile(filePath)
- if err != nil {
- // 如果是xls格式且读取失败,尝试使用xls库
- if ext == ".xls" {
- return readXlsFile(filePath)
- }
- return nil, xerr.Customf("读取Excel文件失败: %s", err.Error())
- }
- defer f.Close()
- // 获取第一个工作表
- sheetName := f.GetSheetName(0)
- rows, err := f.GetRows(sheetName)
- if err != nil {
- return nil, xerr.Customf("读取工作表失败: %s", err.Error())
- }
- if len(rows) == 0 {
- return nil, xerr.Custom("Excel文件为空")
- }
- return rows, nil
- }
- // readXlsFile 使用xls库读取旧版Excel文件
- func readXlsFile(filePath string) ([][]string, error) {
- // 这里需要实现xls文件的读取逻辑
- // 可以使用第三方库如github.com/extrame/xls
- return nil, xerr.Custom("暂不支持.xls格式文件")
- }
- func GetExcelImportConfig(farmId string) *ExcelImportConfig {
- switch farmId {
- case "e50cd455dec93edf3d8135abbc770680":
- return &ExcelImportConfig{
- AllowedExtensions: []string{".xlsx", ".xls"},
- RequiredHeaders: []string{"", "", "耳号", "牛舍名称", "性别", "出生日期", "胎次", "进场日期", "父号", "母号", "最近配种日期", "预产期", "配后天数",
- "累计配次", "最近妊检日期", "最近产犊日期", "产后天数", "品种", "出生重量", "断奶日期", "是否禁配", "体重", "体高", "流产日期", "流产原因"},
- HeadOfField: []string{"", "", "earNumber", "penName", "sex", "birthAt", "lact", "admissionAt", "fatherNumber", "motherNumber",
- "lastMatingAt", "", "matingAge", "allMatingTimes", "lastPregnantCheckAt", "lastCalvingAt", "lactationAge",
- "cowKind", "birthWeight", "weaningAt", "isForbiddenMating", "currentWeight", "currenWtHeight", "lastAbortionAt", ""},
- MaxFileSize: 10 << 20,
- }
- default:
- return nil
- }
- }
- // ExcelHandler Excel处理接口
- type ExcelHandler interface {
- ProcessExcelData([][]string) error
- }
- // validateFile 验证文件基本属性
- func validateFile(file *multipart.FileHeader, cfg *ExcelImportConfig) error {
- // 检查文件扩展名
- ext := strings.ToLower(filepath.Ext(file.Filename))
- validExt := false
- for _, allowedExt := range cfg.AllowedExtensions {
- if ext == allowedExt {
- validExt = true
- break
- }
- }
- if !validExt {
- return xerr.Customf("不支持的文件类型,仅支持: %v", cfg.AllowedExtensions)
- }
- // 检查文件大小
- if file.Size > cfg.MaxFileSize {
- return xerr.Customf("文件大小超过限制(最大%dMB)", cfg.MaxFileSize>>20)
- }
- return nil
- }
- // saveUploadedFile 保存上传的文件
- func saveUploadedFile(c *gin.Context, file *multipart.FileHeader) (string, error) {
- // 创建临时目录
- uploadDir := filepath.Join(config.WorkDir, "temp", "excel")
- if err := os.MkdirAll(uploadDir, 0755); err != nil {
- return "", xerr.Customf("创建临时目录失败: %s", err.Error())
- }
- // 生成唯一文件名
- filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), filepath.Ext(file.Filename))
- filePath := filepath.Join(uploadDir, filename)
- // 保存文件
- if err := c.SaveUploadedFile(file, filePath); err != nil {
- return "", xerr.Customf("保存文件失败: %s", err.Error())
- }
- return filePath, nil
- }
- // validateHeaders 验证表头
- func validateHeaders(headers []string, requiredHeaders []string) error {
- if len(requiredHeaders) == 0 {
- return nil
- }
- headerMap := make(map[string]bool)
- for _, h := range headers {
- headerMap[strings.TrimSpace(h)] = true
- }
- var missingHeaders []string
- for _, req := range requiredHeaders {
- if req == "" {
- continue
- }
- if !headerMap[req] {
- missingHeaders = append(missingHeaders, req)
- }
- }
- if len(missingHeaders) > 0 {
- return xerr.Customf("缺少必要表头: %v", missingHeaders)
- }
- return nil
- }
- func OssVideo(c *gin.Context) {
- filename := c.Param("name")
- videoPath := fmt.Sprintf("%s/files/video/%s", config.WorkDir, filename)
- // 打开视频文件
- file, err := os.Open(videoPath)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Video not found"})
- return
- }
- defer file.Close()
- fileInfo, err := file.Stat()
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Could not get file info"})
- return
- }
- // 设置响应头
- c.Header("Content-Type", "video/mp4")
- c.Header("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
- // 流式传输文件内容
- http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), file)
- }
|