Parcourir la source

project: ci-cd2

Yi il y a 1 an
Parent
commit
485cd122fe

+ 1 - 1
Dockerfile

@@ -1,4 +1,4 @@
-FROM alpine:latest
+FROM golang:1.17-alpine3.16 as builder
 
 LABEL name="kpt-tmr" \
 description="pt service" \

+ 0 - 2
http/handle/api/user.go

@@ -42,8 +42,6 @@ type auth struct {
 // @Failure 404 {string} json "{ "code": 404, "data":null,"msg":"请求参数错误"}"
 // @Router /auth  [POST]
 func Auth(c *gin.Context) {
-
-	fmt.Println("=======Auth========", Auth)
 	appG := app.Gin{C: c}
 	var reqInfo auth
 	err := c.BindJSON(&reqInfo)

+ 38 - 28
http/handle/group/feed_formula.go

@@ -2,18 +2,18 @@ package group
 
 import (
 	"net/http"
-	"time"
-	"tmr-watch/http/handle/restful"
 	"tmr-watch/models"
 	"tmr-watch/pkg/app"
 	"tmr-watch/pkg/e"
+	"tmr-watch/service/group"
 
 	"github.com/gin-gonic/gin"
 )
 
+// DistributeFeedFormula 饲料配方下发
 func DistributeFeedFormula(c *gin.Context) {
 	appG := app.Gin{C: c}
-	var req models.PastureBody
+	var req models.PastureBodyRequest
 	if err := c.BindJSON(&req); err != nil {
 		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
 		return
@@ -22,31 +22,7 @@ func DistributeFeedFormula(c *gin.Context) {
 	if len(req.Body) <= 0 {
 		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
 	}
-
-	feedTemplateList := make([]*models.FeedTemplate, len(req.Body))
-	for i, b := range req.Body {
-		feedTemplateList[i] = &models.FeedTemplate{
-			PastureId:    req.PastureId,
-			TCode:        b.EncodeNumber,
-			TName:        b.Name,
-			TColor:       b.Colour,
-			CCid:         int64(b.CattleCategoryId),
-			CCName:       b.CattleCategoryName,
-			FTType:       "",
-			Source:       "",
-			Remark:       b.Remarks,
-			Enable:       1,
-			Weight:       0,
-			DryWeight:    0,
-			Version:      b.Version,
-			SaveTime:     time.Now().Format("2006-01-02 15:04:05"),
-			IsIssue:      0,
-			IssueVersion: 0,
-			IssueId:      0,
-		}
-	}
-
-	if _, err := restful.Engine.Insert(feedTemplateList); err != nil {
+	if err := group.DistributeFeedFormulaService(&req); err != nil {
 		appG.Response(http.StatusBadRequest, e.ERROR_ADD_FAIL, nil)
 		return
 	}
@@ -56,3 +32,37 @@ func DistributeFeedFormula(c *gin.Context) {
 	})
 
 }
+
+// AnalysisAccuracy 准确性分析
+func AnalysisAccuracy(c *gin.Context) {
+	appG := app.Gin{C: c}
+	var req models.AnalysisAccuracyRequest
+	if err := c.BindJSON(&req); err != nil {
+		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
+		return
+	}
+	if res, err := group.AnalysisAccuracyService(&req); err != nil {
+		appG.Response(http.StatusBadRequest, e.ERROR_GET_S_FAIL, map[string]interface{}{
+			"error": err,
+		})
+	} else {
+		appG.Response(http.StatusOK, e.SUCCESS, res)
+	}
+}
+
+// ProcessAnalysis 过程分析
+func ProcessAnalysis(c *gin.Context) {
+	appG := app.Gin{C: c}
+	var req models.AnalysisAccuracyRequest
+	if err := c.BindJSON(&req); err != nil {
+		appG.Response(http.StatusBadRequest, e.INVALID_PARAMS, nil)
+		return
+	}
+	if res, err := group.ProcessAnalysisService(&req); err != nil {
+		appG.Response(http.StatusBadRequest, e.ERROR_GET_S_FAIL, map[string]interface{}{
+			"error": err,
+		})
+	} else {
+		appG.Response(http.StatusOK, e.SUCCESS, res)
+	}
+}

+ 2 - 0
http/routers/group_api.go

@@ -14,5 +14,7 @@ func GroupAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 
 		apiPasture := s.Group("/pasture")
 		apiPasture.POST("feed_formula/distribute", group.DistributeFeedFormula) // 饲料配方下发
+		apiPasture.POST("dashboard/accuracy_data", group.AnalysisAccuracy)      // 混料准确率数据
+		apiPasture.POST("dashboard/process_analysis", group.ProcessAnalysis)    // 过程分析
 	}
 }

+ 68 - 1
models/group_data.go

@@ -1,6 +1,10 @@
 package models
 
-type PastureBody struct {
+import (
+	"time"
+)
+
+type PastureBodyRequest struct {
 	PastureId int64          `json:"pasture_id"`
 	Body      []*FeedFormula `json:"body"`
 }
@@ -26,3 +30,66 @@ type FeedFormula struct {
 	CreatedAt          int64  `json:"created_at"`
 	UpdatedAt          int64  `json:"updated_at"`
 }
+
+type AnalysisAccuracyRequest struct {
+	CattleParentCategoryId int32  `json:"cattle_Parent_Category_Id"`
+	FeedFormulaId          int32  `json:"feed_formula_id"`
+	StartDate              string `json:"start_date"`
+	EndDate                string `json:"end_date"`   // 结束时间
+	PastureId              int32  `json:"pasture_id"` //牧场id
+}
+
+type MixedFodderDataList struct {
+	Iweight int64     `xorm:"iweight" json:"iweight"`
+	Oweight int64     `xorm:"oweight" json:"oweight"`
+	Lweight float64   `xorm:"lweight" json:"lweight"`
+	MyDate  time.Time `xorm:"mydate" json:"mydate"`
+}
+
+type MixedFodderCorrectDataList struct {
+	UseMixedFodderOptionNumber int32  `xorm:"use_mixed_fodder_option_number" json:"use_mixed_fodder_option_number"` // 已混料操作数
+	Date                       string `xorm:"date" json:"date"`                                                     // 日期
+	MixedCorrectNumber         int32  `xorm:"mixed_correct_number" json:"mixed_correct_number"`                     // 混料正确数
+}
+
+type SprinkleFodderCorrectDataList struct {
+	UseSprinkleOptionNumber int32  `xorm:"use_sprinkle_option_number" json:"use_sprinkle_option_number"` // 已撒料操作数
+	Date                    string `xorm:"date" json:"date"`                                             // 日期
+	SprinkleCorrectNumber   int32  `xorm:"sprinkle_correct_number" json:"sprinkle_correct_number"`       // 撒料正确数
+}
+
+type AnalysisAccuracyResponse struct {
+	MixedFodderAccurateRatio    []*PastureAnalysisAccuracyDataValue `json:"mixed_fodder_accurate_ratio"`    // 混料准确率
+	MixedFodderCorrectRatio     []*PastureAnalysisAccuracyDataValue `json:"mixed_fodder_correct_ratio"`     // 混料正确率
+	SprinkleFodderAccurateRatio []*PastureAnalysisAccuracyDataValue `json:"sprinkle_fodder_accurate_ratio"` // 撒料准确率
+	SprinkleFodderCorrectRatio  []*PastureAnalysisAccuracyDataValue `json:"sprinkle_fodder_correct_ratio"`  // 撒料正确率
+}
+
+type PastureAnalysisAccuracyDataValue struct {
+	DayTime string  `json:"day_time"`
+	Ratio   float64 `json:"ratio"`
+}
+
+type ProcessAnalysisResponse struct {
+	AddFeedTime  *ProcessAnalysisDataValue `json:"add_feed_time"` // 加料时间
+	SprinkleTime *ProcessAnalysisDataValue `json:"sprinkle_time"` // 撒料时间
+	StirTime     *ProcessAnalysisDataValue `json:"stir_time"`     // 搅拌延迟时间
+}
+
+type ProcessAnalysisDataValue struct {
+	MaxValue        string `json:"max_value"`         // 最大时间
+	MinValue        string `json:"min_value"`         // 最小时间
+	MiddleValue     string `json:"middle_value"`      // 中位值时间
+	DownMiddleValue string `json:"down_middle_value"` // 下4分位值
+	UpMiddleValue   string `json:"up_middle_value"`   // 上4分位值
+}
+
+type ProcessData struct {
+	Id              int64     `xorm:"id" json:"id"`
+	ExecBeginTime   time.Time `xorm:"exec_begin_time" json:"exec_begin_time"`
+	ExecEndTime     time.Time `xorm:"exec_end_time" json:"exec_end_time"`
+	ExecProcessTime string    `xorm:"exec_process_time" json:"exec_process_time"`
+	L2BeginTime     time.Time `xorm:"l2_begin_time"  json:"l2_begin_time"`
+	L2EndTime       time.Time `xorm:"l2_end_time"  json:"l2_end_time"`
+	L2ProcessTime   string    `xorm:"l2_process_time" json:"l2_process_time"`
+}

+ 129 - 0
module/group.go

@@ -0,0 +1,129 @@
+package module
+
+import (
+	"tmr-watch/http/handle/restful"
+	"tmr-watch/models"
+)
+
+// MixedFodderData 混料准确率和撒料准确率
+func MixedFodderData(req *models.AnalysisAccuracyRequest) ([]*models.MixedFodderDataList, error) {
+	res := make([]*models.MixedFodderDataList, 0)
+	newSql := restful.Engine.NewSession().Table("downloadedplan").Alias("dp").
+		Select("dp.oweight,dp.lweight,dp.iweight,dp.mydate").
+		Join("LEFT", []string{"feedtemplet", "ft"}, "dp.tempid = ft.id").
+		Where("1 = 1")
+
+	if len(req.StartDate) > 0 && len(req.EndDate) > 0 {
+		newSql.And("dp.mydate >= ? and  dp.mydate <= ?", req.StartDate, req.EndDate)
+	}
+
+	if req.CattleParentCategoryId > 0 {
+		newSql.And("ft.ccid = ?", req.CattleParentCategoryId)
+	}
+
+	if req.PastureId > 0 {
+		newSql.And("dp.pastureid = ?", req.PastureId)
+	}
+
+	if req.FeedFormulaId > 0 {
+		newSql.And("dp.tempid = ?", req.FeedFormulaId)
+	}
+	if err := newSql.GroupBy("dp.mydate").OrderBy("dp.mydate").Find(&res); err != nil {
+		return nil, err
+	}
+
+	return res, nil
+}
+
+// mixedFodderCorrectData 混料正确率
+// SELECT
+//SUM(de.`havebuttom`) AS "已混料操作数",de.date,
+//IFNULL(SUM(IF(ABS(de.`actualweightminus`-de.`lweight`)<=de.`feedallowratio` AND de.`actualweightminus`<>0,1,0)),0) AS "混料正确数"
+//FROM downloadplandtl1 de LEFT JOIN feedtemplet ft ON ft.id = de.fid
+//WHERE de.pastureid = 1653271339
+//AND ft.id =20 AND ft.ccid = ?
+//GROUP BY de.date ORDER BY de.date
+func MixedFodderCorrectData(req *models.AnalysisAccuracyRequest) ([]*models.MixedFodderCorrectDataList, error) {
+	res := make([]*models.MixedFodderCorrectDataList, 0)
+	newSql := restful.Engine.NewSession().Table("downloadplandtl1").Alias("de").
+		Select(`SUM(de.havebuttom) AS "use_mixed_fodder_option_number",de.date,IFNULL(SUM(IF(ABS(de.actualweightminus-de.lweight)<=de.feedallowratio AND de.actualweightminus<>0,1,0)),0) AS "mixed_correct_number"`).
+		Join("LEFT", []string{"feedtemplet", "ft"}, "ft.id = de.fid").
+		Where("1 = 1")
+
+	if len(req.StartDate) > 0 && len(req.EndDate) > 0 {
+		newSql.And("de.date >= ? and  de.date <= ?", req.StartDate, req.EndDate)
+	}
+
+	if req.CattleParentCategoryId > 0 {
+		newSql.And("ft.ccid = ?", req.CattleParentCategoryId)
+	}
+
+	if req.PastureId > 0 {
+		newSql.And("de.pastureid = ?", req.PastureId)
+	}
+
+	if req.FeedFormulaId > 0 {
+		newSql.And("de.tempid = ?", req.FeedFormulaId)
+	}
+	if err := newSql.GroupBy("de.date").OrderBy("de.date").Find(&res); err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
+func SprinkleFodderCorrectData(req *models.AnalysisAccuracyRequest) ([]*models.SprinkleFodderCorrectDataList, error) {
+	res := make([]*models.SprinkleFodderCorrectDataList, 0)
+	newSql := restful.Engine.NewSession().Table("downloadplandtl2").Alias("de").
+		Select(`SUM(de.havebuttom) AS "use_sprinkle_option_number",de.date,IFNULL(SUM(IF(ABS(de.actualweightminus-de.lweight)<=de.allowratio AND de.actualweightminus<>0,1,0)),0) AS "sprinkle_correct_number"`).
+		Join("LEFT", []string{"feedtemplet", "ft"}, "ft.id = de.feedtempletid").
+		Where("1 = 1")
+
+	if len(req.StartDate) > 0 && len(req.EndDate) > 0 {
+		newSql.And("de.date >= ? and  de.date <= ?", req.StartDate, req.EndDate)
+	}
+
+	if req.CattleParentCategoryId > 0 {
+		newSql.And("ft.ccid = ?", req.CattleParentCategoryId)
+	}
+
+	if req.PastureId > 0 {
+		newSql.And("de.pastureid = ?", req.PastureId)
+	}
+
+	if req.FeedFormulaId > 0 {
+		newSql.And("de.tempid = ?", req.FeedFormulaId)
+	}
+	if err := newSql.GroupBy("de.date").OrderBy("de.date").Find(&res); err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
+func ProcessAnalysisData(req *models.AnalysisAccuracyRequest) ([]*models.ProcessData, error) {
+	res := make([]*models.ProcessData, 0)
+	newSql := restful.Engine.Table("downloadplandtl1_exec").Alias("e").
+		Select("e.id,e.begintime as exec_begin_time,e.intime as exec_end_time,e.processtime as exec_process_time,l2.begintime as l2_begin_time,l2.intime as l2_end_time,l2.processtime as l2_process_time").
+		Join("LEFT", []string{"downloadplandtl2", "l2"}, "e.pastureid = l2.pastureid and e.pid = l2.pid").
+		Where("1 = 1")
+
+	if len(req.StartDate) > 0 && len(req.EndDate) > 0 {
+		newSql.And("e.date >= ? and  e.date <= ?", req.StartDate, req.EndDate)
+	}
+
+	if req.CattleParentCategoryId > 0 {
+		newSql.And("l2.cowclassid = ?", req.CattleParentCategoryId)
+	}
+
+	if req.PastureId > 0 {
+		newSql.And("e.pastureid = ?", req.PastureId)
+	}
+
+	if req.FeedFormulaId > 0 {
+		newSql.And("l2.feedtempletid = ?", req.FeedFormulaId)
+	}
+	if err := newSql.GroupBy("e.date").OrderBy("e.date").Find(&res); err != nil {
+		return nil, err
+	}
+
+	return res, nil
+}

+ 54 - 1
pkg/util/time.go

@@ -1,6 +1,11 @@
 package util
 
-import "fmt"
+import (
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
 
 /*
 时间常量
@@ -13,6 +18,8 @@ const (
 	SecondsPerHour int64 = SecondsPerMinute * 60
 	//定义每天的秒数
 	SecondsPerDay int64 = SecondsPerHour * 24
+
+	LayoutDateFormat = "2006-01-02"
 )
 
 /*
@@ -54,3 +61,49 @@ func TimeTransformation(unix int64) string {
 	}
 	return now
 }
+
+// TimeBetween 获取指定时间范围内天数
+// eg startTime => "2023-06-01" endTime => "2023-06-03"
+// return ["2023-06-01","2023-06-02","2023-06-03"]
+func TimeBetween(startTime, endTime string) []string {
+	startDate, _ := time.ParseInLocation(LayoutDateFormat, startTime, time.Local)
+	endDate, _ := time.ParseInLocation(LayoutDateFormat, endTime, time.Local)
+	timeList := make([]string, 0)
+	for endDate.After(startDate) {
+		timeList = append(timeList, startDate.Format(LayoutDateFormat))
+		startDate = startDate.AddDate(0, 0, 1)
+	}
+	timeList = append(timeList, endTime)
+	return timeList
+}
+
+// TimeParseToMinutes 时间转换成分钟
+// durationStr := "00:02:30" => 150
+func TimeParseToMinutes(timeStr string) int {
+	durationSlice := strings.Split(timeStr, ":")
+	if len(durationSlice) < 3 {
+		return 0
+	}
+
+	s1, _ := strconv.Atoi(durationSlice[0])
+	s2, _ := strconv.Atoi(durationSlice[1])
+	s3, _ := strconv.Atoi(durationSlice[2])
+	newDurationStr := fmt.Sprintf("%dh%dm%ds", s1, s2, s3)
+	duration, err := time.ParseDuration(newDurationStr)
+	if err != nil {
+		fmt.Println("无法解析时间间隔:", err)
+		return 0
+	}
+
+	return int(duration.Seconds())
+}
+
+// Median 获取切片的中位值
+func Median(nums []int) float64 {
+	n := len(nums)
+	if n%2 == 0 {
+		return float64(nums[n/2-1]+nums[n/2]) / float64(2)
+	} else {
+		return float64(nums[n/2])
+	}
+}

+ 203 - 0
service/group/group.go

@@ -0,0 +1,203 @@
+package group
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+	"time"
+	"tmr-watch/http/handle/restful"
+	"tmr-watch/models"
+	"tmr-watch/module"
+	"tmr-watch/pkg/util"
+)
+
+// DistributeFeedFormulaService 饲料配方下发牧场端
+func DistributeFeedFormulaService(req *models.PastureBodyRequest) error {
+	feedTemplateList := make([]*models.FeedTemplate, len(req.Body))
+	for i, b := range req.Body {
+		feedTemplateList[i] = &models.FeedTemplate{
+			PastureId:    req.PastureId,
+			TCode:        b.EncodeNumber,
+			TName:        b.Name,
+			TColor:       b.Colour,
+			CCid:         int64(b.CattleCategoryId),
+			CCName:       b.CattleCategoryName,
+			FTType:       "",
+			Source:       "",
+			Remark:       b.Remarks,
+			Enable:       1,
+			Weight:       0,
+			DryWeight:    0,
+			Version:      b.Version,
+			SaveTime:     time.Now().Format("2006-01-02 15:04:05"),
+			IsIssue:      0,
+			IssueVersion: 0,
+			IssueId:      0,
+		}
+	}
+
+	if _, err := restful.Engine.Insert(feedTemplateList); err != nil {
+		return err
+	}
+	return nil
+}
+
+// AnalysisAccuracyService 首页准确率分析
+func AnalysisAccuracyService(req *models.AnalysisAccuracyRequest) (*models.AnalysisAccuracyResponse, error) {
+	response := &models.AnalysisAccuracyResponse{
+		MixedFodderAccurateRatio:    make([]*models.PastureAnalysisAccuracyDataValue, 0),
+		MixedFodderCorrectRatio:     make([]*models.PastureAnalysisAccuracyDataValue, 0),
+		SprinkleFodderAccurateRatio: make([]*models.PastureAnalysisAccuracyDataValue, 0),
+		SprinkleFodderCorrectRatio:  make([]*models.PastureAnalysisAccuracyDataValue, 0),
+	}
+
+	// 混料和撒料准确率
+	mixedFodderDataList, err := module.MixedFodderData(req)
+	if err != nil {
+		return nil, err
+	}
+
+	// 混料正确率
+	mixedFodderCorrectDataList, err := module.MixedFodderCorrectData(req)
+	if err != nil {
+		return nil, err
+	}
+
+	// 撒料正确率
+	sprinkleFodderCorrectDataList, err := module.SprinkleFodderCorrectData(req)
+
+	timeList := util.TimeBetween(req.StartDate, req.EndDate)
+	for _, dayTime := range timeList {
+		var mixedInfo, mixedCorrectInfo, sprinkleInfo bool
+		for _, mixed := range mixedFodderDataList {
+			myDate := mixed.MyDate.Format(util.LayoutDateFormat)
+			if myDate != dayTime {
+				continue
+			}
+			// 混料准确率
+			var mixedFodderAccurateRatio float64 = 0
+			if mixed.Lweight > 0 && mixed.Iweight > 0 {
+				mixedFodderAccurateRatio = float64(mixed.Iweight) / mixed.Lweight * 100
+			}
+			mixedValue := &models.PastureAnalysisAccuracyDataValue{
+				DayTime: dayTime,
+				Ratio:   mixedFodderAccurateRatio,
+			}
+			response.MixedFodderAccurateRatio = append(response.MixedFodderAccurateRatio, mixedValue)
+
+			// 撒料准确率
+			var sprinkleFodderAccurateRatio float64 = 0
+			if mixed.Lweight > 0 && mixed.Iweight > 0 {
+				sprinkleFodderAccurateRatio = float64(mixed.Oweight) / mixed.Lweight * 100
+			}
+			sprinkleValue := &models.PastureAnalysisAccuracyDataValue{
+				DayTime: dayTime,
+				Ratio:   sprinkleFodderAccurateRatio,
+			}
+			response.SprinkleFodderAccurateRatio = append(response.SprinkleFodderAccurateRatio, sprinkleValue)
+			mixedInfo = true
+		}
+		if !mixedInfo {
+			noInfo := &models.PastureAnalysisAccuracyDataValue{
+				DayTime: dayTime,
+				Ratio:   0,
+			}
+			response.MixedFodderAccurateRatio = append(response.MixedFodderAccurateRatio, noInfo)
+			response.SprinkleFodderAccurateRatio = append(response.SprinkleFodderAccurateRatio, noInfo)
+		}
+
+		for _, mixedFodder := range mixedFodderCorrectDataList {
+			myDate := strings.ReplaceAll(mixedFodder.Date, "T00:00:00+08:00", "")
+			if myDate != dayTime {
+				continue
+			}
+
+			if mixedFodder.MixedCorrectNumber == 0 || mixedFodder.UseMixedFodderOptionNumber == 0 {
+				response.MixedFodderCorrectRatio = append(response.MixedFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+					DayTime: dayTime,
+					Ratio:   0,
+				})
+			} else {
+				response.MixedFodderCorrectRatio = append(response.MixedFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+					DayTime: dayTime,
+					Ratio:   float64(mixedFodder.MixedCorrectNumber) / float64(mixedFodder.UseMixedFodderOptionNumber) * 100,
+				})
+			}
+			mixedCorrectInfo = true
+		}
+		if !mixedCorrectInfo {
+			response.MixedFodderCorrectRatio = append(response.MixedFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+				DayTime: dayTime,
+				Ratio:   0,
+			})
+		}
+
+		for _, sprinkle := range sprinkleFodderCorrectDataList {
+			myDate := strings.ReplaceAll(sprinkle.Date, "T00:00:00+08:00", "")
+			if myDate != dayTime {
+				continue
+			}
+
+			if sprinkle.SprinkleCorrectNumber == 0 || sprinkle.UseSprinkleOptionNumber == 0 {
+				response.SprinkleFodderCorrectRatio = append(response.SprinkleFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+					DayTime: dayTime,
+					Ratio:   0,
+				})
+			} else {
+				response.SprinkleFodderCorrectRatio = append(response.SprinkleFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+					DayTime: dayTime,
+					Ratio:   float64(sprinkle.SprinkleCorrectNumber) / float64(sprinkle.UseSprinkleOptionNumber) * 100,
+				})
+			}
+			sprinkleInfo = true
+		}
+		if !sprinkleInfo {
+			response.SprinkleFodderCorrectRatio = append(response.SprinkleFodderCorrectRatio, &models.PastureAnalysisAccuracyDataValue{
+				DayTime: dayTime,
+				Ratio:   0,
+			})
+		}
+	}
+	return response, nil
+}
+
+// ProcessAnalysisService 过程分析
+func ProcessAnalysisService(req *models.AnalysisAccuracyRequest) (*models.ProcessAnalysisResponse, error) {
+	response := &models.ProcessAnalysisResponse{
+		AddFeedTime:  &models.ProcessAnalysisDataValue{},
+		SprinkleTime: &models.ProcessAnalysisDataValue{},
+		StirTime:     &models.ProcessAnalysisDataValue{},
+	}
+
+	processDataList, err := module.ProcessAnalysisData(req)
+	if err != nil {
+		return nil, err
+	}
+
+	addFeedList := make([]int, 0)
+	sprinkleFeedList := make([]int, 0)
+	for _, v := range processDataList {
+		addFeedList = append(addFeedList, util.TimeParseToMinutes(v.ExecProcessTime))
+		sprinkleFeedList = append(sprinkleFeedList, util.TimeParseToMinutes(v.L2ProcessTime))
+	}
+
+	if len(addFeedList) > 0 {
+		sort.Ints(addFeedList)
+		response.AddFeedTime.MaxValue = fmt.Sprintf("%.2f", float64(addFeedList[len(addFeedList)-1])/60)
+		response.AddFeedTime.UpMiddleValue = fmt.Sprintf("%.2f", util.Median(addFeedList[len(addFeedList)/2+1:])/60)
+		response.AddFeedTime.MiddleValue = fmt.Sprintf("%.2f", util.Median(addFeedList)/60)
+		response.AddFeedTime.DownMiddleValue = fmt.Sprintf("%.2f", util.Median(addFeedList[0:len(addFeedList)/2])/60)
+		response.AddFeedTime.MinValue = fmt.Sprintf("%.2f", float64(addFeedList[0])/60)
+	}
+
+	if len(sprinkleFeedList) > 0 {
+		sort.Ints(sprinkleFeedList)
+		response.SprinkleTime.MaxValue = fmt.Sprintf("%.2f", float64(sprinkleFeedList[len(sprinkleFeedList)-1])/60)
+		response.SprinkleTime.UpMiddleValue = fmt.Sprintf("%.2f", util.Median(sprinkleFeedList[len(sprinkleFeedList)/2+1:])/60)
+		response.SprinkleTime.MiddleValue = fmt.Sprintf("%.2f", util.Median(sprinkleFeedList)/60)
+		response.SprinkleTime.DownMiddleValue = fmt.Sprintf("%.2f", util.Median(sprinkleFeedList[0:len(sprinkleFeedList)/2])/60)
+		response.SprinkleTime.MinValue = fmt.Sprintf("%.2f", float64(sprinkleFeedList[0])/60)
+	}
+
+	return response, nil
+}