Pārlūkot izejas kodu

event: disease 疾病治疗

Yi 1 mēnesi atpakaļ
vecāks
revīzija
69b7b32413

+ 7 - 1
model/event_calving.go

@@ -27,6 +27,8 @@ type EventCalving struct {
 	CalvingLevel         pasturePb.CalvingLevel_Kind   `json:"calvingLevel"`
 	OperationId          int64                         `json:"operationId"`
 	OperationName        string                        `json:"operationName"`
+	MessageId            int64                         `json:"messageId"`
+	MessageName          string                        `json:"messageName"`
 	DystociaReason       pasturePb.DystociaReason_Kind `json:"dystociaReason"`
 	IsInducingChildbirth pasturePb.IsShow_Kind         `json:"isInducingChildbirth"`
 	Remarks              string                        `json:"remarks"`
@@ -38,7 +40,7 @@ func (e *EventCalving) TableName() string {
 	return "event_calving"
 }
 
-func (e *EventCalving) EventUpdate(operationUser *SystemUser, req *pasturePb.EventCalving, cow *Cow) {
+func (e *EventCalving) EventUpdate(operationUser, currentUser *SystemUser, req *pasturePb.EventCalving, cow *Cow) {
 	if operationUser != nil {
 		e.OperationId = operationUser.Id
 		e.OperationName = operationUser.Name
@@ -54,12 +56,15 @@ func (e *EventCalving) EventUpdate(operationUser *SystemUser, req *pasturePb.Eve
 	e.IsInducingChildbirth = req.IsInducingChildbirth
 	e.Remarks = req.Remarks
 	e.DystociaReason = req.DystociaReason
+	e.MessageId = currentUser.Id
+	e.MessageName = currentUser.Name
 }
 
 func NewEventCalving(pastureId int64, cow *Cow) *EventCalving {
 	return &EventCalving{
 		PastureId:  pastureId,
 		CowId:      cow.Id,
+		Lact:       cow.Lact,
 		CowKind:    cow.CowKind,
 		CowType:    cow.CowType,
 		EarNumber:  cow.EarNumber,
@@ -161,5 +166,6 @@ func (e EventCalvingSlice) ToPB() []*pasturePb.CalvingReportTable {
 			NormalLaborCount:    0,
 		}
 	}
+
 	return res
 }

+ 45 - 25
model/event_cow_disease.go

@@ -14,17 +14,21 @@ type EventCowDisease struct {
 	CowType               pasturePb.CowType_Kind      `json:"cowType"`
 	Lact                  int32                       `json:"lact"`
 	DayAge                int32                       `json:"dayAge"`
+	PenId                 int32                       `json:"penId"`
+	PenName               string                      `json:"penName"`
 	DiseaseId             int32                       `json:"diseaseId"`
 	DiseaseName           string                      `json:"diseaseName"`
 	DiseaseType           int32                       `json:"diseaseType"`
 	DiseaseTypeName       string                      `json:"diseaseTypeName"`
 	DiagnoseId            int32                       `json:"diagnoseId"`
 	DiagnoseName          string                      `json:"diagnoseName"`
-	PenId                 int32                       `json:"penId"`
 	HealthStatus          pasturePb.HealthStatus_Kind `json:"healthStatus"`
 	DiagnosedResult       pasturePb.IsShow_Kind       `json:"diagnosedResult"`
 	DiagnosedAt           int64                       `json:"diagnosedAt"`
 	DiseaseAt             int64                       `json:"diseaseAt"`
+	FirstTreatmentAt      int64                       `json:"firstTreatmentAt"`
+	LastTreatmentAt       int64                       `json:"lastTreatmentAt"`
+	LastPrescriptionName  string                      `json:"LastPrescriptionName"`
 	Temperature           int32                       `json:"temperature"`
 	Remarks               string                      `json:"remarks"`
 	OperationId           int32                       `json:"operationId"`
@@ -63,8 +67,13 @@ func (e *EventCowDisease) EventDiseaseUpdate(disease *Disease, operationUser *Sy
 	e.DiseaseAt = time.Now().Unix()
 }
 
-// EventHealthStatusUpdate 改变健康状态
-func (e *EventCowDisease) EventHealthStatusUpdate(healthStatus pasturePb.HealthStatus_Kind) {
+// EventTreatmentUpdate 治疗更新
+func (e *EventCowDisease) EventTreatmentUpdate(healthStatus pasturePb.HealthStatus_Kind, treatmentAt int64, prescriptionName string) {
+	if e.FirstTreatmentAt <= 0 {
+		e.FirstTreatmentAt = treatmentAt
+	}
+	e.LastTreatmentAt = treatmentAt
+	e.LastPrescriptionName = prescriptionName
 	e.HealthStatus = healthStatus
 }
 
@@ -76,10 +85,6 @@ func (e *EventCowDisease) EventCurableUpdate(cureAt int64) {
 }
 
 func NewEventCowDisease(pastureId int64, cow *Cow, disease *Disease, req *pasturePb.EventCowDiseaseRequest, operation, currUser *SystemUser) *EventCowDisease {
-	penId := req.PenId
-	if penId == 0 {
-		penId = cow.PenId
-	}
 	return &EventCowDisease{
 		PastureId:       pastureId,
 		CowId:           cow.Id,
@@ -91,7 +96,8 @@ func NewEventCowDisease(pastureId int64, cow *Cow, disease *Disease, req *pastur
 		DiseaseName:     disease.Name,
 		DiseaseType:     disease.DiseaseType,
 		DiseaseTypeName: disease.DiseaseTypeName,
-		PenId:           penId,
+		PenId:           cow.PenId,
+		PenName:         cow.PenName,
 		HealthStatus:    pasturePb.HealthStatus_Health,
 		DiseaseAt:       int64(req.DiseaseAt),
 		Temperature:     int32(req.Temperature * 10),
@@ -111,24 +117,38 @@ type EventCowDiseaseSlice []*EventCowDisease
 func (e EventCowDiseaseSlice) ToPB(healthStatusMap map[pasturePb.HealthStatus_Kind]string) []*pasturePb.EventCowDisease {
 	res := make([]*pasturePb.EventCowDisease, len(e))
 	for i, v := range e {
+		lastTreatedTimeFormat := ""
+		if v.LastTreatmentAt > 0 {
+			lastTreatedTimeFormat = time.Unix(v.LastTreatmentAt, 0).Format(LayoutDate2)
+		}
+		onsetDays := int32(0)
+		if v.FirstTreatmentAt > 0 {
+			firstTime := time.Unix(v.FirstTreatmentAt, 0)
+			diff := time.Now().Sub(firstTime)
+			onsetDays = int32(diff.Hours() / 24)
+		}
 		res[i] = &pasturePb.EventCowDisease{
-			Id:               int32(v.Id),
-			CowId:            int32(v.CowId),
-			EarNumber:        v.EarNumber,
-			Lact:             v.Lact,
-			DayAge:           v.DayAge,
-			DiseaseId:        v.DiseaseId,
-			DiseaseName:      v.DiseaseName,
-			DiagnoseId:       v.DiagnoseId,
-			DiagnoseName:     v.DiagnoseName,
-			PenId:            v.PenId,
-			HealthStatus:     v.HealthStatus,
-			HealthStatusName: healthStatusMap[v.HealthStatus],
-			Temperature:      float32(v.HealthStatus / 10),
-			DiseaseAt:        int32(v.DiseaseAt),
-			Remarks:          v.Remarks,
-			OperationId:      v.OperationId,
-			OperationName:    v.OperationName,
+			Id:                    int32(v.Id),
+			CowId:                 int32(v.CowId),
+			EarNumber:             v.EarNumber,
+			Lact:                  v.Lact,
+			DayAge:                v.DayAge,
+			DiseaseId:             v.DiseaseId,
+			DiseaseName:           v.DiseaseName,
+			DiagnoseId:            v.DiagnoseId,
+			DiagnoseName:          v.DiagnoseName,
+			PenId:                 v.PenId,
+			PenName:               v.PenName,
+			HealthStatus:          v.HealthStatus,
+			HealthStatusName:      healthStatusMap[v.HealthStatus],
+			Temperature:           float32(v.HealthStatus / 10),
+			DiseaseAt:             int32(v.DiseaseAt),
+			Remarks:               v.Remarks,
+			OperationId:           v.OperationId,
+			OperationName:         v.OperationName,
+			LastPrescriptionName:  v.LastPrescriptionName,
+			LastTreatedTimeFormat: lastTreatedTimeFormat,
+			OnsetDays:             onsetDays,
 		}
 	}
 	return res

+ 5 - 2
model/event_cow_log.go

@@ -87,6 +87,10 @@ type EventCowLogSlice []*EventCowLog
 func (e EventCowLogSlice) ToPB(eventCategoryMap map[pasturePb.EventCategory_Kind]string) []*pasturePb.CowEvent {
 	res := make([]*pasturePb.CowEvent, len(e))
 	for i, v := range e {
+		eventAtFormat := ""
+		if v.EventAt > 0 {
+			eventAtFormat = time.Unix(v.EventAt, 0).Format(LayoutDate2)
+		}
 		res[i] = &pasturePb.CowEvent{
 			Id:                int32(v.Id),
 			PastureId:         int32(v.PastureId),
@@ -99,11 +103,10 @@ func (e EventCowLogSlice) ToPB(eventCategoryMap map[pasturePb.EventCategory_Kind
 			EventDescription:  v.EventDescription,
 			OperationName:     v.OperationName,
 			Remarks:           v.Remarks,
-			EventAtFormat:     time.Unix(v.EventAt, 0).Format(LayoutDate2),
+			EventAtFormat:     eventAtFormat,
 			EventCategoryKind: v.EventCategoryKind,
 			EventCategoryName: eventCategoryMap[v.EventCategoryKind],
 		}
 	}
-
 	return res
 }

+ 1 - 0
model/event_sale.go

@@ -2,6 +2,7 @@ package model
 
 type EventSale struct {
 	Id            int64  `json:"id"`
+	PastureId     int64  `json:"pastureId"`
 	DealerId      int32  `json:"dealerId"`
 	DealerName    string `json:"dealerName"`
 	VehicleId     int32  `json:"vehicleId"`

+ 31 - 28
model/indicators_data.go

@@ -4,9 +4,10 @@ import (
 	"fmt"
 	"sort"
 
-	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"go.uber.org/zap"
 
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
 )
 
@@ -28,43 +29,45 @@ func (d *IndicatorsData) TableName() string {
 
 type IndicatorsDataSlice []*IndicatorsData
 
-func (i IndicatorsDataSlice) ToPB(detailsMap map[string]*IndicatorsDetails) *IndicatorComparison {
+func (i IndicatorsDataSlice) ToPB(header, kinds []string, detailsMap map[string]*IndicatorsDetails) *IndicatorComparison {
 	res := &IndicatorComparison{
-		Headers: make([]string, 0),
+		Headers: header,
 		List:    make([]map[string]string, 0),
 	}
-	dateSet := make(map[string]struct{}) // 使用空结构体节省内存
-	for _, v := range i {
-		dateSet[v.Date] = struct{}{}
-	}
-	for date := range dateSet {
-		res.Headers = append(res.Headers, date)
+	if len(header) <= 0 || len(detailsMap) <= 0 || len(kinds) <= 0 {
+		return res
 	}
+
 	sort.Strings(res.Headers) // 确保日期顺序一致
-	dataMap := make(map[string]map[string]string)
+	indicatorsDataMap := make(map[string]*IndicatorsData)
 	for _, v := range i {
-		details, ok := detailsMap[v.Kind]
-		if !ok {
-			zaplog.Error("detailsMap", zap.Any("Kind", v.Kind))
-			continue
+		indicatorsDataMap[fmt.Sprintf("%s-%s", v.Kind, v.Date)] = v
+	}
+	dataMap := make(map[string]map[string]string)
+	for _, kind := range kinds {
+		if dataMap[kind] == nil {
+			dataMap[kind] = make(map[string]string)
+		}
+		details := detailsMap[kind]
+		dataMap[kind] = map[string]string{
+			"name":         details.Name,
+			"kind":         details.Kind,
+			"unit":         details.Unit,
+			"categoryType": fmt.Sprintf("%d", details.CategoryType),
+			"categoryName": details.CategoryName,
+			"zh":           details.Zh,
 		}
-		if data, exists := dataMap[details.Kind]; exists {
-			data[v.Date] = v.Value
-		} else {
-			data = make(map[string]string)
-			for _, date := range res.Headers {
-				data[date] = "" // 初始化所有日期为空字符串
+		for _, v := range header {
+			pref := fmt.Sprintf("%s-%s", kind, v)
+			indicators, ok := indicatorsDataMap[pref]
+			if ok {
+				dataMap[kind][v] = indicators.Value
+			} else {
+				dataMap[kind][v] = ""
 			}
-			data[v.Date] = v.Value
-			data["name"] = details.Name
-			data["kind"] = details.Kind
-			data["unit"] = details.Unit
-			data["categoryType"] = fmt.Sprintf("%d", details.CategoryType)
-			data["categoryName"] = details.CategoryName
-			data["zh"] = details.Zh
-			dataMap[details.Kind] = data
 		}
 	}
+	zaplog.Info("dataMap", zap.Any("dataMap", dataMap), zap.Any("header", header), zap.Any("kinds", kinds))
 
 	for _, data := range dataMap {
 		res.List = append(res.List, data)

+ 3 - 3
module/backend/calendar.go

@@ -154,7 +154,6 @@ func (s *StoreEntry) getCalendarCowList(
 		return nil, xerr.New("不支持的日历类型")
 	}
 }
-
 func (s *StoreEntry) ImmunisationCowList(ctx context.Context, req *pasturePb.ItemsRequest, pagination *pasturePb.PaginationModel) (*pasturePb.ImmunizationItemsResponse, error) {
 	userModel, err := s.GetUserModel(ctx)
 	if err != nil {
@@ -162,7 +161,7 @@ func (s *StoreEntry) ImmunisationCowList(ctx context.Context, req *pasturePb.Ite
 	}
 	eventImmunizationPlanList := make([]*model.EventImmunizationPlan, 0)
 	count := int64(0)
-	pref := s.DB.Model(&model.EventImmunizationPlan{}).
+	pref := s.DB.Model(new(model.ImmunizationPlan)).
 		Where("status = ?", pasturePb.IsShow_No).
 		Where("pasture_id = ?", userModel.AppPasture.Id)
 	if req.StartDay != "" {
@@ -495,8 +494,9 @@ func (s *StoreEntry) CalvingCowList(ctx context.Context, req *pasturePb.ItemsReq
 	count := int64(0)
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCalving).TableName())).
 		Select(`a.id,a.cow_id,a.ear_number,a.status,b.breed_status,b.pen_id,DATE_FORMAT(FROM_UNIXTIME(last_mating_at), '%Y-%m-%d') AS mating_at_format,
-		b.day_age,b.last_bull_number as bull_id,DATEDIFF(NOW(),FROM_UNIXTIME(last_mating_at)) AS mating_age,DATE_FORMAT(FROM_UNIXTIME(a.plan_day), '%Y-%m-%d') AS plan_day`).
+		b.day_age,b.last_bull_number as bull_id,b.pen_name,DATEDIFF(NOW(),FROM_UNIXTIME(last_mating_at)) AS mating_age,DATE_FORMAT(FROM_UNIXTIME(a.plan_day), '%Y-%m-%d') AS plan_day`).
 		Joins("left join cow as b on a.cow_id = b.id").
+		Where("a.status = ?", pasturePb.IsShow_No).
 		Where("b.admission_status = ?", pasturePb.AdmissionStatus_Admission).
 		Where("a.pasture_id = ?", userModel.AppPasture.Id)
 

+ 28 - 0
module/backend/config_data_base.go

@@ -1,6 +1,7 @@
 package backend
 
 import (
+	"context"
 	"kpt-pasture/model"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -215,3 +216,30 @@ func (s *StoreEntry) EventCategoryEnumList(isAll string) []*pasturePb.ConfigOpti
 	})
 	return configOptions
 }
+
+func (s *StoreEntry) IndicatorsDetailsList(isAll string) []*pasturePb.ConfigOptionsList {
+	configOptions := make([]*pasturePb.ConfigOptionsList, 0)
+	if isAll == model.IsAllYes {
+		configOptions = append(configOptions,
+			&pasturePb.ConfigOptionsList{
+				Value:    int32(0),
+				Label:    "全部",
+				Disabled: true,
+			})
+	}
+
+	indicatorsDetailsList, _ := s.FindIndicatorsDetailsList(context.Background())
+	if len(indicatorsDetailsList) <= 0 {
+		return configOptions
+	}
+
+	for _, v := range indicatorsDetailsList {
+		configOptions = append(configOptions, &pasturePb.ConfigOptionsList{
+			Label:    v.Name,
+			Disabled: true,
+			Props:    v.Kind,
+		})
+	}
+
+	return configOptions
+}

+ 4 - 0
module/backend/config_data_other.go

@@ -472,6 +472,10 @@ func (s *StoreEntry) EventTypeEnumList(isAll string) []*pasturePb.ConfigOptionsL
 		Value:    int32(pasturePb.EventType_Estrus),
 		Label:    "发情",
 		Disabled: true,
+	}, &pasturePb.ConfigOptionsList{
+		Value:    int32(pasturePb.EventType_Calving),
+		Label:    "产犊",
+		Disabled: true,
 	}, &pasturePb.ConfigOptionsList{
 		Value:    int32(pasturePb.EventType_Seme_Time),
 		Label:    "同期",

+ 12 - 18
module/backend/cow.go

@@ -6,12 +6,10 @@ import (
 	"kpt-pasture/model"
 	"kpt-pasture/util"
 	"net/http"
+	"strconv"
 	"strings"
 	"time"
 
-	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
-	"go.uber.org/zap"
-
 	"gitee.com/xuyiping_admin/pkg/xerr"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -24,7 +22,14 @@ func (s *StoreEntry) Detail(ctx context.Context, req *pasturePb.SearchEventReque
 	}
 
 	cowInfo := make([]*model.Cow, 0)
-	pref := s.DB.Model(new(model.Cow)).Where("pasture_id = ?", userModel.AppPasture.Id).Where("id = ?", req.CowId)
+	pref := s.DB.Model(new(model.Cow)).
+		Where("pasture_id = ?", userModel.AppPasture.Id)
+
+	if req.CowId != "" {
+		cowId, _ := strconv.Atoi(req.CowId)
+		pref.Where("id = ?", cowId)
+	}
+
 	if req.NeckRingNumber != "" {
 		pref.Where("neck_ring_number = ?", req.NeckRingNumber)
 	}
@@ -140,38 +145,27 @@ func (s *StoreEntry) EventList(ctx context.Context, req *pasturePb.SearchCowEven
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
-
-	cow := &model.Cow{}
-	if err = s.DB.Model(cow).
-		Where("pasture_id = ?", userModel.AppPasture.Id).
-		Where("id = ?", req.CowId).First(cow).Error; err != nil {
-		zaplog.Error("EventList", zap.Any("req", req), zap.Any("err", err), zap.Any("userModel", userModel))
-		return nil, xerr.Customf("该牛不存在: %d", req.CowId)
-	}
-
 	eventCowLogList := make([]*model.EventCowLog, 0)
-	eventCowLog := &model.EventCowLog{CowId: cow.Id}
+	eventCowLog := &model.EventCowLog{CowId: int64(req.CowId)}
 	pref := s.DB.Table(eventCowLog.TableName()).
-		Where("cow_id = ?", req.CowId)
+		Where("cow_id = ?", req.CowId).Where("pasture_id = ?", userModel.AppPasture.Id)
 
 	if req.Lact >= 0 {
 		pref.Where("lact = ?", req.Lact)
 	}
 
 	if req.EventCategoryKind > 0 {
-		pref.Where("event_category_id = ?", req.EventCategoryKind)
+		pref.Where("event_type = ?", req.EventCategoryKind)
 	}
 
 	if err = pref.Order("id desc").
 		Limit(int(pagination.PageSize)).
 		Offset(int(pagination.PageOffset)).
 		Find(&eventCowLogList).Error; err != nil {
-		zaplog.Error("EventList", zap.Any("req", req), zap.Any("err", err), zap.Any("userModel", userModel))
 		return nil, xerr.WithStack(err)
 	}
 
 	eventCategoryMap := s.EventCategoryMap()
-
 	return &pasturePb.CowEventListResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",

+ 1 - 1
module/backend/dashboard.go

@@ -118,7 +118,7 @@ func (s *StoreEntry) FocusIndicatorsList(ctx context.Context, dimension string)
 		zaplog.Error("FocusIndicators", zap.Any("err", err))
 	}
 
-	indicatorsDetailsMap, err := s.GetIndicatorsDetailsMap(ctx)
+	indicatorsDetailsMap, _, err := s.GetIndicatorsDetailsMap(ctx)
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}

+ 2 - 1
module/backend/enum_options.go

@@ -209,7 +209,8 @@ func (s *StoreEntry) SystemBaseConfigOptions(ctx context.Context, optionsName, i
 		"departureType":              s.DepartureTypeEnumList,
 		"outReason":                  s.OutReasonEnumList,
 		"deadReason":                 s.DeadReasonEnumList,
-		"eventCategory":              s.EventCategoryEnumList,
+		"categoryKind":               s.EventCategoryEnumList,
+		"indicatorsDetails":          s.IndicatorsDetailsList,
 	}
 
 	getConfigFunc, ok := getConfigFuncMap[optionsName]

+ 3 - 1
module/backend/event_base.go

@@ -423,7 +423,9 @@ func (s *StoreEntry) DepartureBatch(ctx context.Context, req *pasturePb.EventDep
 
 			// 记录事件日志
 			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, item.Cow, eventType, pasturePb.ExposeEstrusType_Invalid, item)
-			tx.Table(cowLogs.TableName()).Create(cowLogs)
+			if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
+				return xerr.WithStack(err)
+			}
 		}
 		return nil
 	}); err != nil {

+ 29 - 26
module/backend/event_breed.go

@@ -78,6 +78,10 @@ func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalv
 		return xerr.Custom("请选择相关牛只")
 	}
 
+	if cow.IsPregnant != pasturePb.IsShow_Ok || cow.BreedStatus != pasturePb.BreedStatus_Pregnant {
+		return xerr.Custom("该母牛未配种")
+	}
+
 	operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId))
 	if err != nil {
 		return xerr.Customf("获取操作人员信息失败: %s", err.Error())
@@ -85,59 +89,45 @@ func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalv
 	req.OperationName = operationUser.Name
 
 	penMap := s.PenMap(ctx, userModel.AppPasture.Id)
-
-	// 记录牛只事件日志
-	defer func() {
-		if err == nil {
-			// 母牛事件日志
-			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Calving, pasturePb.ExposeEstrusType_Invalid, req)
-			s.DB.Table(cowLogs.TableName()).Create(cowLogs)
-			// 犊牛事件日志
-			for _, v := range req.CalfItemList {
-				if v.IsLive == pasturePb.IsShow_No || v.IsAdoption == pasturePb.IsShow_No {
-					continue
-				}
-				cow, _ = s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(v.CowId))
-				cowLogs = s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Birth, pasturePb.ExposeEstrusType_Invalid, v)
-				s.DB.Table(cowLogs.TableName()).Create(cowLogs)
-			}
-		}
-	}()
-
 	newEventCalving := &model.EventCalving{}
 	if err = s.DB.Model(new(model.EventCalving)).
 		Where("cow_id = ?", cow.Id).
 		Where("lact = ?", cow.Lact).
+		Where("status = ?", pasturePb.IsShow_No).
 		First(newEventCalving).Error; err != nil {
 		return xerr.Custom("该母牛信息不存在")
 	}
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		// 更新产犊事件表
-		newEventCalving.EventUpdate(operationUser, req, cow)
+		newEventCalving.EventUpdate(operationUser, userModel.SystemUser, req, cow)
 		if err = tx.Model(new(model.EventCalving)).
-			Select("operation_id", "operation_name", "reality_day", "day_age", "pregnancy_age", "bull_number",
-				"calving_level", "child_number", "status", "is_inducing_childbirth", "pen_id", "dystocia_reason", "remarks").
+			Select("operation_id", "operation_name", "message_id", "message_name", "reality_day", "day_age", "pregnancy_age",
+				"bull_number", "calving_level", "child_number", "status", "is_inducing_childbirth", "pen_id", "dystocia_reason", "remarks").
 			Where("id = ?", newEventCalving.Id).
 			Updates(newEventCalving).Error; err != nil {
 			return xerr.WithStack(err)
 		}
 
-		for _, v := range req.CalfItemList {
-			if v.IsLive == pasturePb.IsShow_No || v.IsAdoption == pasturePb.IsShow_No {
+		calfCowEventLogs := make([]*model.EventCowLog, 0)
+		for _, calf := range req.CalfItemList {
+			if calf.IsLive == pasturePb.IsShow_No || calf.IsAdoption == pasturePb.IsShow_No {
 				continue
 			}
 			// 犊牛信息
-			newCalvingCalf := model.NewEventCalvingCalf(userModel.AppPasture.Id, int64(req.CowId), newEventCalving.Id, int64(req.CalvingAt), penMap, v)
+			newCalvingCalf := model.NewEventCalvingCalf(userModel.AppPasture.Id, int64(req.CowId), newEventCalving.Id, int64(req.CalvingAt), penMap, calf)
 			// 留养犊牛
 			newCow := model.NewCalfCow(cow.Id, cow.LastBullNumber, newCalvingCalf)
 			if err = tx.Create(newCow).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 
-			v.CowId = int32(newCow.Id)
+			calf.CowId = int32(newCow.Id)
 			if err = tx.Create(newCalvingCalf).Error; err != nil {
 				return xerr.WithStack(err)
 			}
+			// 犊牛日志
+			cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, newCow, pasturePb.EventType_Birth, pasturePb.ExposeEstrusType_Invalid, calf)
+			calfCowEventLogs = append(calfCowEventLogs, cowLogs)
 		}
 
 		// 更新母牛信息
@@ -148,6 +138,19 @@ func (s *StoreEntry) CalvingCreate(ctx context.Context, req *pasturePb.EventCalv
 			Updates(cow).Error; err != nil {
 			return xerr.WithStack(err)
 		}
+
+		// 母牛事件日志
+		cowLogs := s.SubmitEventLog(ctx, userModel.AppPasture.Id, cow, pasturePb.EventType_Calving, pasturePb.ExposeEstrusType_Invalid, req)
+		if err = tx.Table(cowLogs.TableName()).Create(cowLogs).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		// 犊牛事件日志
+		if len(calfCowEventLogs) > 0 {
+			if err = tx.Table(cowLogs.TableName()).Create(calfCowEventLogs).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+		}
 		return nil
 	}); err != nil {
 		return xerr.WithStack(err)

+ 4 - 4
module/backend/event_cow_log.go

@@ -84,14 +84,14 @@ func (s *StoreEntry) SubmitEventLog(
 	case pasturePb.EventType_Calving:
 		data := req.(*pasturePb.EventCalving)
 		eventAt = int64(data.CalvingAt)
-		desc = fmt.Sprintf("怀孕天数:%d;难产等级: %s;产子数量: %d", cow.PregnancyAge, s.CalvingLevelMap()[data.CalvingLevel], data.ChildNumber)
+		desc = fmt.Sprintf("怀孕天数:%d;难产等级: %s;产子数量: %d;", cow.PregnancyAge, s.CalvingLevelMap()[data.CalvingLevel], data.ChildNumber)
 		for _, v := range data.CalfItemList {
 			if v.CowId > 0 {
-				desc += fmt.Sprintf(";犊牛ID: %d; 出生体重: %f", v.CowId, v.Weight)
+				desc += fmt.Sprintf("犊牛耳号: %s; 出生体重: %s KG;", v.EarNumber, strconv.FormatFloat(float64(v.Weight), 'f', 2, 64))
 			}
 		}
 		if data.IsInducingChildbirth == pasturePb.IsShow_Ok {
-			desc += fmt.Sprintf("; 难产原因: %s", s.DystociaReasonMap()[data.DystociaReason])
+			desc += fmt.Sprintf("难产原因: %s", s.DystociaReasonMap()[data.DystociaReason])
 		}
 		operationUser.Id = int64(data.OperationId)
 		operationUser.Name = data.OperationName
@@ -112,7 +112,7 @@ func (s *StoreEntry) SubmitEventLog(
 		remarks = data.Remarks
 	case pasturePb.EventType_Birth:
 		eventAt = cow.BirthAt
-		desc = fmt.Sprintf("出生体重: %fKG;母号:%s;父号: %s", float32(cow.BirthWeight)/1000, cow.MotherNumber, cow.LastBullNumber)
+		desc = fmt.Sprintf("出生体重: %s KG;母号:%s;父号: %s", strconv.FormatFloat(float64(cow.BirthWeight)/1000, 'f', 2, 64), cow.MotherNumber, cow.LastBullNumber)
 	case pasturePb.EventType_Death:
 		data := req.(*model.EventDeparture)
 		eventAt = data.DepartureAt

+ 29 - 20
module/backend/event_health.go

@@ -81,6 +81,8 @@ func (s *StoreEntry) CowDiseaseCreate(ctx context.Context, req *pasturePb.EventC
 	if req.PrescriptionId > 0 || len(req.PrescriptionDetail) > 0 {
 		newEventCowDisease.HealthStatus = pasturePb.HealthStatus_Treatment
 		newEventCowDisease.DiagnosedResult = pasturePb.IsShow_Ok
+		newEventCowDisease.FirstTreatmentAt = int64(req.DiseaseAt)
+		newEventCowDisease.LastTreatmentAt = int64(req.DiseaseAt)
 	}
 
 	newEventCowTreatment := &model.EventCowTreatment{}
@@ -104,9 +106,7 @@ func (s *StoreEntry) CowDiseaseCreate(ctx context.Context, req *pasturePb.EventC
 		}
 	}
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
-		if err = tx.Model(new(model.EventCowDisease)).Create(newEventCowDisease).Error; err != nil {
-			return xerr.WithStack(err)
-		}
+		lastPrescriptionName := ""
 		// 已有的处方使用次数+1
 		if req.PrescriptionId > 0 {
 			prescription.EventUseCountUpdate()
@@ -115,6 +115,7 @@ func (s *StoreEntry) CowDiseaseCreate(ctx context.Context, req *pasturePb.EventC
 				Updates(prescription).Error; err != nil {
 				return xerr.WithStack(err)
 			}
+			lastPrescriptionName = prescription.Name
 		}
 		// 新的临时处方
 		if req.PrescriptionId <= 0 && len(req.PrescriptionDetail) > 0 {
@@ -137,12 +138,19 @@ func (s *StoreEntry) CowDiseaseCreate(ctx context.Context, req *pasturePb.EventC
 				return xerr.WithStack(err)
 			}
 			prescription = newPrescription
+			lastPrescriptionName = newPrescription.Name
 
 			newPrescriptionDrugs := model.NewPrescriptionDrugs(userModel.AppPasture.Id, prescription.Id, req.PrescriptionDetail)
 			if err = tx.Model(new(model.PrescriptionDrugs)).Create(newPrescriptionDrugs).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 		}
+
+		// 创建 CowDisease
+		newEventCowDisease.LastPrescriptionName = lastPrescriptionName
+		if err = tx.Model(new(model.EventCowDisease)).Create(newEventCowDisease).Error; err != nil {
+			return xerr.WithStack(err)
+		}
 		// 创建治疗记录
 		if isCreatePrescription {
 			newCowTreatmentRequest = &pasturePb.CowTreatmentRequest{
@@ -245,6 +253,8 @@ func (s *StoreEntry) CowDiseaseList(ctx context.Context, req *pasturePb.SearchEv
 	cowDiseaseList := make([]*model.EventCowDisease, 0)
 	var count int64 = 0
 	pref := s.DB.Table(fmt.Sprintf("%s as a", new(model.EventCowDisease).TableName())).
+		Joins(fmt.Sprintf("JOIN %s AS b on a.cow_id = b.id", new(model.Cow).TableName())).
+		Select("a.*,b.pen_name").
 		Where("a.pasture_id = ?", userModel.AppPasture.Id)
 
 	if len(req.CowIds) > 0 {
@@ -256,15 +266,7 @@ func (s *StoreEntry) CowDiseaseList(ctx context.Context, req *pasturePb.SearchEv
 	}
 
 	if req.PenId > 0 {
-		pref.Where("a.pen_id = ?", req.PenId)
-	}
-
-	if req.Lact > 0 {
-		pref.Where("a.lact = ?", req.Lact)
-	}
-
-	if req.MinDayAge > 0 && req.MaxDayAge > 0 && req.MaxDayAge >= req.MinDayAge {
-		pref.Where("a.day_age BETWEEN ? AND ?", req.MinDayAge, req.MaxDayAge)
+		pref.Where("b.pen_id = ?", req.PenId)
 	}
 
 	if req.HealthStatus > 0 {
@@ -376,8 +378,10 @@ func (s *StoreEntry) CowDiseaseTreatment(ctx context.Context, req *pasturePb.Cow
 		return xerr.WithStack(err)
 	}
 
+	// 牛只疾病信息
 	eventCowDisease := &model.EventCowDisease{}
 	if err = s.DB.Where("cow_id = ?", req.CowId).
+		Where("pasture_id = ?", userModel.AppPasture.Id).
 		Where("id = ?", req.Id).
 		First(eventCowDisease).Error; err != nil {
 		return xerr.WithStack(err)
@@ -388,21 +392,24 @@ func (s *StoreEntry) CowDiseaseTreatment(ctx context.Context, req *pasturePb.Cow
 		return xerr.Custom("异常牛只数据")
 	}
 
+	cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId))
+	if err != nil {
+		return xerr.WithStack(err)
+	}
+
+	// 操作人信息
 	operationUser, err := s.GetSystemUserById(ctx, int64(req.OperationId))
 	if err != nil {
 		return xerr.Customf("操作人数据异常: %d", req.OperationId)
 	}
 
+	// 处方信息
 	prescription, err := s.GetPrescriptionById(ctx, userModel.AppPasture.Id, req.PrescriptionId)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
 
-	cow, err := s.GetCowInfoByCowId(ctx, userModel.AppPasture.Id, int64(req.CowId))
-	if err != nil {
-		return xerr.Customf("异常牛数据: %d", req.CowId)
-	}
-
+	// 疾病信息
 	disease, err := s.GetDiseaseById(ctx, userModel.AppPasture.Id, eventCowDisease.DiseaseId)
 	if err != nil {
 		return xerr.WithStack(err)
@@ -410,11 +417,13 @@ func (s *StoreEntry) CowDiseaseTreatment(ctx context.Context, req *pasturePb.Cow
 	req.DiseaseName = disease.Name
 	req.DiseaseType = disease.DiseaseType
 
+	// 处方信息
 	prescriptionDrugs, err := s.PrescriptionDrugsByPrescriptionId(ctx, userModel.AppPasture.Id, prescription.Id)
 	if err != nil {
 		return xerr.WithStack(err)
 	}
 
+	// 处方详情
 	unitMap := s.UnitMap()
 	prescriptionDetail := make([]*pasturePb.PrescriptionDrugsList, len(prescriptionDrugs))
 	for i, v := range prescriptionDrugs {
@@ -441,14 +450,14 @@ func (s *StoreEntry) CowDiseaseTreatment(ctx context.Context, req *pasturePb.Cow
 	}
 	diseaseTypeMap := s.DiseaseTypeMap()
 	newEventCowTreatment := model.NewEventCowTreatment(userModel.AppPasture.Id, prescription, req, diseaseTypeMap, operationUser, userModel.SystemUser)
+
 	if err = s.DB.Transaction(func(tx *gorm.DB) error {
 		if err = tx.Create(newEventCowTreatment).Error; err != nil {
 			return xerr.WithStack(err)
 		}
-
-		eventCowDisease.EventHealthStatusUpdate(healthStatus)
+		eventCowDisease.EventTreatmentUpdate(healthStatus, int64(req.TreatmentAt), prescription.Name)
 		if err = tx.Model(eventCowDisease).
-			Select("health_status").
+			Select("health_status", "first_treatment_at", "last_treatment_at", "last_prescription_name").
 			Where("id = ?", req.Id).
 			Where("cow_id = ?", req.CowId).
 			Updates(eventCowDisease).Error; err != nil {

+ 28 - 7
module/backend/indicators.go

@@ -3,6 +3,7 @@ package backend
 import (
 	"context"
 	"kpt-pasture/model"
+	"kpt-pasture/util"
 	"net/http"
 
 	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
@@ -17,8 +18,14 @@ func (s *StoreEntry) IndicatorsComparison(ctx context.Context, req *pasturePb.In
 
 	indicatorsDataList := make([]*model.IndicatorsData, 0)
 	pref := s.DB.Model(new(model.IndicatorsData)).
-		Where("pasture_id = ?", userModel.AppPasture.Id).
-		Where("date IN (?)", req.DateList)
+		Where("pasture_id = ?", userModel.AppPasture.Id)
+
+	dataList, err := util.GetMonthsBetween(req.DateList[0], req.DateList[1])
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	pref.Where("date IN (?)", dataList)
+
 	if req.CategoryKind > 0 {
 		pref.Where("category_type = ?", req.CategoryKind)
 	}
@@ -31,27 +38,41 @@ func (s *StoreEntry) IndicatorsComparison(ctx context.Context, req *pasturePb.In
 		return nil, xerr.WithStack(err)
 	}
 
-	indicatorsDetailsMap, err := s.GetIndicatorsDetailsMap(ctx)
+	indicatorsDetailsMap, kinds, err := s.GetIndicatorsDetailsMap(ctx)
+	if len(req.KindList) > 0 {
+		kinds = req.KindList
+	}
+
+	if req.CategoryKind > 0 {
+		for _, v := range indicatorsDetailsMap {
+			if v.CategoryType != req.CategoryKind {
+				delete(indicatorsDetailsMap, v.Kind)
+			}
+		}
+	}
+
 	if err != nil {
 		return nil, xerr.WithStack(err)
 	}
 	return &model.IndicatorsComparisonResponse{
 		Code: http.StatusOK,
 		Msg:  "ok",
-		Data: model.IndicatorsDataSlice(indicatorsDataList).ToPB(indicatorsDetailsMap),
+		Data: model.IndicatorsDataSlice(indicatorsDataList).ToPB(dataList, kinds, indicatorsDetailsMap),
 	}, nil
 }
 
-func (s *StoreEntry) GetIndicatorsDetailsMap(ctx context.Context) (map[string]*model.IndicatorsDetails, error) {
+func (s *StoreEntry) GetIndicatorsDetailsMap(ctx context.Context) (map[string]*model.IndicatorsDetails, []string, error) {
 	indicatorsDetails := make(map[string]*model.IndicatorsDetails)
 	indicatorsDetailsList, err := s.FindIndicatorsDetailsList(ctx)
 	if err != nil {
-		return nil, xerr.WithStack(err)
+		return nil, nil, xerr.WithStack(err)
 	}
+	kinds := make([]string, 0)
 	for _, v := range indicatorsDetailsList {
 		indicatorsDetails[v.Kind] = v
+		kinds = append(kinds, v.Kind)
 	}
-	return indicatorsDetails, err
+	return indicatorsDetails, kinds, err
 }
 
 func (s *StoreEntry) GetIndicatorsDataByDate(pastureId int64, Kind, date string) (*model.IndicatorsData, error) {

+ 6 - 4
module/crontab/cow_cron.go

@@ -355,7 +355,7 @@ func (e *Entry) SystemBasicCrontab() error {
 	currWeekValue := time.Now().Weekday()
 	for _, systemBasic := range systemBasicList {
 		// 周执行
-		if systemBasic.WeekValue >= 0 && time.Weekday(systemBasic.WeekValue) != currWeekValue {
+		if systemBasic.Name == model.PregnantCheckForFirst && systemBasic.WeekValue >= 0 && time.Weekday(systemBasic.WeekValue) != currWeekValue {
 			continue
 		}
 
@@ -369,17 +369,19 @@ func (e *Entry) SystemBasicCrontab() error {
 			pref.Where("breed_status = ?", pasturePb.BreedStatus_Breeding).
 				Where("last_mating_at > ?", 0).
 				Where("DATE(FROM_UNIXTIME(last_mating_at)) BETWEEN DATE_SUB(CURDATE(), INTERVAL ? DAY) AND DATE_SUB(CURDATE(), INTERVAL ? DAY)", systemBasic.MaxValue, systemBasic.MinValue).
-				Where("NOT EXISTS (SELECT 1 FROM event_pregnant_check WHERE event_pregnant_check.cow_id = cow.id AND event_pregnant_check.status = ?)", pasturePb.IsShow_No)
+				Where(`NOT EXISTS (SELECT 1 FROM event_pregnant_check WHERE event_pregnant_check.cow_id = cow.id 
+				AND event_pregnant_check.status = ? AND event_pregnant_check.pregnant_check_name = ?)`, pasturePb.IsShow_No, model.PregnantCheckForFirst)
 		case model.PregnantCheckForSecond: // 复检清单 过滤初检空怀的牛只
 			pref.Where("breed_status IN (?)", []pasturePb.BreedStatus_Kind{pasturePb.BreedStatus_Pregnant}).
 				Where("last_mating_at > ?", 0).
 				Where("DATE(FROM_UNIXTIME(last_mating_at)) = ?", fmt.Sprintf("DATE_SUB(CURDATE(), INTERVAL %d DAY))", systemBasic.MinValue)).
-				Where("NOT EXISTS (SELECT 1 FROM event_pregnant_check WHERE event_pregnant_check.cow_id = cow.id AND event_pregnant_check.status = ?)", pasturePb.IsShow_No)
+				Where(`NOT EXISTS (SELECT 1 FROM event_pregnant_check WHERE event_pregnant_check.cow_id = cow.id 
+				AND event_pregnant_check.status = ? AND event_pregnant_check.pregnant_check_name = ? )`, pasturePb.IsShow_No, model.PregnantCheckForSecond)
 		case model.WeaningAge: // 断奶清单
 			pref.Where("day_age = ?", systemBasic.MinValue).
 				Where("NOT EXISTS (SELECT 1 FROM event_weaning WHERE event_weaning.cow_id = cow.id AND event_weaning.status = ?)", pasturePb.IsShow_No)
 		case model.PregnancyAge: // 产犊清单
-			pref.Where("pregnancy_age = ?", systemBasic.MinValue).
+			pref.Where("pregnancy_age BETWEEN ? AND ?", systemBasic.MinValue, systemBasic.MaxValue).
 				Where("breed_status = ?", pasturePb.BreedStatus_Pregnant).
 				Where("NOT EXISTS (SELECT 1 FROM event_calving WHERE event_calving.cow_id = cow.id AND event_calving.status = ?)", pasturePb.IsShow_No)
 		default:

+ 59 - 4
util/util.go

@@ -350,7 +350,7 @@ func RemoveDuplicates(slice []string) []string {
 	return result
 }
 
-// DaysBetween 计算两个日期之间的天数差
+// DaysBetween 计算两个日期(时间戳)之间的天数差
 func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
 	time1 := time.Unix(startDayUnix, 0)
 	time2 := time.Unix(endDayUnix, 0)
@@ -364,13 +364,14 @@ func DaysBetween(startDayUnix int64, endDayUnix int64) int64 {
 }
 
 // GetDaysBetween 获取两个日期之间的所有天数
+// 2024-10-01 ~ 2024-10-07 => [2024-10-01,2024-10-02,2024-10-03,2024-10-04,2024-10-05,2024-10-06,2024-10-07]
 func GetDaysBetween(startDate, endDate string) ([]string, error) {
 	// 解析日期
-	start, err := time.Parse("2006-01-02", startDate)
+	start, err := time.Parse(Layout, startDate)
 	if err != nil {
 		return nil, fmt.Errorf("解析开始日期失败: %v", err)
 	}
-	end, err := time.Parse("2006-01-02", endDate)
+	end, err := time.Parse(Layout, endDate)
 	if err != nil {
 		return nil, fmt.Errorf("解析结束日期失败: %v", err)
 	}
@@ -385,12 +386,59 @@ func GetDaysBetween(startDate, endDate string) ([]string, error) {
 
 	// 遍历日期
 	for current := start; !current.After(end); current = current.AddDate(0, 0, 1) {
-		days = append(days, current.Format("2006-01-02"))
+		days = append(days, current.Format(Layout))
 	}
 
 	return days, nil
 }
 
+// GetMonthsBetween 返回两个日期之间的所有月份
+// 参数格式为 "YYYY-MM",例如 "2025-01"
+func GetMonthsBetween(start, end string) ([]string, error) {
+	// 检查参数合法性
+	if err := validateDate(start); err != nil {
+		return nil, fmt.Errorf("invalid start date: %v", err)
+	}
+	if err := validateDate(end); err != nil {
+		return nil, fmt.Errorf("invalid end date: %v", err)
+	}
+
+	// 解析起始日期和结束日期
+	startTime, err := time.Parse(LayoutMonth, start)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse start date: %v", err)
+	}
+	endTime, err := time.Parse(LayoutMonth, end)
+	if err != nil {
+		return nil, fmt.Errorf("failed to parse end date: %v", err)
+	}
+
+	// 检查起始日期是否早于结束日期
+	if startTime.After(endTime) {
+		return nil, xerr.Custom("start date must be before or equal to end date")
+	}
+
+	// 生成月份列表
+	var months []string
+	for current := startTime; !current.After(endTime); current = current.AddDate(0, 1, 0) {
+		months = append(months, current.Format(LayoutMonth))
+	}
+
+	return months, nil
+}
+
+// validateDate 检查日期字符串的合法性
+func validateDate(date string) error {
+	if len(date) != 7 || date[4] != '-' {
+		return xerr.Custom("date format must be YYYY-MM")
+	}
+	_, err := time.Parse(LayoutMonth, date)
+	if err != nil {
+		return xerr.Customf("invalid date: %v", err)
+	}
+	return nil
+}
+
 // MsgFormat 格式化消息字符串 字符串里面有多个冒号,仅删除冒号前后的空格(如果存在)
 func MsgFormat(input string) string {
 	// 定义正则表达式,用于匹配冒号两边的空格
@@ -510,3 +558,10 @@ func GetMonthStartAndEndTimestamp() (startTimestamp, endTimestamp int64) {
 
 	return startTimestamp, endTimestamp
 }
+
+// SubDays 计算两个日期(时间戳)之间的天数差
+func SubDays(startDay, endDay int64) int32 {
+	s1 := time.Unix(startDay, 0)
+	s2 := time.Unix(endDay, 0)
+	return int32(s2.Sub(s1).Hours() / 24)
+}

+ 9 - 4
util/util_test.go

@@ -507,8 +507,13 @@ func TestGetNeckRingActiveTimer(t *testing.T) {
 }
 
 func Test_demo(t *testing.T) {
-	oldValue := float64(850)
-	currValue := float64(364)
-	a := RoundToTwoDecimals((oldValue - currValue) / oldValue * 100)
-	fmt.Println(a)
+	start := int64(1739548799)
+	end := int64(1739548800)
+	ad := SubDays(start, end)
+	fmt.Println(ad)
+
+	a := "2025-01-01"
+	b := "2025-03-01"
+
+	fmt.Println(GetDaysBetween(a, b))
 }