Parcourir la source

config: penType update

Yi il y a 1 mois
Parent
commit
421f989c10

+ 24 - 0
http/handler/test.go

@@ -91,3 +91,27 @@ func DataWarning(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func NeckRingOriginalAsync(c *gin.Context) {
+	pastureIdStr := c.Query("pasture_id")
+	if err := valid.Validate(pastureIdStr, valid.Required); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
+	pastureId, _ := strconv.Atoi(pastureIdStr)
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+	if err := middleware.BackendOperation(c).OpsService.NeckRingOriginalAsync(c, int64(pastureId), pagination); err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, &operationPb.CommonOK{
+		Code: http.StatusOK,
+		Msg:  "ok",
+		Data: &operationPb.Success{Success: true},
+	})
+}

+ 143 - 0
http/handler/upload/upload.go

@@ -1,15 +1,20 @@
 package upload
 
 import (
+	"bytes"
 	"fmt"
+	"io"
 	"kpt-pasture/config"
 	"kpt-pasture/http/middleware"
 	"net/http"
 	"os"
+	"path/filepath"
+	"time"
 
 	"gitee.com/xuyiping_admin/pkg/apierr"
 	"gitee.com/xuyiping_admin/pkg/xerr"
 	"github.com/gin-gonic/gin"
+	"github.com/xuri/excelize/v2"
 )
 
 func Photos(c *gin.Context) {
@@ -36,7 +41,145 @@ func Photos(c *gin.Context) {
 }
 
 func Files(c *gin.Context) {
+	// 获取上传的文件
+	file, err := c.FormFile("file")
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("获取文件失败: %s", err.Error()))
+		return
+	}
+
+	// 检查文件类型
+	ext := filepath.Ext(file.Filename)
+	if ext != ".xlsx" && ext != ".xls" {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("只支持Excel文件(.xlsx, .xls)"))
+		return
+	}
+
+	// 检查文件大小
+	if file.Size == 0 {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件为空"))
+		return
+	}
+
+	// 创建保存目录
+	uploadDir := fmt.Sprintf("%s/files/excel", config.WorkDir)
+	if err := os.MkdirAll(uploadDir, 0755); err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("创建目录失败: %s", err.Error()))
+		return
+	}
+
+	// 生成唯一文件名
+	filename := fmt.Sprintf("%d%s", time.Now().UnixNano(), ext)
+	filePath := filepath.Join(uploadDir, filename)
+
+	// 保存文件
+	if err := c.SaveUploadedFile(file, filePath); err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("保存文件失败: %s", err.Error()))
+		return
+	}
+
+	// 打开上传的文件
+	src, err := file.Open()
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("打开文件失败: %s", err.Error()))
+		return
+	}
+	defer src.Close()
+
+	// 读取文件内容
+	fileBytes, err := io.ReadAll(src)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取文件内容失败: %s", err.Error()))
+		return
+	}
+
+	// 检查文件头部签名
+	if len(fileBytes) < 8 {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确"))
+		return
+	}
+
+	// 检查Excel文件签名
+	isExcel := false
+	if ext == ".xlsx" {
+		// XLSX文件签名
+		if bytes.Equal(fileBytes[:4], []byte{0x50, 0x4B, 0x03, 0x04}) {
+			isExcel = true
+		}
+	} else if ext == ".xls" {
+		// XLS文件签名
+		if bytes.Equal(fileBytes[:8], []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1}) {
+			isExcel = true
+		}
+	}
+
+	if !isExcel {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("文件格式不正确,请确保上传的是有效的Excel文件"))
+		return
+	}
+
+	// 读取Excel文件
+	options := excelize.Options{
+		RawCellValue: true,
+	}
+	f, err := excelize.OpenReader(bytes.NewReader(fileBytes), options)
+	if err != nil {
+		// 如果直接读取失败,尝试从保存的文件读取
+		f, err = excelize.OpenFile(filePath)
+		if err != nil {
+			apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取Excel文件失败,请确保文件格式正确: %s", err.Error()))
+			return
+		}
+	}
+	defer f.Close()
+
+	// 获取第一个工作表
+	sheetName := f.GetSheetName(0)
+	rows, err := f.GetRows(sheetName)
+	if err != nil {
+		apierr.AbortBadRequest(c, http.StatusInternalServerError, xerr.Customf("读取工作表失败: %s", err.Error()))
+		return
+	}
+
+	// 处理Excel数据
+	if len(rows) < 2 {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Custom("Excel文件数据为空"))
+		return
+	}
+
+	// 获取表头并转换为中文
+	headers := rows[0]
+	headerMap := make(map[string]string)
+	for _, header := range headers {
+		headerMap[header] = header
+	}
+
+	// 处理数据行
+	var data []map[string]string
+	for i, row := range rows[1:] {
+		if len(row) != len(headers) {
+			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("第%d行数据列数与表头不匹配", i+2))
+			return
+		}
+
+		rowData := make(map[string]string)
+		for j, cell := range row {
+			rowData[headers[j]] = cell
+		}
+		data = append(data, rowData)
+	}
+
+	// 调用后端服务处理数据
+	if err := middleware.BackendOperation(c).OpsService.ImportExcel(c, data); err != nil {
+		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
+		return
+	}
 
+	c.JSON(http.StatusOK, gin.H{
+		"code": http.StatusOK,
+		"msg":  "导入成功",
+		"data": nil,
+	})
 }
 
 func OssVideo(c *gin.Context) {

+ 1 - 0
http/route/test_api.go

@@ -17,5 +17,6 @@ func TestAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		testRoute.POST("/cow/neck_ring/bound2", handler.CowNeckRingBound2)
 		testRoute.POST("/cow/pen/update", handler.UpdateCowPen)
 		testRoute.GET("/data/warning", handler.DataWarning)
+		testRoute.GET("/neck_ring_original/sync", handler.NeckRingOriginalAsync)
 	}
 }

+ 35 - 0
model/h_active_original.go

@@ -0,0 +1,35 @@
+package model
+
+type HActiveOriginal struct {
+	Id           int64  `gorm:"column:id"`
+	UUID         int64  `gorm:"column:UUID"`
+	IntPastureId int32  `gorm:"column:intPastureId"`
+	EID1         int64  `gorm:"column:EID1"`
+	HeatDate     string `gorm:"column:heatdate;type:date"` // 明确指定列名
+	CreateTime   string `gorm:"column:createtime"`         // 明确指定列名
+	Low          int32  `gorm:"column:low"`
+	High         int32  `gorm:"column:high"`
+	Rumina       int32  `gorm:"column:Rumina"`
+	Active       int32  `gorm:"column:active"`
+	Intake       int32  `gorm:"column:intake"`
+	Inactive     int32  `gorm:"column:inactive"`
+	Other        int32  `gorm:"column:Other"`
+	Gasp         int32  `gorm:"column:Gasp"`
+	Voltage      int32  `gorm:"column:voltage"`
+	UpPec        int32  `gorm:"column:UpPec"`
+	Version      int32  `gorm:"column:Version"`
+	Sign         int32  `gorm:"column:Sign"`
+	Remain       int32  `gorm:"column:Remain"`
+	Feed         int32  `gorm:"column:feed"`
+	FrameId      int32  `gorm:"column:frameid"` // 明确指定列名
+	Imei         int64  `gorm:"column:Imei"`
+	ReceiveNum   int32  `gorm:"column:receivenum"`
+	FrameNb      int32  `gorm:"column:frameNb"`
+	Senser       int32  `gorm:"column:senser"`
+	HIB          string `gorm:"column:HIB"`
+	Nb           int32  `gorm:"column:nb"`
+}
+
+func (h *HActiveOriginal) TableName() string {
+	return "h_active_original"
+}

+ 2 - 2
module/backend/analysis_more.go

@@ -65,13 +65,13 @@ func (s *StoreEntry) PenBehaviorDaily(ctx context.Context, req *pasturePb.BarnMo
 	if len(req.BarnIds) == 0 && req.BehaviorKind > 0 {
 		if err = pref.Where("pen_id IN (?)", req.BarnIds).
 			Order("heat_date").
-			Find(penBehaviorDayModelList).Error; err != nil {
+			Find(&penBehaviorDayModelList).Error; err != nil {
 			return nil, xerr.WithStack(err)
 		}
 	} else {
 		if err = pref.Group("heat_date,pen_id").
 			Order("heat_date,pen_id").
-			Find(penBehaviorDayModelList).Error; err != nil {
+			Find(&penBehaviorDayModelList).Error; err != nil {
 			return nil, xerr.WithStack(err)
 		}
 	}

+ 1 - 1
module/backend/config_data.go

@@ -26,7 +26,7 @@ func (s *StoreEntry) BarnTypeEnumList() []*pasturePb.ConfigOptionsList {
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.PenType_Lactation),
-		Label:    "泌乳牛舍",
+		Label:    "成母牛舍",
 		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.PenType_Peripartum),

+ 80 - 0
module/backend/data_warning.go

@@ -4,8 +4,11 @@ import (
 	"context"
 	"fmt"
 	"kpt-pasture/model"
+	"kpt-pasture/util"
 	"strings"
+	"time"
 
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 	"gitee.com/xuyiping_admin/pkg/xerr"
 )
 
@@ -27,6 +30,83 @@ func (s *StoreEntry) TestDataWaring(ctx context.Context, userId int64) error {
 	return nil
 }
 
+func (s *StoreEntry) NeckRingOriginalAsync(ctx context.Context, pastureId int64, pagination *pasturePb.PaginationModel) error {
+	pastureData := &model.AppPastureList{}
+	if err := s.DB.Model(new(model.AppPastureList)).
+		Where("id = ?", pastureId).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		First(pastureData).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	hActiveOriginalList := make([]*model.HActiveOriginal, 0)
+	if err := s.DB.Model(new(model.HActiveOriginal)).
+		Where("intPastureId = ?", pastureId).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&hActiveOriginalList).Error; err != nil {
+		return xerr.WithStack(err)
+	}
+
+	neckRingOriginalList := make([]*model.NeckRingOriginal, 0)
+	for _, v := range hActiveOriginalList {
+		_, hours := util.GetNeckRingActiveTimer(v.FrameId)
+		activeDate := ""
+		if v.HeatDate != "" && len(v.HeatDate) > 10 {
+			hd, _ := time.Parse(time.RFC3339, v.HeatDate)
+			activeDate = hd.Format(model.LayoutDate2)
+		}
+
+		var count int64
+		if err := s.DB.Model(new(model.NeckRingOriginal)).
+			Where("pasture_id = ?", pastureId).
+			Where("neck_ring_number = ?", v.EID1).
+			Where("frameid = ?", v.FrameId).
+			Where("active_date = ?", activeDate).
+			Count(&count).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		if count > 0 {
+			continue
+		}
+
+		neckRingOriginalList = append(neckRingOriginalList,
+			&model.NeckRingOriginal{
+				PastureId:       pastureId,
+				Uuid:            fmt.Sprintf("%d", v.UUID),
+				NeckRingNumber:  fmt.Sprintf("%d", v.EID1),
+				ActiveDate:      activeDate,
+				Hours:           int32(hours),
+				Frameid:         v.FrameId,
+				Rumina:          v.Rumina,
+				Intake:          v.Intake,
+				Inactive:        v.Inactive,
+				Gasp:            v.Gasp,
+				High:            v.High,
+				Active:          v.Active,
+				Other:           v.Other,
+				FirmwareVersion: 10,
+				HardwareVersion: 57,
+				Remain:          v.Remain,
+				Voltage:         v.Voltage,
+				RestartReason:   0,
+				Upper:           1,
+				ActiveDateType:  pasturePb.ActiveTimeType_Twenty_Minutes,
+				IsShow:          pasturePb.IsShow_No,
+				Imei:            fmt.Sprintf("%d", v.Imei),
+				ReceiveNumber:   fmt.Sprintf("%d", v.Imei),
+			})
+	}
+	if len(neckRingOriginalList) > 0 {
+		if err := s.DB.Model(new(model.NeckRingOriginal)).
+			Create(neckRingOriginalList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+	}
+	return nil
+}
+
 func (s *StoreEntry) BuildQuery(warningId int64) (string, []interface{}, error) {
 
 	conditionsMap := make(map[int32][]string)

+ 2 - 0
module/backend/interface.go

@@ -344,6 +344,7 @@ type MilkHallService interface {
 
 type UploadService interface {
 	Photos(ctx context.Context, files []*multipart.FileHeader) ([]string, error)
+	ImportExcel(ctx context.Context, data []map[string]string) error
 }
 
 type TestService interface {
@@ -351,4 +352,5 @@ type TestService interface {
 	CowNeckRingNumberBound2(ctx context.Context, pagination *pasturePb.PaginationModel) error
 	UpdateCowPen(ctx context.Context, pagination *pasturePb.PaginationModel) error
 	TestDataWaring(ctx context.Context, userId int64) error
+	NeckRingOriginalAsync(ctx context.Context, pastureId int64, pagination *pasturePb.PaginationModel) error
 }

+ 12 - 2
module/backend/system_service.go

@@ -82,10 +82,17 @@ func (s *StoreEntry) Login(ctx context.Context, req *pasturePb.SearchUserRequest
 
 // SearchSystemUserList 查询系统用户
 func (s *StoreEntry) SearchSystemUserList(ctx context.Context, req *pasturePb.SearchUserRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchUserResponse, error) {
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
 	systemUserList := make([]*model.SystemUser, 0)
 	var count int64 = 0
 
-	pref := s.DB.Model(new(model.SystemUser)).Where("is_delete = ?", operationPb.IsShow_OK)
+	pref := s.DB.Model(new(model.SystemUser)).
+		Where("is_delete = ?", operationPb.IsShow_OK).
+		Where("FIND_IN_SET(?, pasture_ids) > ?", userModel.AppPasture.Id, 0)
 	if req.Name != "" {
 		pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
 	}
@@ -102,7 +109,10 @@ func (s *StoreEntry) SearchSystemUserList(ctx context.Context, req *pasturePb.Se
 		pref.Where("is_show = ?", req.IsShow)
 	}
 
-	if err := pref.Order("is_show asc,id desc").Count(&count).Limit(int(pagination.PageSize)).Offset(int(pagination.PageOffset)).
+	if err = pref.Order("is_show asc,id desc").
+		Count(&count).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
 		Find(&systemUserList).Debug().Error; err != nil {
 		return nil, xerr.WithStack(err)
 	}

+ 59 - 0
module/backend/upload_file.go

@@ -4,12 +4,18 @@ import (
 	"context"
 	"fmt"
 	"kpt-pasture/config"
+	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"mime/multipart"
 	"os"
 	"path/filepath"
 	"time"
 
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
 	"gitee.com/xuyiping_admin/pkg/xerr"
 )
 
@@ -65,3 +71,56 @@ func (s *StoreEntry) Photos(ctx context.Context, files []*multipart.FileHeader)
 	}
 	return filePaths, nil
 }
+
+func (s *StoreEntry) ImportExcel(ctx context.Context, data []map[string]string) error {
+	// 获取当前用户信息
+	userModel, err := s.GetUserModel(ctx)
+	if err != nil {
+		return err
+	}
+
+	// 验证牧场ID
+	if userModel.AppPasture.Id <= 0 {
+		return xerr.Custom("无效的牧场ID")
+	}
+
+	cowList := make([]*model.Cow, 0)
+
+	// 批量处理数据
+	for _, row := range data {
+		// 验证必要字段
+		if _, ok := row["耳号"]; !ok {
+			return xerr.Custom("缺少必要字段: 耳号")
+		}
+
+		sex := pasturePb.Genders_Female
+		if _, ok := row["性别"]; !ok {
+			return xerr.Custom("缺少必要字段: 性别")
+		} else {
+			if row["性别"] == "公" {
+				sex = pasturePb.Genders_Male
+			}
+		}
+
+		// 创建或更新牛只信息
+		cow := &model.Cow{
+			PastureId: userModel.AppPasture.Id,
+			EarNumber: row["耳号"],
+			Sex:       sex,
+		}
+
+		// 检查牛只是否已存在
+		var existingCow model.Cow
+		if err = s.DB.Model(new(model.Cow)).
+			Where("pasture_id = ? AND ear_number = ?", userModel.AppPasture.Id, cow.EarNumber).
+			First(&existingCow).Error; err == nil {
+
+		} else {
+			cowList = append(cowList, cow)
+		}
+	}
+
+	zaplog.Info("ImportExcel", zap.Any("cowList", cowList))
+
+	return nil
+}

+ 1 - 1
module/crontab/neck_ring_merge.go

@@ -36,7 +36,7 @@ func (e *Entry) NeckRingOriginalMerge() (err error) {
 		e.CreateCrontabLog(NeckRingOriginal)
 		// 原始数据删除15天前的
 		e.DB.Model(new(model.NeckRingOriginal)).
-			Where("created_at < ?", newTime.AddDate(0, 0, -7).Unix()).
+			Where("created_at < ?", newTime.AddDate(0, -1, 0).Unix()).
 			Delete(new(model.NeckRingOriginal))
 		// 活动数据删除6个月前的数据
 		e.DB.Model(new(model.NeckActiveHabit)).