Prechádzať zdrojové kódy

file: upload excel update

Yi 2 dní pred
rodič
commit
38612ed364

+ 14 - 16
http/handler/upload/upload.go

@@ -52,7 +52,7 @@ func Files(c *gin.Context) {
 		return
 	}
 
-	cf := GetExcelImportConfig(c.GetHeader("farmid"))
+	cf := GetExcelImportConfig()
 	// 2. 验证文件基本属性
 	if err = validateFile(file, cf); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
@@ -73,13 +73,15 @@ func Files(c *gin.Context) {
 		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 {
+	if err = middleware.BackendOperation(c).OpsService.ImportExcel2(c, excelData, cf.RequiredHeaders, cf.HeadOfField); err != nil {
 		apierr.AbortBadRequest(c, http.StatusBadRequest, err)
 		return
 	}
@@ -115,6 +117,7 @@ func readExcelFile(filePath, ext string) ([][]string, error) {
 	// 获取第一个工作表
 	sheetName := f.GetSheetName(0)
 	rows, err := f.GetRows(sheetName)
+
 	if err != nil {
 		return nil, xerr.Customf("读取工作表失败: %s", err.Error())
 	}
@@ -133,20 +136,15 @@ func readXlsFile(filePath string) ([][]string, error) {
 	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
+func GetExcelImportConfig() *ExcelImportConfig {
+	return &ExcelImportConfig{
+		AllowedExtensions: []string{".xlsx", ".xls"},
+		RequiredHeaders: []string{"序号", "牛号", "性别", "出生日期", "栏舍名称", "牛只类型", "牛只品种", "牛只来源", "入场日期", "当前体重(KG)", "用途",
+			"胎次", "配种日期", "孕检日期", "孕检结果", "产犊日期", "流产日期", "是否禁配", "配次", "与公牛配", "脖环号", "父号", "母号", "批次号", "购买价格(¥)"},
+		HeadOfField: []string{"id", "earNumber", "sex", "birthDate", "penName", "cowType", "cowKind", "sourceKind", "admissionDate", "currentWeight",
+			"purposeKind", "lact", "matingDate", "pregnantDate", "calvingDate", "abortionDate", "isForbiddenMating", "matingTimes", "bullNumber",
+			"neckRingNumber", "fatherNumber", "motherNumber", "batchNumber", "admissionPrice"},
+		MaxFileSize: 10 << 20,
 	}
 }
 

+ 33 - 0
module/backend/enum_map.go

@@ -71,6 +71,15 @@ func (s *StoreEntry) CowTypeMap(userModel *model.UserModel) map[pasturePb.CowTyp
 	return res
 }
 
+func (s *StoreEntry) CowTypeMap2(userModel *model.UserModel) map[string]pasturePb.CowType_Kind {
+	res := make(map[string]pasturePb.CowType_Kind)
+	optionName, isAll := "", ""
+	for _, v := range s.CowTypeEnumList(userModel, optionName, isAll) {
+		res[v.Label] = pasturePb.CowType_Kind(v.Value)
+	}
+	return res
+}
+
 func (s *StoreEntry) CowBreedStatusMap(userModel *model.UserModel) map[pasturePb.BreedStatus_Kind]string {
 	res := make(map[pasturePb.BreedStatus_Kind]string)
 	for _, v := range s.BreedStatusEnumList(userModel, "") {
@@ -87,6 +96,14 @@ func (s *StoreEntry) CowKindMap(userModel *model.UserModel) map[pasturePb.CowKin
 	return res
 }
 
+func (s *StoreEntry) CowKindMap2(userModel *model.UserModel) map[string]pasturePb.CowKind_Kind {
+	res := make(map[string]pasturePb.CowKind_Kind)
+	for _, v := range s.CowKindEnumList(userModel, "") {
+		res[v.Label] = pasturePb.CowKind_Kind(v.Value)
+	}
+	return res
+}
+
 func (s *StoreEntry) CowSourceMap(userModel *model.UserModel) map[pasturePb.CowSource_Kind]string {
 	res := make(map[pasturePb.CowSource_Kind]string)
 	for _, v := range s.CowSourceEnumList(userModel, "") {
@@ -95,6 +112,14 @@ func (s *StoreEntry) CowSourceMap(userModel *model.UserModel) map[pasturePb.CowS
 	return res
 }
 
+func (s *StoreEntry) CowSourceMap2(userModel *model.UserModel) map[string]pasturePb.CowSource_Kind {
+	res := make(map[string]pasturePb.CowSource_Kind)
+	for _, v := range s.CowSourceEnumList(userModel, "") {
+		res[v.Label] = pasturePb.CowSource_Kind(v.Value)
+	}
+	return res
+}
+
 func (s *StoreEntry) WorkOrderCategoryMap(userModel *model.UserModel) map[pasturePb.WorkOrderCategory_Kind]string {
 	res := make(map[pasturePb.WorkOrderCategory_Kind]string)
 	for _, v := range s.WorkOrderCategoryEnumList(userModel, "") {
@@ -191,6 +216,14 @@ func (s *StoreEntry) PurposeMap(userModel *model.UserModel) map[pasturePb.Purpos
 	return res
 }
 
+func (s *StoreEntry) PurposeMap2(userModel *model.UserModel) map[string]pasturePb.Purpose_Kind {
+	res := make(map[string]pasturePb.Purpose_Kind)
+	for _, v := range s.CowPurposeList(userModel, "") {
+		res[v.Label] = pasturePb.Purpose_Kind(v.Value)
+	}
+	return res
+}
+
 func (s *StoreEntry) DiseaseTypeMap(userModel *model.UserModel) map[int32]string {
 	res := make(map[int32]string)
 	for _, v := range s.diseaseTypeEnumList(userModel, "") {

+ 1 - 1
module/backend/interface.go

@@ -364,7 +364,7 @@ type MilkHallService interface {
 type UploadService interface {
 	Photos(ctx context.Context, files []*multipart.FileHeader) ([]string, error)
 	ImportExcel(ctx context.Context, data [][]string) error
-	ImportExcel2(ctx context.Context, data [][]string, excelHeader []string) error
+	ImportExcel2(ctx context.Context, data [][]string, excelHeader []string, headOfField []string) error
 }
 
 type FeedingService interface {

+ 1 - 1
module/backend/sql.go

@@ -225,7 +225,7 @@ func (s *StoreEntry) GetPenList(ctx context.Context, pastureId int64) ([]*model.
 		Where("is_delete = ?", pasturePb.IsShow_Ok).
 		Where("pasture_id = ?", pastureId).
 		Find(&penList).Error; err != nil {
-		return nil, xerr.WithStack(err)
+		return penList, xerr.WithStack(err)
 	}
 	return penList, nil
 }

+ 104 - 130
module/backend/upload_file.go

@@ -9,9 +9,7 @@ import (
 	"mime/multipart"
 	"os"
 	"path/filepath"
-	"reflect"
 	"strconv"
-	"strings"
 	"time"
 
 	"gorm.io/gorm"
@@ -199,189 +197,167 @@ func (s *StoreEntry) ImportExcel(ctx context.Context, data [][]string) error {
 	return s.ExecExcelData(ctx, pastureId, eventEnterList)
 }
 
-func (s *StoreEntry) ImportExcel2(ctx context.Context, data [][]string, excelHeader []string) error {
+func (s *StoreEntry) ImportExcel2(ctx context.Context, data [][]string, excelHeader []string, headOfField []string) error {
 	// 获取当前用户信息
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
 		return err
 	}
+	// excelHeader ["序号","牛号","性别","出生日期","栏舍名称","牛只类型","牛只品种","牛只来源","入场日期","当前体重(KG)","用途","胎次","配种日期","孕检日期","孕检结果","产犊日期","流产日期","是否禁配",
 
-	penMap := s.PenMap2(ctx, userModel.AppPasture.Id)
+	pastureId := userModel.AppPasture.Id
 	if userModel.AppPasture.Id <= 0 {
 		return xerr.Custom("无效的牧场ID")
 	}
+	penMap := s.PenMap2(ctx, pastureId)
+	if len(penMap) <= 0 {
+		return xerr.Custom("当前牧场没有配置栏舍信息")
+	}
+
+	cowTypeMap := s.CowTypeMap2(userModel)
+	cowKindMap := s.CowKindMap2(userModel)
+	cowSourceMap := s.CowSourceMap2(userModel)
+	purposeMap := s.PurposeMap2(userModel)
+
 	// 处理Excel数据
-	//headers := data[0]
 	eventEnterList := make([]*pasturePb.EventEnterRequest, 0)
-	/*for _, row := range data[1:] {
-		if len(row) <= 0 {
+	// 遍历数据行(跳过表头)
+	for _, rows := range data[1:] {
+		if len(rows) == 0 { //  [1 HS10034 母 08-12-22 东区1号 成母牛 西门塔尔牛 产犊 09-12-23 500 繁育 1 12-05-24 01-15-25 有胎   否 2  789]
 			continue
 		}
+
 		ts := &pasturePb.EventEnterRequest{
 			OperationId:   int32(userModel.SystemUser.Id),
 			OperationName: userModel.SystemUser.Name,
 			MessengerId:   int32(userModel.SystemUser.Id),
 			MessengerName: userModel.SystemUser.Name,
 		}
-		for j, d := range row {
-			switch j {
-			case 0:
+
+		for j, _ := range rows {
+			if j == 0 {
 				continue
+			}
+			value := rows[j]
+			switch j {
 			case 1:
-				continue
+				ts.EarNumber = value
 			case 2:
-				ts.EarNumber = d
-			case 3:
-				if pn, ok := penMap[d]; ok {
-					ts.PenId = pn.Id
-					ts.PenName = pn.Name
-				}
-			case 4:
 				ts.Sex = pasturePb.Genders_Female
-				if d == "公" {
+				if value == "公" {
 					ts.Sex = pasturePb.Genders_Male
 				}
-			case 5:
-				if d == "成母牛" {
-					ts.CowType = pasturePb.CowType_Breeding_Calf
-				} else if d == "犊牛" {
-					ts.CowType = pasturePb.CowType_Lactating_Calf
-				} else if d == "青年牛" {
-					ts.CowType = pasturePb.CowType_Youth_Calf
-				} else if d == "育成牛" {
-					ts.CowType = pasturePb.CowType_Reserve_Calf
+			case 3:
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.BirthAt = int32(bat.Unix())
+				} else {
+					ts.BirthAt = 0
 				}
+			case 4:
+				ts.PenName = value
+			case 5:
+				ts.CowTypeName = value
 			case 6:
-				continue
+				ts.CowKindName = value
 			case 7:
-				bat, _ := util.TimeParseLocal(model.LayoutTime2, d)
-				if !bat.IsZero() {
-					ts.BirthAt = int32(bat.Local().Unix())
+				if cs, ok := cowSourceMap[value]; ok {
+					ts.CowSource = cs
 				}
 			case 8:
-				lact, _ := strconv.Atoi(d)
-				ts.Lact = int32(lact)
-			case 9:
-				eat, _ := util.TimeParseLocal(model.LayoutTime2, d)
-				if !eat.IsZero() {
-					ts.EnterAt = int32(eat.Local().Unix())
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.EnterAt = int32(bat.Unix())
+				} else {
+					ts.EnterAt = 0
 				}
+			case 9:
+				ct, _ := strconv.ParseFloat(value, 64)
+				ts.Weight = float32(ct)
 			case 10:
-				ts.FatherNumber = d
+				ts.PurposeName = value
 			case 11:
-				ts.MotherNumber = d
+				lact, _ := strconv.Atoi(value)
+				ts.Lact = int32(lact)
 			case 12:
-				mat, _ := util.TimeParseLocal(model.LayoutTime2, d)
-				if !mat.IsZero() {
-					ts.MatingAt = int32(mat.Local().Unix())
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.MatingAt = int32(bat.Unix())
+				} else {
+					ts.MatingAt = 0
 				}
 			case 13:
-				continue
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.PregnancyCheckAt = int32(bat.Unix())
+				} else {
+					ts.PregnancyCheckAt = 0
+				}
 			case 14:
-				continue
+				ts.PregnantCheckResult = pasturePb.PregnantCheckResult_Pregnant
+				if value == "无胎" {
+					ts.PregnantCheckResult = pasturePb.PregnantCheckResult_UnPregnant
+				}
 			case 15:
-				mt, _ := strconv.Atoi(d)
-				ts.MatingTimes = int32(mt)
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.CalvingAt = int32(bat.Unix())
+				} else {
+					ts.CalvingAt = 0
+				}
 			case 16:
-				continue
+				bat, _ := util.TimeParseLocal(model.LayoutTime2, value+" 00:00:00")
+				if !bat.IsZero() {
+					ts.AbortionAt = int32(bat.Unix())
+				} else {
+					ts.AbortionAt = 0
+				}
 			case 17:
-				pat, _ := util.TimeParseLocal(model.LayoutTime2, d)
-				if !pat.IsZero() {
-					ts.PregnancyCheckAt = int32(pat.Local().Unix())
+				if value == "是" {
+					ts.BreedStatus = pasturePb.BreedStatus_No_Mating
 				}
 			case 18:
-				continue
+				mts, _ := strconv.Atoi(value)
+				ts.MatingTimes = int32(mts)
+
 			case 19:
-				continue
+				ts.BullNumber = value
 			case 20:
-				cat, _ := util.TimeParseLocal(model.LayoutTime2, d)
-				if !cat.IsZero() {
-					ts.CalvingAt = int32(cat.Local().Unix())
-				}
+				ts.NeckRingNumber = value
 			case 21:
-				continue
+				ts.MotherNumber = value
 			case 22:
-				continue
+				ts.FatherNumber = value
 			case 23:
-				continue
+				ts.BatchNumber = value
 			case 24:
-				ts.CowKind = pasturePb.CowKind_AGSN
+				price, _ := strconv.ParseFloat(value, 64)
+				ts.Price = float32(price)
 			}
 		}
-		eventEnterList = append(eventEnterList, ts)
-	}*/
-	// 遍历数据行(跳过表头)
-	for _, row := range data[1:] {
-		if len(row) == 0 {
-			continue
-		}
 
-		ts := &pasturePb.EventEnterRequest{
-			OperationId:   int32(userModel.SystemUser.Id),
-			OperationName: userModel.SystemUser.Name,
-			MessengerId:   int32(userModel.SystemUser.Id),
-			MessengerName: userModel.SystemUser.Name,
-		}
-
-		// 使用反射动态匹配字段
-		val := reflect.ValueOf(ts).Elem()
-		typ := val.Type()
-
-		for i := 0; i < val.NumField(); i++ {
-			field := typ.Field(i)
-			fieldVal := val.Field(i)
-
-			// 获取字段的 excel 标签(如 excel:"耳标号")
-			excelTag := strings.ReplaceAll(field.Tag.Get("json"), ",omitempty", "")
-			if excelTag == "" {
-				continue // 没有 excel 标签的字段跳过
-			}
-
-			// 查找 excelHeader 中匹配的列索引
-			colIndex := -1
-			for j, colName := range excelHeader {
-				if colName == "" {
-					continue // 跳过空列名
-				}
-				if colName == excelTag {
-					colIndex = j
-					break
-				}
+		// 特殊处理 PenId 和 PenName(依赖 penMap)
+		if ts.PenName != "" {
+			if pen, ok := penMap[ts.PenName]; ok {
+				ts.PenId = pen.Id
 			}
+		}
 
-			if colIndex == -1 || colIndex >= len(row) {
-				continue // 列名不匹配或数据越界
+		if ts.CowTypeName != "" {
+			if ct, ok := cowTypeMap[ts.CowTypeName]; ok {
+				ts.CowType = ct
 			}
+		}
 
-			cellValue := row[colIndex]
-			if cellValue == "" {
-				continue // 空值跳过
-			}
-			// 根据字段类型设置值
-			switch fieldVal.Kind() {
-			case reflect.String:
-				fieldVal.SetString(cellValue)
-			case reflect.Int32:
-				if num, err := strconv.ParseInt(cellValue, 10, 32); err == nil {
-					fieldVal.SetInt(num)
-				}
-			case reflect.Struct: // 处理枚举或时间类型
-				switch field.Type {
-				case reflect.TypeOf(pasturePb.Genders_Female):
-					if cellValue == "公" {
-						fieldVal.Set(reflect.ValueOf(pasturePb.Genders_Male))
-					} else {
-						fieldVal.Set(reflect.ValueOf(pasturePb.Genders_Female))
-					}
-					// 添加其他自定义类型的处理...
-				}
+		if ts.CowKindName != "" {
+			if ck, ok := cowKindMap[ts.CowKindName]; ok {
+				ts.CowKind = ck
 			}
 		}
 
-		// 特殊处理 PenId 和 PenName(依赖 penMap)
-		if ts.PenName != "" {
-			if pen, ok := penMap[ts.PenName]; ok {
-				ts.PenId = pen.Id
+		if ts.PurposeName != "" {
+			if p, ok := purposeMap[ts.PurposeName]; ok {
+				ts.PurposeKind = p
 			}
 		}
 
@@ -390,9 +366,7 @@ func (s *StoreEntry) ImportExcel2(ctx context.Context, data [][]string, excelHea
 	if len(eventEnterList) <= 0 {
 		return nil
 	}
-
-	return nil
-	return s.ExecExcelData(ctx, userModel.AppPasture.Id, eventEnterList)
+	return s.ExecExcelData(ctx, pastureId, eventEnterList)
 }
 
 func (s *StoreEntry) ExecExcelData(ctx context.Context, pastureId int64, dataList []*pasturePb.EventEnterRequest) error {