Explorar o código

add:添加保温箱配送

zoie hai 10 meses
pai
achega
a9ee22eeeb
Modificáronse 41 ficheiros con 998 adicións e 169 borrados
  1. 1 1
      app/admin/controller/car.go
  2. 1 1
      app/admin/controller/cooler_box.go
  3. 1 1
      app/admin/controller/warehouse.go
  4. 148 54
      app/admin/controller/waybill.go
  5. 55 0
      app/admin/controller/waybill_task.go
  6. 1 1
      app/admin/model/car.go
  7. 3 3
      app/admin/model/cooler_box.go
  8. 1 0
      app/admin/model/sys_dept.go
  9. 6 6
      app/admin/model/warehouse.go
  10. 2 1
      app/admin/model/waybill.go
  11. 3 1
      app/admin/model/waybill_logistics.go
  12. 1 0
      app/admin/router/car.go
  13. 1 0
      app/admin/router/warehouse.go
  14. 1 0
      app/admin/router/waybill.go
  15. 1 0
      app/admin/router/waybill_task.go
  16. 41 2
      app/admin/service/car.go
  17. 16 0
      app/admin/service/company.go
  18. 39 1
      app/admin/service/cooler_box.go
  19. 13 12
      app/admin/service/dto/car.go
  20. 8 8
      app/admin/service/dto/company.go
  21. 10 9
      app/admin/service/dto/cooler_box.go
  22. 1 1
      app/admin/service/dto/sys_user.go
  23. 8 7
      app/admin/service/dto/warehouse.go
  24. 35 21
      app/admin/service/dto/waybill.go
  25. 2 2
      app/admin/service/dto/waybill_logistics.go
  26. 4 4
      app/admin/service/dto/waybill_task.go
  27. 2 2
      app/admin/service/sys_user.go
  28. 39 1
      app/admin/service/warehouse.go
  29. 212 15
      app/admin/service/waybill.go
  30. 4 1
      app/admin/service/waybill_logistics.go
  31. 26 10
      app/admin/service/waybill_task.go
  32. 1 1
      common/model/byat.go
  33. 52 0
      common/nats/nats_server/NatsColdApi.go
  34. 23 0
      common/nats/nats_server/models.go
  35. 9 0
      common/sf/application_apis.go
  36. 98 0
      common/sf/clent.go
  37. 53 0
      common/sf/model.go
  38. 53 0
      common/sf/order_test.go
  39. 0 1
      conf/settings.yml
  40. 5 0
      go.mod
  41. 18 2
      go.sum

+ 1 - 1
app/admin/controller/car.go

@@ -116,7 +116,7 @@ func (e CarController) Insert(c *gin.Context) {
 	// 设置创建人
 	req.SetCreateBy(user.GetUserId(c))
 	req.SetDeptId(p.DeptId)
-	err = s.Insert(&req)
+	err = s.Insert(&req, p)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return

+ 1 - 1
app/admin/controller/cooler_box.go

@@ -116,7 +116,7 @@ func (e CoolerBoxController) Insert(c *gin.Context) {
 	// 设置创建人
 	req.SetCreateBy(user.GetUserId(c))
 	req.SetDeptId(p.DeptId)
-	err = s.Insert(&req)
+	err = s.Insert(&req, p)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return

+ 1 - 1
app/admin/controller/warehouse.go

@@ -116,7 +116,7 @@ func (e WarehouseController) Insert(c *gin.Context) {
 	// 设置创建人
 	req.SetCreateBy(user.GetUserId(c))
 	req.SetDeptId(p.DeptId)
-	err = s.Insert(&req)
+	err = s.Insert(&req, p)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return

+ 148 - 54
app/admin/controller/waybill.go

@@ -17,6 +17,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin/binding"
 	"github.com/golang/freetype/truetype"
+	"github.com/jordan-wright/email"
 	"github.com/ser163/png2j"
 	"github.com/signintech/gopdf"
 	"github.com/skip2/go-qrcode"
@@ -35,6 +36,7 @@ import (
 	"image/png"
 	"log"
 	"math"
+	"net/smtp"
 	"net/url"
 	"os"
 	"path"
@@ -689,7 +691,7 @@ func (e WaybillController) CarOut(c *gin.Context) {
 // @Security Bearer
 func (e WaybillController) CoolerBoxIn(c *gin.Context) {
 	s := service.Waybill{}
-	req := dto.WaybillInOutReq{}
+	req := dto.WaybillCoolerBoxInReq{}
 	err := e.MakeContext(c).
 		MakeOrm().
 		Bind(&req, binding.JSON, nil).
@@ -1575,19 +1577,31 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 		return
 	}
 	var waybill model.Waybill
-	var company model.SysDept
-	//err = s.GetByWaybillNo(&req, &waybill, p)
 	err = s.GetByWaybillNo(&req, &waybill, nil)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return
 	}
+	var company model.SysDept
 	err = companySvc.Get(&dto.CompanyGetReq{Id: waybill.DeptId}, &company)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return
 	}
-	DeviceSensor_data, Pdf_data, waybillPDF, err := s.GetTwoDeviceSensorData(&dto.WaybillGetByWaybillNoReq{WaybillNo: req.WaybillNo})
+	filename, filePath, err := PDF(s, waybill, company, req.HumidityShow)
+	defer func() {
+		os.Remove(filePath)
+	}()
+
+	c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(filename))
+	c.Header("Content-Transfer-Encoding", "binary")
+	c.File(filePath)
+
+}
+
+func PDF(s service.Waybill, waybill model.Waybill, company model.SysDept, humidityShow bool) (filename, filePath string, err error) {
+
+	DeviceSensor_data, Pdf_data, waybillPDF, err := s.GetTwoDeviceSensorData(&dto.WaybillGetByWaybillNoReq{WaybillNo: waybill.WaybillNo})
 	// 最高温度、最低温度、最高湿度、最低湿度
 	var maxTemp, minTemp, maxHumidity, minHumidity float32
 	// 最高温度时间、最低温度时间、最高湿度时间、最低湿度时间
@@ -1671,40 +1685,52 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 		imgH, errImg := lib.GetImage(company.Logo)
 		if errImg != nil {
 			err = errors.New("获取图片失败")
-			e.Error(500, err, err.Error())
 			return
 		}
 		imgWidth := float64(imgH.Bounds().Dx())
 		imgHeight := float64(imgH.Bounds().Dy())
-		W := 50.
-		H := (imgHeight * W) / imgWidth
+		H := 60.
+		W := (imgWidth * H) / imgHeight
 
 		if strings.Contains(company.Logo, ".png") {
-			err = pdf.ImageFrom(imgH, 10, 5, &gopdf.Rect{W: W, H: H})
+			err = pdf.ImageFrom(imgH, 10, 0, &gopdf.Rect{W: W, H: H})
 			if err != nil {
 				err = errors.New("写入图片失败")
-				e.Error(500, err, err.Error())
 				return
 			}
 		} else {
-			fileName := "./ofile/" + path.Base(company.Logo)
+			fileName := "./ofile/" + time.Now().Format("20060102150405") + ".png"
 			f, _ := os.Create(fileName)
 			defer f.Close()
 			defer func() {
 				os.Remove(fileName)
 			}()
-			jpeg.Encode(f, imgH, &jpeg.Options{100})
-			err = pdf.Image(fileName, 10, 5, &gopdf.Rect{W: W, H: H})
+
+			png.Encode(f, imgH)
+
+			err = pdf.Image(fileName, 10, 0, &gopdf.Rect{W: W, H: H})
 			if err != nil {
-				err = errors.New("写入图片失败")
-				e.Error(500, err, err.Error())
-				return
+				if err.Error() == "16-bit depth not supported" {
+					fileName2 := "./ofile/" + time.Now().Format("20060102150405") + ".png"
+					f2, _ := os.Create(fileName2)
+					defer f.Close()
+					defer func() {
+						os.Remove(fileName2)
+					}()
+					jpeg.Encode(f2, imgH, &jpeg.Options{100})
+					err = pdf.Image(fileName2, 10, 0, &gopdf.Rect{W: W, H: H})
+				}
+				if err != nil {
+					err = errors.New("写入图片失败")
+					return
+				}
+
 			}
 		}
 	}
-	title := "运单" + req.WaybillNo + "温度记录"
-	if req.HumidityShow {
-		title = "运单" + req.WaybillNo + "温湿度记录"
+	title := "运单" + waybill.WaybillNo + "温度记录"
+	if humidityShow {
+		title = "运单" + waybill.WaybillNo + "温湿度记录"
 	}
 	var y float64 = 70
 	textw, _ := pdf.MeasureTextWidth(title)
@@ -1749,7 +1775,7 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 	pdf.SetXY(470, y)
 	pdf.Text(fmt.Sprintf("平均温度:%.1f℃", lib.RoundToDecimal(float64(avgTemp), 1)))
 
-	if req.HumidityShow {
+	if humidityShow {
 		y += 15
 		pdf.SetXY(10, y)
 		pdf.Text(fmt.Sprintf("最高湿度:%.1f%%RH,%s", lib.RoundToDecimal(float64(maxHumidity), 1), maxHumidityTime))
@@ -1767,7 +1793,7 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 	pdf.SetXY(10, y)
 	pdf.Text(fmt.Sprintf("温度阈值:%s℃", tempThreshold))
 
-	if req.HumidityShow {
+	if humidityShow {
 		pdf.SetXY(240, y)
 		pdf.Text(fmt.Sprintf("温度阈值:%s%%", humidityThreshold))
 	}
@@ -1920,8 +1946,8 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 		}
 
 	}
-	filename := "运单" + req.WaybillNo + "温湿度记录" + time.Now().Format("20060102150405") + ".pdf"
-	filePath := "ofile/" + filename
+	filename = "运单" + waybill.WaybillNo + "温度记录" + time.Now().Format("20060102150405") + ".pdf"
+	filePath = "ofile/" + filename
 
 	err = pdf.WritePdf(filePath)
 	if err != nil {
@@ -1929,18 +1955,12 @@ func (e WaybillController) TemperaturePDF(c *gin.Context) {
 	}
 	defer func() {
 		os.Remove(tempFilepath)
-		//os.Remove(humidityFilepath)
-		os.Remove(filePath)
 	}()
-
-	c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(filename))
-	c.Header("Content-Transfer-Encoding", "binary")
-	c.File(filePath)
-
+	return filename, filePath, nil
 }
 
-// WaybillPdf 导出运单pdf
-// // @Summary 导出运单pdf
+// WaybillPDF 导出运单pdf
+// @Summary 导出运单pdf
 // @Description 导出运单pdf
 // @Tags 运单
 // @Param no query string false "运单号"
@@ -2020,6 +2040,22 @@ func (e WaybillController) WaybillPDF(c *gin.Context) {
 		var y float64 = 20
 		pdf.SetFont("wts", "", 10)
 
+		//y += 12
+		pdf.SetXY(250, 20)
+		pdf.SetFont("wts", "", 13)
+		if waybill.Quantity > 1 {
+			pdf.Text(fmt.Sprintf("%d/%d", i+1, waybill.Quantity))
+		}
+		pdf.SetFont("wts", "", 10)
+
+		y += 10
+		pdf.Image(imgPath2, 20, y, &gopdf.Rect{W: 240, H: 30})
+		y += 43
+		textw, _ := pdf.MeasureTextWidth(waybill.WaybillNo)
+		pdf.SetX((283.46 / 2) - (textw / 2))
+		pdf.SetY(y)
+		pdf.Text(waybill.WaybillNo)
+
 		if len(company.Logo) > 0 {
 			imgH, errImg := lib.GetImage(company.Logo)
 			if errImg != nil {
@@ -2029,8 +2065,8 @@ func (e WaybillController) WaybillPDF(c *gin.Context) {
 			}
 			imgWidth := float64(imgH.Bounds().Dx())
 			imgHeight := float64(imgH.Bounds().Dy())
-			W := 30.
-			H := (imgHeight * W) / imgWidth
+			H := 30.
+			W := (imgWidth * H) / imgHeight
 
 			if strings.Contains(company.Logo, ".png") {
 				err = pdf.ImageFrom(imgH, 10, 0, &gopdf.Rect{W: W, H: H})
@@ -2040,40 +2076,40 @@ func (e WaybillController) WaybillPDF(c *gin.Context) {
 					return
 				}
 			} else {
-				fileName := "./ofile/" + path.Base(company.Logo)
+				fileName := "./ofile/" + time.Now().Format("20060102150405") + ".png"
 				f, _ := os.Create(fileName)
 				defer f.Close()
 				defer func() {
 					os.Remove(fileName)
 				}()
-				jpeg.Encode(f, imgH, &jpeg.Options{100})
+
+				png.Encode(f, imgH)
+
 				err = pdf.Image(fileName, 10, 0, &gopdf.Rect{W: W, H: H})
 				if err != nil {
-					err = errors.New("写入图片失败")
-					e.Error(500, err, err.Error())
-					return
+					if err.Error() == "16-bit depth not supported" {
+						fileName2 := "./ofile/" + time.Now().Format("20060102150405") + ".png"
+						f2, _ := os.Create(fileName2)
+						defer f.Close()
+						defer func() {
+							os.Remove(fileName2)
+						}()
+						jpeg.Encode(f2, imgH, &jpeg.Options{100})
+						err = pdf.Image(fileName2, 10, 0, &gopdf.Rect{W: W, H: H})
+					}
+					if err != nil {
+						err = errors.New("写入图片失败")
+						e.Error(500, err, err.Error())
+						return
+					}
 				}
 			}
 		} else {
 			title := "#" + company.Name
-			pdf.SetXY(10, y)
+			pdf.SetXY(10, 20)
 			pdf.Text(title)
 		}
 
-		//y += 12
-		pdf.SetXY(250, 20)
-		pdf.SetFont("wts", "", 13)
-		pdf.Text(fmt.Sprintf("%d/%d", i+1, waybill.Quantity))
-		pdf.SetFont("wts", "", 10)
-
-		y += 10
-		pdf.Image(imgPath2, 20, y, &gopdf.Rect{W: 240, H: 30})
-		y += 43
-		textw, _ := pdf.MeasureTextWidth(waybill.WaybillNo)
-		pdf.SetX((283.46 / 2) - (textw / 2))
-		pdf.SetY(y)
-		pdf.Text(waybill.WaybillNo)
-
 		// 线
 		y += 6
 		pdf.SetLineWidth(0.5)
@@ -2157,7 +2193,7 @@ func (e WaybillController) WaybillPDF(c *gin.Context) {
 
 	}
 
-	filename := "运单" + req.WaybillNo + "温湿度记录" + time.Now().Format("20060102150405") + ".pdf"
+	filename := "运单" + req.WaybillNo + ".pdf"
 	filePath := "ofile/" + filename
 
 	err = pdf.WritePdf(filePath)
@@ -2176,6 +2212,64 @@ func (e WaybillController) WaybillPDF(c *gin.Context) {
 
 }
 
+// SendMail 发送邮件
+// @Summary 通过id获取运单
+// @Description 通过id获取运单
+// @Tags 运单
+// @Param id path string true "运单id"
+// @Success 200 {object} response.Response{data=model.Waybill} "{"code": 200, "data": [...]}"
+// @Router /api/waybill/{id} [get]
+// @Security Bearer
+func (e WaybillController) SendMail(c *gin.Context) {
+	s := service.Waybill{}
+	companySvc := service.Company{}
+	req := dto.WaybillSendMailPdfReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		MakeService(&companySvc.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	var waybill model.Waybill
+	err = s.GetByWaybillNo(&dto.WaybillGetByWaybillPdfReq{WaybillNo: req.WaybillNo, HumidityShow: req.HumidityShow}, &waybill, nil)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	var company model.SysDept
+	err = companySvc.Get(&dto.CompanyGetReq{Id: waybill.DeptId}, &company)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	_, filePath, err := PDF(s, waybill, company, req.HumidityShow)
+	defer func() {
+		os.Remove(filePath)
+	}()
+
+	em := email.NewEmail()
+	em.From = "冷链物流运输 <bzdcold@163.com>"
+	em.To = req.To
+	em.Subject = fmt.Sprintf("【冷链物流运输】运单%s温湿度记录", waybill.WaybillNo)
+	em.Text = []byte(fmt.Sprintf("运单%s温湿度记录", waybill.WaybillNo))
+
+	// 主要就是在这个位置添加了附件,注意:这里AttachFile是Email结构体的方法而不是字段,所以不是用等于而是括号
+	em.AttachFile(filePath)
+	err = em.Send("smtp.163.com:25", smtp.PlainAuth("", "bzdcold@163.com", "ZIZXBCDKVTMPJMVX", "smtp.163.com"))
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	e.OK(nil, "发送成功")
+}
+
 // 获取温度图片
 func DeviceDataTemperatureJPG(startTime, endTime string, waybillPDF []service.WaybillPDF) (string, error) {
 

+ 55 - 0
app/admin/controller/waybill_task.go

@@ -5,6 +5,7 @@ import (
 	"cold-logistics/app/admin/service"
 	"cold-logistics/app/admin/service/dto"
 	"cold-logistics/common/nats/nats_server"
+	"errors"
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin/binding"
 	"gogs.baozhida.cn/zoie/OAuth-core/api"
@@ -83,6 +84,60 @@ func (e WaybillTaskController) GetData(c *gin.Context) {
 	}
 	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
 }
+func (e WaybillTaskController) GetPrintData(c *gin.Context) {
+	s := service.WaybillTask{}
+	waybillSvc := service.Waybill{}
+	companySvc := service.Company{}
+	req := dto.WaybillTaskGetDataPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		MakeService(&companySvc.Service).
+		MakeService(&waybillSvc.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	var waybill model.Waybill
+	var company model.SysDept
+	err = waybillSvc.GetByWaybillNo(&dto.WaybillGetByWaybillPdfReq{WaybillNo: req.WaybillNo}, &waybill, nil)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	err = companySvc.Get(&dto.CompanyGetReq{Id: waybill.DeptId}, &company)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	if company.PrintIntercept == true {
+		var qualified bool
+		qualified, err = waybillSvc.VerifyDataQualified(req.WaybillNo)
+		if err != nil {
+			e.Error(500, err, err.Error())
+			return
+		}
+		if !qualified {
+			err = errors.New("数据不合格,无法打印!")
+			// 错误码 5000 提示无法打印
+			e.Error(5000, err, err.Error())
+			return
+		}
+	}
+	list := make([]nats_server.DeviceData_R, 0)
+	var count int64
+	list, count, err = s.GetDataPage(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
 
 // GetNewestData 获取最新温湿度记录
 // @Summary 获取最新温湿度记录

+ 1 - 1
app/admin/model/car.go

@@ -9,7 +9,7 @@ type Car struct {
 	model2.Model
 	CarNo     string            `json:"carNo" gorm:"size:128"`                                // 商品名称
 	Sn        string            `json:"sn" gorm:"size:128"`                                   // sn
-	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
+	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
 	UserId    int               `json:"userId" gorm:"size:255;"`                              // 司机id
 	HistorySn model2.StringList `json:"historySn"`                                            // 历史绑定的sn
 	User      SysUserOmit       `json:"user"`

+ 3 - 3
app/admin/model/cooler_box.go

@@ -5,9 +5,9 @@ import model2 "cold-logistics/common/model"
 // 仓库
 type CoolerBox struct {
 	model2.Model
-	Name      string            `json:"name" gorm:"size:128"`                                 // 商品名称
+	Name      string            `json:"name" gorm:"size:128"`                                 // 保温箱名称
 	Sn        string            `json:"sn" gorm:"size:128"`                                   // sn
-	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
+	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
 	HistorySn model2.StringList `json:"historySn"`                                            // 历史绑定的sn
 
 	model2.ControlBy
@@ -16,7 +16,7 @@ type CoolerBox struct {
 }
 type CoolerBoxOmit struct {
 	Id   int    `json:"id,omitempty"`   // 主键编码
-	Name string `json:"name,omitempty"` // 商品名称
+	Name string `json:"name,omitempty"` // 保温箱名称
 }
 
 func (CoolerBox) TableName() string {

+ 1 - 0
app/admin/model/sys_dept.go

@@ -22,6 +22,7 @@ type SysDept struct {
 	ColdKey        string `json:"coldKey"  gorm:"size:128;"`               // 冷链3.0key
 	PrintIntercept bool   `json:"printIntercept"`                          // 温湿度打印拦截 默认关闭
 	Logo           string `json:"logo"  gorm:"size:128;"`                  // 公司logo
+	ColdPid        int    `json:"coldPid"  gorm:"size:128;"`               // 冷链3.0公司id
 	model2.ControlBy
 	model2.ModelTime
 }

+ 6 - 6
app/admin/model/warehouse.go

@@ -5,12 +5,12 @@ import model2 "cold-logistics/common/model"
 // 仓库
 type Warehouse struct {
 	model2.Model
-	Name      string            `json:"name" gorm:"size:128"`                                 // 商品名称
-	Sn        string            `json:"sn" gorm:"size:128"`                                   // sn
-	Address   string            `json:"address" gorm:"size:255;"`                             // 地址
-	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
-	UserId    int               `json:"userId" gorm:"size:255;"`                              // 仓管id
-	HistorySn model2.StringList `json:"historySn"`                                            // 历史绑定的sn
+	Name      string            `json:"name" gorm:"size:128"`                                   // 商品名称
+	Sn        string            `json:"sn" gorm:"size:128"`                                     // sn
+	Address   string            `json:"address" gorm:"size:255;"`                               // 地址
+	Status    string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-启用
+	UserId    int               `json:"userId" gorm:"size:255;"`                                // 仓管id
+	HistorySn model2.StringList `json:"historySn"`                                              // 历史绑定的sn
 
 	User SysUserOmit `json:"user"`
 	model2.ControlBy

+ 2 - 1
app/admin/model/waybill.go

@@ -46,7 +46,7 @@ func GetCustomerWaybillStatus(status int) string {
 type Waybill struct {
 	model2.Model
 	WaybillNo string `json:"waybillNo"  gorm:"size:128"` //单号
-	Status    int    `json:"status"  gorm:"size:128"`    //单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
+	Status    int    `json:"status"  gorm:"size:128"`    //单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
 	//SenderAddressId         int         `json:"senderAddressId"  gorm:"size:128"`         //发货地址Id
 	SenderAddressDetails string `json:"senderAddressDetails"  gorm:"size:128"` //发货地址详情
 	SenderAddressName    string `json:"senderAddressName"  gorm:"size:128"`    //发货地址名称
@@ -70,6 +70,7 @@ type Waybill struct {
 	CarId                   int         `json:"carId"  gorm:"size:128"`                   // 仓库id
 	CoolerBoxId             int         `json:"coolerBoxId"  gorm:"size:128"`             // 保温箱id
 	ReceiptImg              string      `json:"ReceiptImg"  gorm:"size:text"`             // 签收图片
+	Qualified               int         `json:"qualified"  gorm:"size:128"`               // 1-合格 2-不合格
 
 	Freight   float64     `json:"freight"  gorm:"size:9"` //运费
 	PrintUser SysUserOmit `json:"printUser" gorm:"->;foreignkey:PrintUserId;references:Id"`

+ 3 - 1
app/admin/model/waybill_logistics.go

@@ -10,7 +10,7 @@ import (
 type WaybillLogistics struct {
 	model2.Model
 	WaybillNo   string        `json:"waybillNo"  gorm:"size:128"`   // 单号
-	Status      int           `json:"status"  gorm:"size:128"`      //单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
+	Status      int           `json:"status"  gorm:"size:128"`      //单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
 	WarehouseId int           `json:"warehouseId"  gorm:"size:128"` // 仓库id
 	CoolerBoxId int           `json:"coolerBoxId"  gorm:"size:128"` // 保温箱id
 	CarId       int           `json:"carId"  gorm:"size:128"`       // 仓库id
@@ -21,6 +21,8 @@ type WaybillLogistics struct {
 	CoolerBox   CoolerBoxOmit `json:"coolerBox" gorm:"->;foreignkey:CoolerBoxId;references:Id"`
 	Warehouse   WarehouseOmit `json:"warehouse" gorm:"->;foreignkey:WarehouseId;references:Id"`
 	Car         CarOmit       `json:"car" gorm:"->;foreignkey:CarId;references:Id"`
+	User        SysUserOmit   `json:"user" gorm:"->;foreignkey:UserId;references:Id"`
+
 	model2.ControlBy
 	model2.ModelTime
 	model2.DeptBy

+ 1 - 0
app/admin/router/car.go

@@ -19,6 +19,7 @@ func registerCarRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware
 		r.GET("", cont.GetPage)
 		r.GET("/:id", cont.Get)
 		r.POST("", cont.Insert)
+		r.POST("/import", cont.BatchInsert)
 		r.PUT("", cont.Update)
 		r.DELETE("", cont.Delete)
 	}

+ 1 - 0
app/admin/router/warehouse.go

@@ -20,6 +20,7 @@ func registerWarehouseRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMidd
 		r.GET("", cont.GetPage)
 		r.GET("/:id", cont.Get)
 		r.POST("", cont.Insert)
+		r.POST("/import", cont.BatchInsert)
 		r.PUT("", cont.Update)
 		r.DELETE("", cont.Delete)
 	}

+ 1 - 0
app/admin/router/waybill.go

@@ -42,5 +42,6 @@ func registerWaybillRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle
 		r.GET("/customer/export", cont.CustomerExport) // 客户下单
 		r.GET("/temperature-pdf", cont.TemperaturePDF) // 导出温湿度pdf
 		r.GET("/waybill-pdf", cont.WaybillPDF)         // 导出运单pdf
+		r.POST("/send-mail", cont.SendMail)            // 发送温湿度邮件
 	}
 }

+ 1 - 0
app/admin/router/waybill_task.go

@@ -17,6 +17,7 @@ func registerWaybillTaskRouter(v1 *gin.RouterGroup) {
 	{
 		r.GET("", cont.GetPage)
 		r.POST("/data", cont.GetData)
+		r.POST("/print-data", cont.GetPrintData)
 		r.GET("/locus", cont.GetLocus)
 		r.POST("/newest-locus", cont.GetNewestData)
 	}

+ 41 - 2
app/admin/service/car.go

@@ -6,6 +6,7 @@ import (
 	"cold-logistics/common/actions"
 	cDto "cold-logistics/common/dto"
 	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
 	"errors"
 	"gogs.baozhida.cn/zoie/OAuth-core/service"
 	"gorm.io/gorm"
@@ -75,7 +76,7 @@ func (e *Car) Get(d *dto.CarGetReq, carModel *model.Car, p *actions.DataPermissi
 }
 
 // Insert 创建Car对象
-func (e *Car) Insert(c *dto.CarInsertReq) error {
+func (e *Car) Insert(c *dto.CarInsertReq, p *actions.DataPermission) error {
 	var err error
 	var data model.Car
 
@@ -118,11 +119,30 @@ func (e *Car) Insert(c *dto.CarInsertReq) error {
 		return global.CreateFailedErr
 	}
 	if k > 0 {
-		err = errors.New("该Sn已绑定其他车辆!")
+		err = errors.New("该SN已绑定其他车辆!")
 		e.Log.Errorf("db error: %s", err)
 		return err
 	}
 
+	var device nats_server.Device_R
+	var company model.SysDept
+	device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+	if err != nil {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	company, err = model.GetCompanyById(p.DeptId)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	if device.T_pid != company.Id {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+		return err
+	}
+
 	// 添加车辆
 	c.Generate(&data)
 	data.HistorySn = []string{data.Sn}
@@ -176,6 +196,25 @@ func (e *Car) Update(c *dto.CarUpdateReq, p *actions.DataPermission) error {
 			return err
 		}
 
+		var device nats_server.Device_R
+		var company model.SysDept
+		device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+		if err != nil {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		company, err = model.GetCompanyById(p.DeptId)
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		if device.T_pid != company.Id {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+			return err
+		}
+
 		carModel.HistorySn = append(carModel.HistorySn, c.Sn)
 	}
 

+ 16 - 0
app/admin/service/company.go

@@ -4,6 +4,7 @@ import (
 	"cold-logistics/common/actions"
 	cDto "cold-logistics/common/dto"
 	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
 	"errors"
 	"gogs.baozhida.cn/zoie/OAuth-core/service"
 	"gorm.io/gorm"
@@ -103,6 +104,13 @@ func (e *Company) Insert(c *dto.CompanyInsertReq) error {
 		return err
 	}
 
+	coldCompany, err := nats_server.Cold_ReadCompanyByT_key(c.ColdKey)
+	if err != nil {
+		err = errors.New("查询公司秘钥信息失败,请检查秘钥是否正确!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	data.ColdPid = coldCompany.Id
 	c.Generate(&data)
 	err = tx.Create(&data).Error
 	if err != nil {
@@ -152,6 +160,14 @@ func (e *Company) Update(c *dto.CompanyUpdateReq) error {
 		return global.UpdateFailedErr
 	}
 
+	coldCompany, err := nats_server.Cold_ReadCompanyByT_key(c.ColdKey)
+	if err != nil {
+		err = errors.New("查询公司秘钥信息失败,请检查秘钥是否正确!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	deptModel.ColdPid = coldCompany.Id
+
 	c.Generate(&deptModel)
 
 	DeptPath := pkg.IntToString(deptModel.Id) + "/"

+ 39 - 1
app/admin/service/cooler_box.go

@@ -6,6 +6,7 @@ import (
 	"cold-logistics/common/actions"
 	cDto "cold-logistics/common/dto"
 	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
 	"errors"
 	"gogs.baozhida.cn/zoie/OAuth-core/service"
 	"gorm.io/gorm"
@@ -61,7 +62,7 @@ func (e *CoolerBox) Get(d *dto.CoolerBoxGetReq, CoolerBoxModel *model.CoolerBox,
 }
 
 // Insert 创建CoolerBox对象
-func (e *CoolerBox) Insert(c *dto.CoolerBoxInsertReq) error {
+func (e *CoolerBox) Insert(c *dto.CoolerBoxInsertReq, p *actions.DataPermission) error {
 	var err error
 	var data model.CoolerBox
 
@@ -86,6 +87,25 @@ func (e *CoolerBox) Insert(c *dto.CoolerBoxInsertReq) error {
 		return err
 	}
 
+	var device nats_server.Device_R
+	var company model.SysDept
+	device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+	if err != nil {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	company, err = model.GetCompanyById(p.DeptId)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	if device.T_pid != company.Id {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+		return err
+	}
+
 	// 添加保温箱
 	c.Generate(&data)
 	data.HistorySn = []string{data.Sn}
@@ -179,6 +199,24 @@ func (e *CoolerBox) Update(c *dto.CoolerBoxUpdateReq, p *actions.DataPermission)
 			e.Log.Errorf("db error: %s", err)
 			return err
 		}
+		var device nats_server.Device_R
+		var company model.SysDept
+		device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+		if err != nil {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		company, err = model.GetCompanyById(p.DeptId)
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		if device.T_pid != company.Id {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+			return err
+		}
 
 		CoolerBoxModel.HistorySn = append(CoolerBoxModel.HistorySn, c.Sn)
 	}

+ 13 - 12
app/admin/service/dto/car.go

@@ -8,9 +8,10 @@ import (
 
 type CarGetPageReq struct {
 	dto.Pagination `search:"-"`
-	Name           string `form:"name" search:"-"`                               // 车牌号
-	Sn             string `form:"sn" search:"type:contains;column:sn;table:car"` // sn
-	IsBind         bool   `form:"isBind" search:"-"`                             // 是否绑定司机
+	Name           string `form:"name" search:"-"`                                    // 车牌号
+	Sn             string `form:"sn" search:"type:contains;column:sn;table:car"`      // sn
+	Status         string `form:"status" search:"type:exact;column:status;table:car"` // 1-停用 2-启用
+	IsBind         bool   `form:"isBind" search:"-"`                                  // 是否绑定司机
 	CarOrder
 }
 
@@ -24,9 +25,9 @@ func (m *CarGetPageReq) GetNeedSearch() interface{} {
 
 type CarInsertReq struct {
 	Id               int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
-	CarNo            string `json:"carNo"`                                // 商品名称
-	Sn               string `json:"sn"`                                   // sn
-	Status           string `json:"status"`                               // 1-停用 2-启用
+	CarNo            string `json:"carNo" vd:"len($)>0;msg:'车牌号不能为空'"`    // 车牌号
+	Sn               string `json:"sn" vd:"len($)>0;msg:'SN不能为空'"`        // sn
+	Status           string `json:"status"`                               // 1-停用 2-启用
 	UserId           int    `json:"userId"`                               // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
@@ -56,11 +57,11 @@ func (s *CarInsertReq) GetId() interface{} {
 }
 
 type CarUpdateReq struct {
-	Id               int    `json:"id" comment:"编码"` // 编码
-	CarNo            string `json:"carNo"`           // 商品名称
-	Sn               string `json:"sn"`              // sn
-	Status           string `json:"status"`          // 1-停用 2-启用
-	UserId           int    `json:"userId"`          // 绑定的用户id
+	Id               int    `json:"id" comment:"编码"`                   // 编码
+	CarNo            string `json:"carNo" vd:"len($)>0;msg:'车牌号不能为空'"` // 车牌号
+	Sn               string `json:"sn" vd:"len($)>0;msg:'SN不能为空'"`     // sn
+	Status           string `json:"status"`                            // 1-停用 2-启用
+	UserId           int    `json:"userId"`                            // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 }
 
@@ -103,7 +104,7 @@ func (s *CarDeleteReq) GetId() interface{} {
 
 type CarBatchInsertReq struct {
 	List             []CarInsertReq
-	Status           string `json:"status"` // 1-停用 2-启用
+	Status           string `json:"status"` // 1-停用 2-启用
 	UserId           int    `json:"userId"` // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`

+ 8 - 8
app/admin/service/dto/company.go

@@ -34,7 +34,7 @@ type CompanyInsertReq struct {
 	Id      int    `json:"id" swaggerignore:"true"`
 	Name    string `json:"name" example:"名称" vd:"len($)>0;msg:'公司名称不能为空'"` //名称
 	Remark  string `json:"remark"`                                         // 备注
-	ColdKey string `json:"coldKey"`                                        // 冷链3.0key
+	ColdKey string `json:"coldKey" vd:"len($)>0;msg:'公司秘钥不能为空'"`           // 冷链3.0key
 
 	common.ControlBy `swaggerignore:"true"`
 }
@@ -55,10 +55,10 @@ func (s *CompanyInsertReq) GetId() interface{} {
 }
 
 type CompanyUpdateReq struct {
-	Id               int    `json:"id" comment:"编码"`   // 编码
-	Name             string `json:"name" example:"名称"` //名称
-	Remark           string `json:"remark"`            // 备注
-	ColdKey          string `json:"coldKey"`           // 冷链3.0key
+	Id               int    `json:"id" comment:"编码"`                                // 编码
+	Name             string `json:"name" example:"名称" vd:"len($)>0;msg:'公司名称不能为空'"` //名称
+	Remark           string `json:"remark"`                                         // 备注
+	ColdKey          string `json:"coldKey" vd:"len($)>0;msg:'公司秘钥不能为空'"`           // 冷链3.0key
 	common.ControlBy `swaggerignore:"true"`
 }
 
@@ -78,9 +78,9 @@ func (s *CompanyUpdateReq) GetId() interface{} {
 }
 
 type MyCompanyUpdateReq struct {
-	Id             int    `json:"id" comment:"编码"`                 // 编码
-	Logo           string `json:"logo" example:"https://xxxx.jpg"` //名称
-	PrintIntercept bool   `json:"printIntercept"`                  // 备注
+	Id               int    `json:"id" comment:"编码"`                 // 编码
+	Logo             string `json:"logo" example:"https://xxxx.jpg"` //名称
+	PrintIntercept   bool   `json:"printIntercept"`                  // 备注
 	common.ControlBy `swaggerignore:"true"`
 }
 

+ 10 - 9
app/admin/service/dto/cooler_box.go

@@ -8,8 +8,9 @@ import (
 
 type CoolerBoxGetPageReq struct {
 	dto.Pagination `search:"-"`
-	Name           string `form:"name" search:"type:contains;column:name;table:cooler_box"` // 保温箱
-	Sn             string `form:"sn" search:"type:contains;column:sn;table:cooler_box"`     // sn
+	Name           string `form:"name" search:"type:contains;column:name;table:cooler_box"`  // 保温箱
+	Sn             string `form:"sn" search:"type:contains;column:sn;table:cooler_box"`      // sn
+	Status         string `form:"status" search:"type:exact;column:status;table:cooler_box"` // 1-停用 2-启用
 	CoolerBoxOrder
 }
 
@@ -23,9 +24,9 @@ func (m *CoolerBoxGetPageReq) GetNeedSearch() interface{} {
 
 type CoolerBoxInsertReq struct {
 	Id               int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
-	Name             string `json:"name"`                                   // 保温箱名称
-	Sn               string `json:"sn"`                                     // sn
-	Status           string `json:"status"`                                 // 1-停用 2-启用
+	Name             string `json:"name"`                                 // 保温箱名称
+	Sn               string `json:"sn"`                                   // sn
+	Status           string `json:"status"`                               // 1-停用 2-启用
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
 }
@@ -54,9 +55,9 @@ func (s *CoolerBoxInsertReq) GetId() interface{} {
 
 type CoolerBoxUpdateReq struct {
 	Id               int    `json:"id" comment:"编码"` // 编码
-	Name             string `json:"Name"`              // 保温箱名称
-	Sn               string `json:"sn"`                // sn
-	Status           string `json:"status"`            // 1-停用 2-启用
+	Name             string `json:"Name"`            // 保温箱名称
+	Sn               string `json:"sn"`              // sn
+	Status           string `json:"status"`          // 1-停用 2-启用
 	common.ControlBy `swaggerignore:"true"`
 }
 
@@ -103,7 +104,7 @@ type DeviceGetPageReq struct {
 
 type CoolerBoxBatchInsertReq struct {
 	List             []CoolerBoxInsertReq
-	Status           string `json:"status"` // 1-停用 2-启用
+	Status           string `json:"status"` // 1-停用 2-启用
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
 }

+ 1 - 1
app/admin/service/dto/sys_user.go

@@ -75,7 +75,7 @@ type SysUserInsertReq struct {
 	Username         string `json:"username" example:"username" vd:"@:len($)>0;msg:'用户名不能为空'"` // 用户名
 	Password         string `json:"password" example:"123456" vd:"@:len($)>5;msg:'密码格式不正确'"`   // 密码
 	Name             string `json:"name" vd:"@:len($)>0;msg:'姓名不能为空'"`                         // 姓名
-	Phone            string `json:"phone"`                                                     // 电话号码
+	Phone            string `json:"phone"  vd:"regexp('^1[3-9]\\d{9}$');msg:'电话号码格式不正确'"`      // 电话号码
 	RoleId           int    `json:"roleId" example:"1" swaggerignore:"true"`                   // 角色id
 	DeptId           int    `json:"deptId" example:"1"`                                        // 机构id
 	Status           string `json:"status" example:"2" swaggerignore:"true"`                   // 状态 	// 货车司机绑定《道路运输从业人员从业资格证》信息

+ 8 - 7
app/admin/service/dto/warehouse.go

@@ -9,9 +9,10 @@ import (
 type WarehouseGetPageReq struct {
 	dto.Pagination `search:"-"`
 	//Name           string `form:"name" search:"type:contains;column:name;table:warehouse"` // 仓库名称
-	Name   string `form:"name" search:"-"`                                     // 仓库名称
-	Sn     string `form:"sn" search:"type:contains;column:sn;table:warehouse"` // sn
-	IsBind bool   `form:"isBind" search:"-"`                                   // 是否绑定司机
+	Name   string `form:"name" search:"-"`                                          // 仓库名称
+	Sn     string `form:"sn" search:"type:contains;column:sn;table:warehouse"`      // sn
+	Status string `form:"status" search:"type:exact;column:status;table:warehouse"` // 1-停用 2-启用
+	IsBind bool   `form:"isBind" search:"-"`                                        // 是否绑定司机
 	WarehouseOrder
 }
 
@@ -25,10 +26,10 @@ func (m *WarehouseGetPageReq) GetNeedSearch() interface{} {
 
 type WarehouseInsertReq struct {
 	Id               int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
-	Name             string `json:"name"`                                 // 商品名称
+	Name             string `json:"name"`                                 // 仓库名称
 	Sn               string `json:"sn"`                                   // sn
 	Address          string `json:"address"`                              // 地址
-	Status           string `json:"status"`                               // 1-停用 2-启用
+	Status           string `json:"status"`                               // 1-停用 2-启用
 	UserId           int    `json:"userId"`                               // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
@@ -63,7 +64,7 @@ type WarehouseUpdateReq struct {
 	Name             string `json:"name"`            // 商品名称
 	Sn               string `json:"sn"`              // sn
 	Address          string `json:"address"`         // 地址
-	Status           string `json:"status"`          // 1-停用 2-启用
+	Status           string `json:"status"`          // 1-停用 2-启用
 	UserId           int    `json:"userId"`          // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 }
@@ -108,7 +109,7 @@ func (s *WarehouseDeleteReq) GetId() interface{} {
 
 type WarehouseBatchInsertReq struct {
 	List             []WarehouseInsertReq
-	Status           string `json:"status"` // 1-停用 2-启用
+	Status           string `json:"status"` // 1-停用 2-启用
 	UserId           int    `json:"userId"` // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`

+ 35 - 21
app/admin/service/dto/waybill.go

@@ -42,23 +42,23 @@ func (m *WaybillGetCustomerPageReq) GetNeedSearch() interface{} {
 }
 
 type WaybillInsertReq struct {
-	Id                      int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
-	No                      string `json:"no" swaggerignore:"true"`              //单号
-	Status                  int    `json:"status" swaggerignore:"true"`          //订单状态:1-待处理;102-待装车 103-运输中 104-已签收
-	SenderAddressDetails    string `json:"senderAddressDetails"`                 //发货地址详情
-	SenderAddressName       string `json:"senderAddressName"`                    //发货地址名称
-	SenderAddressPhone      string `json:"senderAddressPhone"`                   //发货地址电话
-	ConsigneeAddressDetails string `json:"consigneeAddressDetails"`              //收发货地址详情
-	ConsigneeAddressName    string `json:"consigneeAddressName"`                 //收发货地址名称
-	ConsigneeAddressPhone   string `json:"consigneeAddressPhone"`                //收发货地址电话
-	CargoType               string `json:"cargoType"`                            //货物类型
-	TemperatureInterval     string `json:"temperatureInterval"`                  //温度要求
-	DeliveryCondition       string `json:"deliveryCondition"`                    //配送要求
-	Quantity                int    `json:"quantity"`                             //药品数量
-	Remark                  string `json:"remark"`                               //运输备注
-	CustomerId              int    `json:"customerId"`                           //下单客户id
-	CustomerName            string `json:"customerName"`                         //下单客户名称
-	CoolerBoxId             string `json:"coolerBoxId"`                          //保温箱id
+	Id                      int    `json:"id" comment:"编码" swaggerignore:"true"`                                 // 编码
+	No                      string `json:"no" swaggerignore:"true"`                                              //单号
+	Status                  int    `json:"status" swaggerignore:"true"`                                          //运单状态:1-待处理;102-待装车 103-运输中 104-已签收
+	SenderAddressDetails    string `json:"senderAddressDetails"`                                                 //发货地址详情
+	SenderAddressName       string `json:"senderAddressName"`                                                    //发货地址名称
+	SenderAddressPhone      string `json:"senderAddressPhone" vd:"regexp('^1[3-9]\\d{9}$');msg:'寄件人电话格式不正确'"`    //发货地址电话
+	ConsigneeAddressDetails string `json:"consigneeAddressDetails"`                                              //收发货地址详情
+	ConsigneeAddressName    string `json:"consigneeAddressName"`                                                 //收发货地址名称
+	ConsigneeAddressPhone   string `json:"consigneeAddressPhone" vd:"regexp('^1[3-9]\\d{9}$');msg:'收件人电话格式不正确'"` //收发货地址电话
+	CargoType               string `json:"cargoType"`                                                            //货物类型
+	TemperatureInterval     string `json:"temperatureInterval"`                                                  //温度要求
+	DeliveryCondition       string `json:"deliveryCondition"`                                                    //配送要求
+	Quantity                int    `json:"quantity"`                                                             //药品数量
+	Remark                  string `json:"remark"`                                                               //运输备注
+	CustomerId              int    `json:"customerId"`                                                           //下单客户id
+	CustomerName            string `json:"customerName"`                                                         //下单客户名称
+	CoolerBoxId             int    `json:"coolerBoxId"`                                                          //保温箱id
 	model2.ControlBy        `swaggerignore:"true"`
 	model2.DeptBy
 }
@@ -81,6 +81,7 @@ func (s *WaybillInsertReq) Generate(m *model.Waybill) {
 	m.Remark = s.Remark
 	m.CustomerId = s.CustomerId
 	m.CustomerName = s.CustomerName
+	m.CoolerBoxId = s.CoolerBoxId
 	m.OrderTime = model2.Time(time.Now())
 
 	if s.ControlBy.UpdateBy != 0 {
@@ -178,13 +179,19 @@ func (s *WaybillDeleteReq) GetId() interface{} {
 // 运单出入库/上下车
 type WaybillInOutReq struct {
 	StartTime     model2.Time `json:"startTime"`
-	WaybillNoList []string    `json:"waybillNoList"  gorm:"size:128"` // 订单编号
+	WaybillNoList []string    `json:"waybillNoList"` // 运单编号
+}
+
+type WaybillCoolerBoxInReq struct {
+	StartTime     model2.Time `json:"startTime"`
+	WaybillNoList []string    `json:"waybillNoList" ` // 运单编号
+	CoolerBoxId   int         `json:"coolerBoxId"`    // 保温箱id
 }
 
 // 运单签收
 type WaybillReceiptReq struct {
 	StartTime  model2.Time `json:"startTime"`
-	WaybillNo  string      `json:"waybillNo"  gorm:"size:128"`  // 订单编号
+	WaybillNo  string      `json:"waybillNo"  gorm:"size:128"`  // 单编号
 	ReceiptImg string      `json:"receiptImg"  gorm:"size:128"` // 签收图片
 }
 
@@ -217,6 +224,7 @@ type WaybillStatsRes struct {
 	WaitDeliveryNum int64 `json:"waitDeliveryNum"` // 待派单
 	WaitTruckNum    int64 `json:"waitTruckNum"`    // 未装车
 	WaitStorageNum  int64 `json:"waitStorageNum"`  // 未入库
+	WaitVanningNum  int64 `json:"waitVanningNum"`  // 未装箱
 	InDeliveryNum   int64 `json:"inDeliveryNum"`   // 运送中
 	ThisMonthNum    int64 `json:"thisMonthNum"`    // 本月运单数
 	LastMonthNum    int64 `json:"lastMonthNum"`    // 上月运单数
@@ -230,11 +238,17 @@ type WaybillStatsRes struct {
 }
 
 type WaybillGetByWaybillNoReq struct {
-	WaybillNo string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 }
 
 type WaybillGetByWaybillPdfReq struct {
-	WaybillNo    string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo    string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 	HumidityShow bool   `form:"humidityShow" vd:""`                     // 湿度显示
 	//TemplateShow bool   `form:"templateShow" vd:""`                     // 湿度显示
 }
+
+type WaybillSendMailPdfReq struct {
+	WaybillNo    string   `form:"waybillNo" vd:"len($)>0;msg:'运单编号不能为空'"` // 运单编号-必填
+	HumidityShow bool     `form:"humidityShow" vd:""`                     // 湿度显示
+	To           []string `form:"to" vd:""`                               // 收件人
+}

+ 2 - 2
app/admin/service/dto/waybill_logistics.go

@@ -3,13 +3,13 @@ package dto
 // 运单
 
 type WaybillLogisticsGetPageReq struct {
-	WaybillNo string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_logistics" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_logistics" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 	Status    int    `form:"status" search:"type:exact;column:status;table:waybill_logistics"`                                        // 状态
 	WaybillLogisticsOrder
 }
 
 type WaybillLogisticsOrder struct {
-	IdOrder        string `search:"type:order;column:id;table:waybill_logistics" form:"idOrder" default:"desc"`
+	IdOrder string `search:"type:order;column:id;table:waybill_logistics" form:"idOrder" default:"desc"`
 }
 
 func (m *WaybillLogisticsGetPageReq) GetNeedSearch() interface{} {

+ 4 - 4
app/admin/service/dto/waybill_task.go

@@ -6,7 +6,7 @@ import "cold-logistics/common/dto"
 
 type WaybillTaskGetPageReq struct {
 	//dto.Pagination `search:"-"`
-	WaybillNo string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_task" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_task" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 	WaybillTaskOrder
 }
 
@@ -20,7 +20,7 @@ func (m *WaybillTaskGetPageReq) GetNeedSearch() interface{} {
 
 type WaybillTaskGetDataPageReq struct {
 	dto.Pagination `search:"-"`
-	WaybillNo      string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo      string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 	TaskId         int    `form:"taskId"`
 	T_ids          []int  `form:"t_ids"`
 	StartTime      string `form:"startTime"`
@@ -33,7 +33,7 @@ func (m *WaybillTaskGetDataPageReq) GetNeedSearch() interface{} {
 
 // 获取运单轨迹
 type WaybillGetLocusReq struct {
-	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_task" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_task" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 	CreatedAtOrder string `search:"type:order;column:created_at;table:waybill_task" form:"createdAtOrder" default:"asc"`
 }
 
@@ -42,5 +42,5 @@ func (m *WaybillGetLocusReq) GetNeedSearch() interface{} {
 }
 
 type WaybillTaskGetNewestDataPageReq struct {
-	WaybillNo string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
+	WaybillNo string `form:"waybillNo" vd:"len($)>0;msg:'单编号不能为空'"` // 运单编号-必填
 }

+ 2 - 2
app/admin/service/sys_user.go

@@ -266,7 +266,7 @@ func (e *SysUser) Remove(c *dto.SysUserDeleteReq, p *actions.DataPermission) err
 		return global.CreateFailedErr
 	}
 	if j > 0 {
-		err = errors.New("该用户已绑定车辆,禁止删除!")
+		err = errors.New("该用户已绑定车辆,请解绑后删除!")
 		e.Log.Errorf("db error: %s", err)
 		return err
 	}
@@ -279,7 +279,7 @@ func (e *SysUser) Remove(c *dto.SysUserDeleteReq, p *actions.DataPermission) err
 		return global.CreateFailedErr
 	}
 	if k > 0 {
-		err = errors.New("该用户已绑定仓库,禁止删除!")
+		err = errors.New("该用户已绑定仓库,请解绑后删除!")
 		e.Log.Errorf("db error: %s", err)
 		return err
 	}

+ 39 - 1
app/admin/service/warehouse.go

@@ -6,6 +6,7 @@ import (
 	"cold-logistics/common/actions"
 	cDto "cold-logistics/common/dto"
 	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
 	"errors"
 	"gogs.baozhida.cn/zoie/OAuth-core/service"
 	"gorm.io/gorm"
@@ -76,7 +77,7 @@ func (e *Warehouse) Get(d *dto.WarehouseGetReq, warehouseModel *model.Warehouse,
 }
 
 // Insert 创建Warehouse对象
-func (e *Warehouse) Insert(c *dto.WarehouseInsertReq) error {
+func (e *Warehouse) Insert(c *dto.WarehouseInsertReq, p *actions.DataPermission) error {
 	var err error
 	var data model.Warehouse
 
@@ -124,6 +125,25 @@ func (e *Warehouse) Insert(c *dto.WarehouseInsertReq) error {
 		return err
 	}
 
+	var device nats_server.Device_R
+	var company model.SysDept
+	device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+	if err != nil {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	company, err = model.GetCompanyById(p.DeptId)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+	if device.T_pid != company.Id {
+		err = errors.New("获取SN信息失败,请检查SN是否正确!")
+		e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+		return err
+	}
+
 	// 添加仓库
 	c.Generate(&data)
 	data.HistorySn = []string{c.Sn}
@@ -175,6 +195,24 @@ func (e *Warehouse) Update(c *dto.WarehouseUpdateReq, p *actions.DataPermission)
 			e.Log.Errorf("db error: %s", err)
 			return err
 		}
+		var device nats_server.Device_R
+		var company model.SysDept
+		device, err = nats_server.Cold_ReadDeviceByT_sn(c.Sn)
+		if err != nil {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		company, err = model.GetCompanyById(p.DeptId)
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+		if device.T_pid != company.Id {
+			err = errors.New("获取SN信息失败,请检查SN是否正确!")
+			e.Log.Errorf("获取SN信息失败,device.T_pid != company.Id")
+			return err
+		}
 		warehouseModel.HistorySn = append(warehouseModel.HistorySn, c.Sn)
 	}
 

+ 212 - 15
app/admin/service/waybill.go

@@ -28,9 +28,7 @@ func WaybillCustomerStatusScopes(status int) func(db *gorm.DB) *gorm.DB {
 		// 未发货
 		if status == 1 {
 			statusList := []int{
-				model.WaybillStatusWaitDelivery,
 				model.WaybillStatusTruck,
-				model.WaybillStatusWaitStorage,
 			}
 			return db.Where("status in (?)", statusList)
 		}
@@ -41,6 +39,8 @@ func WaybillCustomerStatusScopes(status int) func(db *gorm.DB) *gorm.DB {
 				model.WaybillStatusStorage,
 				model.WaybillStatusTruckOut,
 				model.WaybillStatusStorageOut,
+				model.WaybillStatusVanning,
+				model.WaybillStatusVanningOut,
 			}
 			return db.Where("status in (?)", statusList)
 		}
@@ -51,6 +51,15 @@ func WaybillCustomerStatusScopes(status int) func(db *gorm.DB) *gorm.DB {
 			}
 			return db.Where("status in (?)", statusList)
 		}
+		// 已处理
+		if status == 4 {
+			statusList := []int{
+				model.WaybillStatusWaitDelivery,
+				model.WaybillStatusWaitStorage,
+				model.WaybillStatusWaitVanning,
+			}
+			return db.Where("status in (?)", statusList)
+		}
 		return db
 	}
 
@@ -182,6 +191,7 @@ func (e *Waybill) Get(d *dto.WaybillGetReq, waybillModel *model.Waybill, p *acti
 
 	return nil
 }
+
 func (e *Waybill) GetByWaybillNo(d *dto.WaybillGetByWaybillPdfReq, waybillModel *model.Waybill, p *actions.DataPermission) error {
 	err := e.Orm.
 		Scopes(actions.Permission(waybillModel.TableName(), p)).
@@ -265,7 +275,7 @@ func (e *Waybill) AppletInsert(c *dto.WaybillInsertReq, p *actions.DataPermissio
 		}
 		return global.CreateFailedErr
 	}
-	if (userModel.Type != model.SysUserTypeDriver && userModel.Type != model.SysUserTypeWarehouse) && userModel.UserType != "customer" {
+	if c.CoolerBoxId == 0 && (userModel.Type != model.SysUserTypeDriver && userModel.Type != model.SysUserTypeWarehouse) && userModel.UserType != "customer" {
 		err = errors.New("无权添加!")
 		return err
 	}
@@ -299,8 +309,8 @@ func (e *Waybill) AppletInsert(c *dto.WaybillInsertReq, p *actions.DataPermissio
 	var coolerBox = model.CoolerBox{}
 	if c.DeliveryCondition == "保温箱" {
 		status = model.WaybillStatusWaitVanning
-		// 查询仓库信息
-		err = tx.Scopes(actions.Permission(warehouse.TableName(), p)).
+		// 查询保温箱信息
+		err = tx.Scopes(actions.Permission(coolerBox.TableName(), p)).
 			Where("id = ?", c.CoolerBoxId).
 			First(&coolerBox).Error
 		if err != nil {
@@ -486,6 +496,7 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 
 		if c.Type == model.TypeCoolerBox {
 			waybillModel.Status = model.WaybillStatusWaitVanning
+			waybillModel.CoolerBoxId = coolerBox.Id
 		}
 
 		waybillModel.PrintUserId = c.PrintUserId
@@ -1121,7 +1132,7 @@ func (e *Waybill) CarOut(c *dto.WaybillInOutReq, p *actions.DataPermission) erro
 	return nil
 }
 
-func (e *Waybill) CoolerBoxIn(c *dto.WaybillInOutReq, p *actions.DataPermission) error {
+func (e *Waybill) CoolerBoxIn(c *dto.WaybillCoolerBoxInReq, p *actions.DataPermission) error {
 	var err error
 
 	tx := e.Orm.Begin()
@@ -1146,7 +1157,9 @@ func (e *Waybill) CoolerBoxIn(c *dto.WaybillInOutReq, p *actions.DataPermission)
 			}
 			return errors.New(fmt.Sprintf("运单号%s查询失败", waybillNo))
 		}
-		if waybillModel.Status != model.WaybillStatusWaitVanning {
+		if waybillModel.Status != model.WaybillStatusWaitVanning &&
+			waybillModel.Status != model.WaybillStatusStorage &&
+			waybillModel.Status != model.WaybillStatusTruck {
 			err = errors.New(fmt.Sprintf("运单号%s状态为%s,无法装箱!", waybillNo, model.WaybillStatusMap[waybillModel.Status]))
 			return err
 		}
@@ -1155,11 +1168,11 @@ func (e *Waybill) CoolerBoxIn(c *dto.WaybillInOutReq, p *actions.DataPermission)
 		var coolerBox = model.CoolerBox{}
 		// 查询运单是否存在
 		err = tx.Scopes(actions.Permission(coolerBox.TableName(), p)).
-			Where("user_id = ?", p.UserId).
+			Where("id = ?", c.CoolerBoxId).
 			First(&coolerBox).Error
 		if err != nil {
 			e.Log.Errorf("db error: %s", err)
-			return errors.New("获取车辆绑定信息失败")
+			return errors.New("获取保温箱信息失败")
 		}
 
 		// 验证时间
@@ -1177,6 +1190,105 @@ func (e *Waybill) CoolerBoxIn(c *dto.WaybillInOutReq, p *actions.DataPermission)
 			return err
 		}
 
+		// 如果上一个状态为已装车,则自动下车
+		if waybillModel.Status == model.WaybillStatusTruck {
+			// 已装车
+			var car model.Car
+			err = e.Orm.First(&car, waybillModel.CarId).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("查询仓库信息失败:%s", err))
+			}
+			// 添加物流
+			Logistics := model.WaybillLogistics{
+				WaybillNo: waybillNo,
+				Status:    model.WaybillStatusTruckOut,
+				CarId:     waybillModel.CarId,
+				UserId:    car.UserId,
+				Lng:       lng,
+				Lat:       lat,
+				ControlBy: model2.ControlBy{
+					CreateBy: p.UserId,
+				},
+				DeptBy: model2.DeptBy{
+					DeptId: car.DeptId,
+				},
+				ModelTime: model2.ModelTime{
+					CreatedAt: c.StartTime,
+				},
+			}
+			err = tx.Create(&Logistics).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单物流信息失败:%s", err))
+			}
+
+			// 查询任务
+			var task model.WaybillTask
+			err = tx.Model(&task).Where("waybill_no = ? and car_id = ?", waybillNo, waybillModel.CarId).
+				Last(&task).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("查询运单任务信息失败:%s", err))
+			}
+			task.EndTime = c.StartTime
+			task.UpdateBy = p.UserId
+			err = tx.Save(&task).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单任务信息失败:%s", err))
+			}
+		}
+
+		// 如果上一个状态为入库,则自动出库
+		if waybillModel.Status == model.WaybillStatusStorage {
+			var warehouse model.Warehouse
+			err = e.Orm.First(&warehouse, waybillModel.WarehouseId).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("查询仓库信息失败:%s", err))
+			}
+			// 添加物流
+			Logistics := model.WaybillLogistics{
+				WaybillNo:   waybillNo,
+				Status:      model.WaybillStatusStorageOut,
+				WarehouseId: waybillModel.WarehouseId,
+				UserId:      warehouse.UserId,
+				Lng:         lng,
+				Lat:         lat,
+				ControlBy: model2.ControlBy{
+					CreateBy: p.UserId,
+				},
+				DeptBy: model2.DeptBy{
+					DeptId: warehouse.DeptId,
+				},
+				ModelTime: model2.ModelTime{
+					CreatedAt: c.StartTime,
+				},
+			}
+			err = tx.Create(&Logistics).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单物流信息失败:%s", err))
+			}
+
+			// 查询任务
+			var task model.WaybillTask
+			err = tx.Model(&task).Where("waybill_no = ? and warehouse_id = ?", waybillNo, waybillModel.WarehouseId).
+				Last(&task).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("查询运单任务信息失败:%s", err))
+			}
+			task.EndTime = c.StartTime
+			task.UpdateBy = p.UserId
+			err = tx.Save(&task).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单任务信息失败:%s", err))
+			}
+		}
+
 		waybillModel.Status = model.WaybillStatusVanning
 		waybillModel.CoolerBoxId = coolerBox.Id
 		waybillModel.DeliveryTime = c.StartTime
@@ -1189,7 +1301,7 @@ func (e *Waybill) CoolerBoxIn(c *dto.WaybillInOutReq, p *actions.DataPermission)
 		// 添加物流
 		Logistics := model.WaybillLogistics{
 			WaybillNo:   waybillNo,
-			Status:      model.WaybillStatusTruck,
+			Status:      model.WaybillStatusVanning,
 			CoolerBoxId: coolerBox.Id,
 			UserId:      p.UserId,
 			Lng:         lng,
@@ -1302,7 +1414,7 @@ func (e *Waybill) Receipt(c *dto.WaybillReceiptReq, p *actions.DataPermission) e
 		}
 		// 未出箱 直接点签收
 		if time.Time(task.EndTime).IsZero() {
-			task.EndTime = model2.Time(time.Now())
+			task.EndTime = c.StartTime
 			task.UpdateBy = p.UserId
 			err = tx.Save(&task).Error
 			if err != nil {
@@ -1399,7 +1511,7 @@ func (e *Waybill) Receipt(c *dto.WaybillReceiptReq, p *actions.DataPermission) e
 	}
 	// 未下车 直接点签收
 	if time.Time(task.EndTime).IsZero() {
-		task.EndTime = model2.Time(time.Now())
+		task.EndTime = c.StartTime
 		task.UpdateBy = p.UserId
 		err = tx.Save(&task).Error
 		if err != nil {
@@ -1522,8 +1634,14 @@ func (e *Waybill) GetBasicsStats(c *dto.WaybillStatsReq, p *actions.DataPermissi
 	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p)).Where("status = ?", model.WaybillStatusWaitTruck).Count(&res.WaitTruckNum)
 	// 未入库
 	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p)).Where("status = ?", model.WaybillStatusWaitStorage).Count(&res.WaitStorageNum)
+	// 未装箱
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p)).Where("status = ?", model.WaybillStatusWaitVanning).Count(&res.WaitVanningNum)
 	// 配送中
-	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p)).Where("status in (?)", []int{model.WaybillStatusTruck, model.WaybillStatusStorage, model.WaybillStatusTruckOut, model.WaybillStatusStorageOut}).Count(&res.InDeliveryNum)
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p)).Where("status in (?)",
+		[]int{model.WaybillStatusTruck, model.WaybillStatusStorage,
+			model.WaybillStatusTruckOut, model.WaybillStatusStorageOut,
+			model.WaybillStatusVanning, model.WaybillStatusVanningOut,
+		}).Count(&res.InDeliveryNum)
 	// 获取本月,上月数据
 	e.Orm.Model(&data).Select("date_format(order_time,'%Y%m') date,count(1) as count ").Scopes(actions.Permission(data.TableName(), p)).
 		Where("order_time between ? and ?", monthStartTime, now).Group("date").Find(&monthCount)
@@ -1563,7 +1681,7 @@ func (e *Waybill) GetBasicsStats(c *dto.WaybillStatsReq, p *actions.DataPermissi
 
 	if c.Type == "month" {
 		// 获取上个月第一天
-		month, _ := time.Parse("2006-01", c.Date)
+		month, _ := time.ParseInLocation("2006-01", c.Date, time.Local)
 		firstDayOfMonth := time.Date(month.Year(), month.Month(), 1, 0, 0, 0, 0, now.Location())
 		lastDayOfMonth := time.Date(month.Year(), month.Month()+1, 1, 23, 59, 59, 0, now.Location()).Add(-time.Hour * 24)
 		e.Orm.Model(&data).Select("date_format(order_time,'%Y-%m-%d') date,count(1) as num ").Scopes(actions.Permission(data.TableName(), p)).
@@ -1573,7 +1691,7 @@ func (e *Waybill) GetBasicsStats(c *dto.WaybillStatsReq, p *actions.DataPermissi
 	return res
 }
 
-// 获取运单所有温湿度素具
+// 获取运单所有温湿度数据
 func (e *Waybill) GetAllData(c *dto.WaybillGetByWaybillNoReq) ([]nats_server.DeviceData_R, []WaybillPDF, error) {
 	var err error
 	var data model.WaybillTask
@@ -1885,3 +2003,82 @@ func DeviceSensorDataListToDeviceDataPdfList(list []nats_server.DeviceData_R, T_
 	return finalList
 
 }
+
+// 获取运单所有温湿度数据
+func (e *Waybill) VerifyDataQualified(waybillNo string) (qualified bool, err error) {
+	var data model.WaybillTask
+	var waybill model.Waybill
+	var taskList []model.WaybillTask
+
+	err = e.Orm.Model(&waybill).Where("waybill_no = ?", waybillNo).First(&waybill).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return qualified, errors.New("获取运单信息失败")
+	}
+
+	// 未签收,不返回数据
+	if waybill.Status != model.WaybillStatusReceipt {
+		return qualified, nil
+	}
+
+	// 获取公司秘钥
+	var company model.SysDept
+	company, err = model.GetCompanyById(waybill.DeptId)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return qualified, model.GetCompanyKeyErr
+	}
+
+	err = e.Orm.Model(&data).
+		Where("waybill_no = ?", waybillNo).
+		Order("id desc").
+		Find(&taskList).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return qualified, global.GetFailedErr
+	}
+
+	// 获取最后一个任务id
+	var lastWaybillTask model.WaybillTask
+	err = e.Orm.Model(&lastWaybillTask).Where("waybill_no = ?", waybillNo).Last(&lastWaybillTask).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		err = errors.New("获取运单信息错误!")
+		return qualified, err
+	}
+
+	// 创建名称到权重的映射
+	orderMap := make(map[string]int)
+	for i, v := range taskList {
+		orderMap[v.Sn] = i
+	}
+
+	for i := 0; i < len(taskList); i++ {
+		// 获取传感器信息
+		deviceSensorList, _, _ := nats_server.Cold_CompanyDeviceSensor_List_ByKey(taskList[i].Sn, company.ColdKey)
+		var T_snid string
+		deviceSensorParameterMap := map[string]nats_server.DeviceSensorParameter_R{}
+		for _, r := range deviceSensorList {
+			T_snid += fmt.Sprintf("%s,%d|", r.T_sn, r.T_id)
+			deviceSensorParameterMap[fmt.Sprintf("%s-%d", r.T_sn, r.T_id)] = r.T_DeviceSensorParameter
+		}
+		var list []nats_server.DeviceData_R
+		list, _, err = nats_server.Cold_ReadDeviceDataListBy_T_snid(T_snid, taskList[i].StartTime.String(), taskList[i].EndTime.String(), 0, 9999)
+		if err != nil {
+			e.Log.Errorf("nats 获取温湿度信息失败: %s", err)
+			return qualified, global.GetFailedErr
+		}
+
+		// 判断数据是否合格
+		qualified = true
+		for _, v := range list {
+			dsp := deviceSensorParameterMap[fmt.Sprintf("%s-%d", v.T_sn, v.T_id)]
+			if v.T_t < dsp.T_Tlower || v.T_t > dsp.T_Tupper || v.T_rh < dsp.T_RHlower || v.T_t > dsp.T_RHupper {
+				qualified = false
+				break
+			}
+		}
+	}
+
+	return qualified, nil
+}

+ 4 - 1
app/admin/service/waybill_logistics.go

@@ -22,6 +22,8 @@ func (e *WaybillLogistics) GetPage(c *dto.WaybillLogisticsGetPageReq, list *[]mo
 		model.WaybillStatusStorage,
 		model.WaybillStatusTruckOut,
 		model.WaybillStatusStorageOut,
+		model.WaybillStatusVanning,
+		model.WaybillStatusVanningOut,
 		model.WaybillStatusReceipt,
 	}
 	err = e.Orm.Model(&data).
@@ -29,7 +31,8 @@ func (e *WaybillLogistics) GetPage(c *dto.WaybillLogisticsGetPageReq, list *[]mo
 			cDto.MakeCondition(c.GetNeedSearch()),
 		).
 		Where("status in (?)", status).
-		Preload("Warehouse.User").Preload("Car.User").
+		Preload("User").
+		Preload("Warehouse.User").Preload("Car.User").Preload("CoolerBox").
 		Find(list).
 		Count(count).Error
 	if err != nil {

+ 26 - 10
app/admin/service/waybill_task.go

@@ -44,7 +44,7 @@ func (e *WaybillTask) GetPage(c *dto.WaybillTaskGetPageReq, list *[]model.Waybil
 		Scopes(
 			cDto.MakeCondition(c.GetNeedSearch()),
 		).
-		Preload("Warehouse").Preload("Car").
+		Preload("Warehouse").Preload("Car").Preload("CoolerBox").
 		Find(list).Limit(-1).Offset(-1).
 		Count(count).Error
 	if err != nil {
@@ -93,28 +93,38 @@ func (e *WaybillTask) GetDataPage(c *dto.WaybillTaskGetDataPageReq) (list []nats
 		err = errors.New("获取运单信息错误!")
 		return
 	}
+	var afterStartTime bool
+	var beforeEndTime bool
 	if len(c.StartTime) > 0 {
 		var st time.Time
-		st, err = time.Parse("2006-01-02 15:04:05", c.StartTime)
+		st, err = time.ParseInLocation("2006-01-02 15:04:05", c.StartTime, time.Local)
 		if err != nil {
 			err = errors.New("解析时间字符串出错!")
 			return
 		}
-		if st.Before(time.Time(task.StartTime)) {
+		fmt.Println("st", st)
+		fmt.Println("task.StartTime", task.StartTime.Local())
+		if task.StartTime.Local().After(st) || st.Truncate(time.Second).Equal(task.StartTime.Local().Truncate(time.Second)) {
 			c.StartTime = task.StartTime.String()
+		} else {
+			afterStartTime = true
 		}
 	} else {
 		c.StartTime = task.StartTime.String()
 	}
 	if len(c.EndTime) > 0 {
 		var et time.Time
-		et, err = time.Parse("2006-01-02 15:04:05", c.EndTime)
+		et, err = time.ParseInLocation("2006-01-02 15:04:05", c.EndTime, time.Local)
 		if err != nil {
 			err = errors.New("解析时间字符串出错!")
 			return
 		}
-		if et.After(time.Time(task.EndTime)) {
+		fmt.Println("et", et)
+		fmt.Println("task.EndTime", task.EndTime.Local())
+		if et.After(task.EndTime.Local()) || et.Truncate(time.Second).Equal(task.EndTime.Local().Truncate(time.Second)) {
 			c.EndTime = task.EndTime.String()
+		} else {
+			beforeEndTime = true
 		}
 	} else {
 		c.EndTime = task.EndTime.String()
@@ -131,9 +141,12 @@ func (e *WaybillTask) GetDataPage(c *dto.WaybillTaskGetDataPageReq) (list []nats
 		for _, v := range c.T_ids {
 
 			for i := 0; i < len(list); i++ {
+				if afterStartTime {
+					break
+				}
 				if v == list[i].T_id {
 					if list[i].T_time != task.StartTime.String() {
-						data := list[i]
+						data := list[len(list)-1-i]
 						data.T_time = task.StartTime.String()
 						firstMap[v] = data
 						count += 1
@@ -143,9 +156,12 @@ func (e *WaybillTask) GetDataPage(c *dto.WaybillTaskGetDataPageReq) (list []nats
 			}
 			if waybill.Status == model.WaybillStatusReceipt {
 				for i := len(list) - 1; i >= 0; i-- {
+					if beforeEndTime {
+						break
+					}
 					if v == list[i].T_id {
-						if c.TaskId == lastWaybillTask.Id && list[i].T_time != task.EndTime.String() && !time.Time(task.EndTime).IsZero() {
-							data := list[i]
+						if c.TaskId == lastWaybillTask.Id && list[i].T_time != task.EndTime.String() && !task.EndTime.Local().IsZero() {
+							data := list[len(list)-1-i]
 							data.T_time = task.EndTime.String()
 							lastMap[v] = data
 							count += 1
@@ -178,7 +194,7 @@ func (e *WaybillTask) GetDataPage(c *dto.WaybillTaskGetDataPageReq) (list []nats
 
 	// 如果n超出范围,返回错误
 	if c.Page > totalPages {
-		c.Page = totalPages
+		return []nats_server.DeviceData_R{}, count, err
 	}
 	if c.Page < 1 {
 		c.Page = 1
@@ -272,7 +288,7 @@ func (e *WaybillTask) GetLocus(c *dto.WaybillGetLocusReq) ([]nats_server.DeviceD
 	err = e.Orm.Model(&data).
 		Scopes(
 			cDto.MakeCondition(c.GetNeedSearch()),
-		).Where("car_id > 0").
+		).Where("car_id > 0 or cooler_box_id > 0 ").
 		Find(&taskList).Error
 	if err != nil {
 		e.Log.Errorf("db error: %s", err)

+ 1 - 1
common/model/byat.go

@@ -75,7 +75,7 @@ func (t Time) String() string {
 	return time.Time(t).Format(timeFormat)
 }
 
-func (t Time) local() time.Time {
+func (t Time) Local() time.Time {
 	loc, _ := time.LoadLocation(timezone)
 	return time.Time(t).In(loc)
 }

+ 52 - 0
common/nats/nats_server/NatsColdApi.go

@@ -216,3 +216,55 @@ func Cold_ReadDevice_List(name, T_key string, page, page_z int) (data []Device_R
 
 	return list, t_R.Count, nil
 }
+func Cold_ReadDeviceByT_sn(T_sn string) (data Device_R, err error) {
+
+	msg, err := nats.Nats.Request("Cold_ReadDevice_List", []byte(T_sn), 3*time.Second)
+	if err != nil {
+		return
+	}
+
+	type T_R struct {
+		Code int16    `xml:"Code"`
+		Msg  string   `xml:"Msg"`
+		Data Device_R `xml:"Data"` // 泛型
+	}
+	var t_R T_R
+
+	err = msgpack.Unmarshal(msg.Data, &t_R)
+	if err != nil {
+		return
+	}
+	if t_R.Code != 200 {
+		err = errors.New(t_R.Msg)
+		return
+	}
+	data = t_R.Data
+
+	return data, nil
+}
+func Cold_ReadCompanyByT_key(T_key string) (data Company, err error) {
+
+	msg, err := nats.Nats.Request("Cold_ReadCompanyByT_key", []byte(T_key), 3*time.Second)
+	if err != nil {
+		return
+	}
+
+	type T_R struct {
+		Code int16   `xml:"Code"`
+		Msg  string  `xml:"Msg"`
+		Data Company `xml:"Data"` // 泛型
+	}
+	var t_R T_R
+
+	err = msgpack.Unmarshal(msg.Data, &t_R)
+	if err != nil {
+		return
+	}
+	if t_R.Code != 200 {
+		err = errors.New(t_R.Msg)
+		return
+	}
+	data = t_R.Data
+
+	return data, nil
+}

+ 23 - 0
common/nats/nats_server/models.go

@@ -114,6 +114,7 @@ type DeviceSensorParameter_R struct {
 
 type Device_R struct {
 	T_sn              string // 设备序列号 KF开头,环境监测主机。 YD开头,温途监测主机
+	T_pid             int    // Account.Company 绑定公司
 	T_devName         string // 设备名称
 	T_protocol        int    // 1 1.0协议  2 2.0协议
 	T_VerifyTime      string // 验证时间
@@ -141,3 +142,25 @@ type Device_R struct {
 	T_DeviceSensor_Num int // 传感器数量
 
 }
+
+type Company struct {
+	Id     int    `orm:"column(ID);size(11);auto;pk"`
+	T_mid  int    `orm:"size(200);null"`       //  上一级 ID
+	T_name string `orm:"size(256);null"`       // 公司名称
+	T_key  string `orm:"size(256);index;null"` // 公司密钥
+	T_type int    `orm:"size(256);default(1)"` // 公司类型 1-医药公司 2-运输企业
+
+	T_plan string `orm:"type(text);null"` //  平面图
+	T_data string `orm:"type(text);null"` //  大数据
+	T_v3d  string `orm:"type(text);null"` //  3D 视图
+
+	T_path     string    `orm:"size(256);null"` // 公司路径 /0/1/5/
+	T_money    float32   `orm:"digits(12);decimals(2)"`
+	T_State    int       `orm:"size(200);default(1)"`                                  // 0删除  1正常
+	T_warning  int       `orm:"size(20);default(1)"`                                   // 是否处理报警信息 1处理 2不处理
+	T_Charging int       `orm:"size(11);default(0)"`                                   //  记账扣费 公司ID  默认为:0 (自己)
+	CreateTime time.Time `orm:"column(create_time);type(timestamp);null;auto_now_add"` // auto_now 每次 model 保存时都会对时间自动更新
+	UpdateTime time.Time `orm:"column(update_time);type(timestamp);null;auto_now"`     // auto_now_add 第一次保存时才设置时间
+
+	Children []Company `orm:"-"`
+}

+ 9 - 0
common/sf/application_apis.go

@@ -0,0 +1,9 @@
+package sf
+
+var (
+	BASE_URL              = "https://bspgw.sf-express.com/std/service"
+	BASE_URL_SBOX         = "https://sfapi-sbox.sf-express.com/std/service"
+	EXP_RECE_CREATE_ORDER = "EXP_RECE_CREATE_ORDER"
+	ContactTypeSender     = 1 // 寄件
+	ContactTypeConsignee  = 2 // 收件
+)

+ 98 - 0
common/sf/clent.go

@@ -0,0 +1,98 @@
+package sf
+
+import (
+	"crypto/md5"
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/go-resty/resty/v2"
+	"github.com/google/uuid"
+	"net/url"
+	"time"
+)
+
+func (c *Client) GetMsgDigest(msgData_ti string) string {
+
+	// 将业务报文+时间戳+校验码组合成需加密的字符串
+	toVerifyText := msgData_ti + c.CheckWord
+
+	// URL 编码处理
+	toVerifyText = url.QueryEscape(toVerifyText)
+
+	// 进行 MD5 加密
+	hash := md5.Sum([]byte(toVerifyText))
+
+	// 通过 Base64 生成数字签名
+	msgDigest := base64.StdEncoding.EncodeToString(hash[:])
+
+	fmt.Println(msgDigest)
+	return msgDigest
+}
+
+type Client struct {
+	PartnerID   string //合作伙伴编码(即顾客编码)
+	CheckWord   string //校验码
+	MonthlyCard string // 月结账号
+	IsBox       bool   // 是否为沙箱环境
+}
+
+func NewClient(PartnerID, CheckWord, MonthlyCard string, IsBox bool) *Client {
+	client := &Client{
+		PartnerID:   PartnerID,
+		CheckWord:   CheckWord,
+		MonthlyCard: MonthlyCard,
+		IsBox:       IsBox,
+	}
+	return client
+}
+
+// body可传map,结构体
+func (c *Client) Send(serverid string, body interface{}) (bd string, err error) {
+	myurl := ""
+
+	myurl += "?partnerID=" + c.PartnerID
+	myurl += "&requestID=" + uuid.New().String()
+	myurl += "&serviceCode=" + serverid
+	//获取时间戳并拼接
+	ti := fmt.Sprintf("%v", time.Now().Unix()*1000)
+	myurl += "&timestamp=" + ti
+	//把结构体转成json字符串,再生成签名
+	by, _ := json.Marshal(body)
+	msgdata := string(by)
+
+	//fmt.Println(md5s)
+	myurl += "&msgDigest=" + c.GetMsgDigest(msgdata+ti)
+
+	myurl += "&msgData=" + msgdata
+
+	//return a.发送(myurl, "")
+	order_url := BASE_URL
+	if c.IsBox {
+		order_url = BASE_URL_SBOX
+	}
+	by, err = c.POST(order_url+myurl, "")
+	if err != nil {
+		return
+	}
+	res := new(Response)
+	if err = json.Unmarshal(by, res); err != nil {
+		return
+	}
+	bd = res.ApiResultData
+	return
+}
+
+// application/x-www-form-urlencoded类型请求发送
+func (c *Client) POST(url, body string) ([]byte, error) {
+
+	client := resty.New()
+
+	resp, err := client.R().
+		SetHeader("Content-Type", "application/x-www-form-urlencoded").
+		SetBody(body).Post(url)
+	if err != nil {
+		return nil, err
+	}
+	return resp.Body(), nil
+
+}

+ 53 - 0
common/sf/model.go

@@ -0,0 +1,53 @@
+package sf
+
+type SFOrder struct {
+	//XMLName           xml.Name `json:"Order"`
+	OrderId            string        `json:"orderId"`            //运单id
+	Language           string        `json:"language"`           //语言
+	MonthlyCard        string        `json:"monthlyCard"`        //月结账号
+	ParcelQty          int           `json:"parcelQty"`          //包裹数,一个包裹对应一个运单号;若包裹数大于1,则返回一个母运单号和N-1个子运单号
+	PayMethod          int           `json:"payMethod"`          //付款方式,支持以下值: 1:寄方付 2:收方付 3:第三方付
+	TotalLength        int           `json:"totalLength"`        //物品长,非必填
+	TotalWeight        int           `json:"totalWeight"`        //物品宽,非必填
+	TotalHeight        int           `json:"totalHeight"`        //物品高,非必填
+	IsOneselfPickup    int           `json:"isOneselfPickup"`    //快件自取,支持以下值: 1:客户同意快件自取 0:客户不同意快件自取
+	ExpressTypeId      int           `json:"expressTypeId"`      //快件产品类别, 支持附录 《快件产品类别表》 的产品编码值,仅可使用与顺丰销售约定的快件产品
+	IsReturnRoutelabel int           `json:"isReturnRoutelabel"` //是否返回路由标签: 默认1, 1:返回路由标签, 0:不返回;除部分特殊用户外,其余用户都默认返回
+	ContactInfoList    []ContactInfo `json:"contactInfoList"`    //收件人信息,要两个
+	CargoDetails       []CargoDetail `json:"CargoDetails"`       //物品信息
+	CargoDesc          string        `json:"cargoDesc"`          //托寄物品描述,非必填
+}
+
+type ContactInfo struct {
+	Address     string `json:"address"`     //地址
+	Province    string `json:"province"`    //省
+	City        string `json:"city"`        //市
+	County      string `json:"county"`      //区
+	Mobile      string `json:"mobile"`      //手机
+	Contact     string `json:"contact"`     //人物
+	ContactType int    `json:"contactType"` //地址类型: 1,寄件方信息 2,到件方信息
+	Country     string `json:"country"`     //国家或地区编码
+}
+
+// 托寄物品信息
+type CargoDetail struct {
+	Name string `json:"name"` //市
+}
+
+type Response struct {
+	ApiErrorMsg   string `json:"apiErrorMsg"` //返回提示
+	ApiResponseID string `json:"apiResponseID"`
+	ApiResultCode string `json:"apiResultCode"`
+	ApiResultData string `json:"apiResultData"`
+}
+
+type ResultData struct {
+	Success   bool   `json:"success"`
+	ErrorCode string `json:"errorCode"`
+	ErrorMsg  string `json:"errorMsg"`
+}
+
+type OrderResponse struct {
+	ResultData
+	MsgData interface{} `json:"msgData"`
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 53 - 0
common/sf/order_test.go


+ 0 - 1
conf/settings.yml

@@ -30,7 +30,6 @@ settings:
     # 数据库类型 mysql
     driver: mysql
     # 数据库连接字符串 mysql 缺省信息 charset=utf8&parseTime=True&loc=Local&timeout=1000ms
-#    source: godown:Fp8stPSZ!W3@#G@tcp(192.168.0.88:3306)/godown?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
     source: gas_cylinder:PtwLf5LLhP8w7Xi5@tcp(192.168.11.77:3306)/gas_cylinder?charset=utf8&parseTime=True&loc=Local&timeout=1000ms
     host: localhost:6400
   cache:

+ 5 - 0
go.mod

@@ -51,6 +51,7 @@ require (
 	github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
 	github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b // indirect
 	github.com/bitly/go-simplejson v0.5.0 // indirect
+	github.com/blend/go-sdk v1.20240719.1 // indirect
 	github.com/bsm/redislock v0.5.0 // indirect
 	github.com/bytedance/sonic v1.8.0 // indirect
 	github.com/campoy/embedmd v1.0.0 // indirect
@@ -84,12 +85,16 @@ require (
 	github.com/inconshreveable/mousetrap v1.0.1 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.5 // indirect
+	github.com/jonboulle/clockwork v0.3.0 // indirect
+	github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
 	github.com/klauspost/compress v1.17.2 // indirect
 	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
+	github.com/lestrrat-go/strftime v1.0.6 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.7 // indirect
 	github.com/mattn/go-isatty v0.0.17 // indirect

+ 18 - 2
go.sum

@@ -11,6 +11,7 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k
 cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
 contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+git.sr.ht/~sbinet/cmpimg v0.1.0 h1:E0zPRk2muWuCqSKSVZIWsgtU9pjsw3eKHi8VmQeScxo=
 git.sr.ht/~sbinet/gg v0.5.0 h1:6V43j30HM623V329xA9Ntq+WJrMjDxRjuAB1LFWF5m8=
 git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo=
 github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
@@ -79,6 +80,8 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
 github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
 github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
 github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
+github.com/blend/go-sdk v1.20240719.1 h1:eyispDP9DzQuNE+y7j1xSqwRm6ndMS4jgwlOQU4BTGY=
+github.com/blend/go-sdk v1.20240719.1/go.mod h1:aTw/exIbMHDYcJLTiqeWMMVhUs9+72BDe26AA0A6jno=
 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
 github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
 github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
@@ -179,6 +182,8 @@ github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aev
 github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M=
 github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
+github.com/go-fonts/dejavu v0.3.2 h1:3XlHi0JBYX+Cp8n98c6qSoHrxPa4AUKDMKdrh/0sUdk=
+github.com/go-fonts/latin-modern v0.3.2 h1:M+Sq24Dp0ZRPf3TctPnG1MZxRblqyWC/cRUL9WmdaFc=
 github.com/go-fonts/liberation v0.3.2 h1:XuwG0vGHFBPRRI8Qwbi5tIvR3cku9LUfZGq/Ar16wlQ=
 github.com/go-fonts/liberation v0.3.2/go.mod h1:N0QsDLVUQPy3UYg9XAc3Uh3UDMp2Z7M1o4+X98dXkmI=
 github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
@@ -359,6 +364,10 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
 github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jonboulle/clockwork v0.3.0 h1:9BSCMi8C+0qdApAp4auwX0RkLGUjs956h0EkuQymUhg=
+github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
+github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
+github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -403,6 +412,12 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
 github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
 github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
 github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
+github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
+github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
 github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE=
 github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8=
 github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0=
@@ -736,12 +751,11 @@ golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
 golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
+golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
-golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
-golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
 golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -918,6 +932,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+gonum.org/v1/gonum v0.14.0 h1:2NiG67LD1tEH0D7kM+ps2V+fXmsAnpUeec7n8tcr4S0=
 gonum.org/v1/plot v0.14.0 h1:+LBDVFYwFe4LHhdP8coW6296MBEY4nQ+Y4vuUpJopcE=
 gonum.org/v1/plot v0.14.0/go.mod h1:MLdR9424SJed+5VqC6MsouEpig9pZX2VZ57H9ko2bXU=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
@@ -1008,5 +1023,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
 honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
 k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=
 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
 sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio