Browse Source

ADD:冰排、冰柜管理

zoie 9 months ago
parent
commit
5322986733
41 changed files with 3041 additions and 131 deletions
  1. 0 1
      .gitignore
  2. 3 2
      app/admin/controller/company.go
  3. 1 1
      app/admin/controller/customer.go
  4. 195 0
      app/admin/controller/ice_locker.go
  5. 514 0
      app/admin/controller/ice_raft.go
  6. 1 1
      app/admin/controller/sys_user.go
  7. 93 17
      app/admin/controller/waybill.go
  8. 1 1
      app/admin/model/car.go
  9. 7 2
      app/admin/model/cooler_box.go
  10. 30 0
      app/admin/model/ice_locker.go
  11. 92 0
      app/admin/model/ice_raft.go
  12. 8 7
      app/admin/model/sys_dept.go
  13. 1 1
      app/admin/model/warehouse.go
  14. 42 23
      app/admin/model/waybill.go
  15. 25 0
      app/admin/router/ice_locker.go
  16. 38 0
      app/admin/router/ice_raft.go
  17. 1 0
      app/admin/router/waybill.go
  18. 2 2
      app/admin/service/address.go
  19. 145 0
      app/admin/service/company.go
  20. 8 8
      app/admin/service/dto/car.go
  21. 12 5
      app/admin/service/dto/company.go
  22. 4 4
      app/admin/service/dto/cooler_box.go
  23. 93 0
      app/admin/service/dto/ice_locker.go
  24. 169 0
      app/admin/service/dto/ice_raft.go
  25. 3 3
      app/admin/service/dto/warehouse.go
  26. 33 7
      app/admin/service/dto/waybill.go
  27. 190 0
      app/admin/service/ice_locker.go
  28. 588 0
      app/admin/service/ice_raft.go
  29. 288 32
      app/admin/service/waybill.go
  30. 205 0
      app/jobs/jobbase.go
  31. 85 0
      app/jobs/jobs.go
  32. 62 0
      app/jobs/model/sys_job.go
  33. 16 0
      app/jobs/type.go
  34. 5 4
      cmd/api/server.go
  35. 1 1
      common/actions/permission.go
  36. 58 0
      common/lib/lib.go
  37. 1 1
      common/model/byat.go
  38. 1 1
      common/nats/nats_server/NatsColdApi.go
  39. 5 0
      db/migration.go
  40. 7 5
      go.mod
  41. 8 2
      go.sum

+ 0 - 1
.gitignore

@@ -38,7 +38,6 @@ Makefile
 /ofile
 setting.yml
 /temp
-/app/jobs
 /docs
 uploads
 

+ 3 - 2
app/admin/controller/company.go

@@ -40,15 +40,16 @@ func (e CompanyController) GetPage(c *gin.Context) {
 
 	//数据权限检查
 	p := actions.GetPermissionFromContext(c)
-	p.DeptId = user.GetDeptId(c)
+	//p.DeptId = user.GetDeptId(c)
 	list := make([]model.SysDept, 0)
 	var count int64
 	err = s.GetPage(&req, &list, &count)
+	list, err = s.SetDeptPage(&req, p)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return
 	}
-	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+	e.OK(list, "查询成功")
 }
 
 // GetAll 添加用户-获取公司列表

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

@@ -422,7 +422,7 @@ func (e Customer) VerifyCode(c *gin.Context) {
 	res, err := ss.Send(req.Phone, content)
 	if err != nil || res.Status != sms.SUCCESS {
 		e.Logger.Error("发送短信验证码出现异常", zap.Any("res", res), zap.Error(err))
-		e.Error(500, err, "验证发送失败,请重试")
+		e.Error(500, err, "验证发送失败,请重试")
 		return
 	}
 	_ = e.Cache.Set(key, code, 600)

+ 195 - 0
app/admin/controller/ice_locker.go

@@ -0,0 +1,195 @@
+package controller
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/app/admin/service"
+	"cold-delivery/app/admin/service/dto"
+	"cold-delivery/common/actions"
+	"github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin/binding"
+	"gogs.baozhida.cn/zoie/OAuth-core/api"
+	"gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth/user"
+	_ "gogs.baozhida.cn/zoie/OAuth-core/pkg/response"
+)
+
+type IceLockerController struct {
+	api.Api
+}
+
+// GetPage 获取冰柜列表
+// @Summary 获取冰柜列表
+// @Description 获取冰柜列表
+// @Tags 冰柜
+// @Param name query string false "冰柜名称"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.IceLocker}} "{"code": 200, "data": [...]}"
+// @Router /api/ice-locker [get]
+// @Security Bearer
+func (e IceLockerController) GetPage(c *gin.Context) {
+	s := service.IceLocker{}
+	req := dto.IceLockerGetPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]model.IceLocker, 0)
+	var count int64
+
+	err = s.GetPage(&req, &list, &count, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// Get 通过id获取冰柜
+// @Summary 通过id获取冰柜
+// @Description 通过id获取冰柜
+// @Tags 冰柜
+// @Param id path string true "冰柜id"
+// @Success 200 {object} response.Response{data=model.IceLocker} "{"code": 200, "data": [...]}"
+// @Router /api/ice-locker/{id} [get]
+// @Security Bearer
+func (e IceLockerController) Get(c *gin.Context) {
+	s := service.IceLocker{}
+	req := dto.IceLockerGetReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	var object model.IceLocker
+	p := actions.GetPermissionFromContext(c)
+
+	//数据权限检查
+	err = s.Get(&req, &object, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	e.OK(object, "查询成功")
+}
+
+// Insert 添加冰柜
+// @Summary 添加冰柜
+// @Description 添加冰柜
+// @Tags 冰柜
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceLockerInsertReq true "data"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/ice-locker [post]
+// @Security Bearer
+func (e IceLockerController) Insert(c *gin.Context) {
+	s := service.IceLocker{}
+	req := dto.IceLockerInsertReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	p := actions.GetPermissionFromContext(c)
+	// 设置创建人
+	req.SetCreateBy(user.GetUserId(c))
+	req.SetDeptId(p.DeptId)
+	err = s.Insert(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "创建成功")
+}
+
+// Update 修改冰柜
+// @Summary 修改冰柜
+// @Description 修改冰柜
+// @Tags 冰柜
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceLockerUpdateReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/ice-locker [put]
+// @Security Bearer
+func (e IceLockerController) Update(c *gin.Context) {
+	s := service.IceLocker{}
+	req := dto.IceLockerUpdateReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	p := actions.GetPermissionFromContext(c)
+
+	req.SetUpdateBy(user.GetUserId(c))
+	err = s.Update(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "更新成功")
+}
+
+// Delete 删除冰柜
+// @Summary 删除冰柜
+// @Description 删除冰柜
+// @Tags 冰柜
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceLockerDeleteReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/ice-locker [delete]
+// @Security Bearer
+func (e IceLockerController) Delete(c *gin.Context) {
+	s := service.IceLocker{}
+	req := dto.IceLockerDeleteReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	err = s.Remove(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "删除成功")
+}

+ 514 - 0
app/admin/controller/ice_raft.go

@@ -0,0 +1,514 @@
+package controller
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/app/admin/service"
+	"cold-delivery/app/admin/service/dto"
+	"cold-delivery/common/actions"
+	"encoding/json"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin/binding"
+	"gogs.baozhida.cn/zoie/OAuth-core/api"
+	"gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth/user"
+	_ "gogs.baozhida.cn/zoie/OAuth-core/pkg/response"
+	"time"
+)
+
+type IceRaftController struct {
+	api.Api
+}
+
+// GetPage 获取冰排列表
+// @Summary 获取冰排列表
+// @Description 获取冰排列表
+// @Tags 冰排
+// @Param name query string false "冰排名称"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.IceRaft}} "{"code": 200, "data": [...]}"
+// @Router /api/ice-raft [get]
+// @Security Bearer
+func (e IceRaftController) GetPage(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftGetPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]model.IceRaft, 0)
+	var count int64
+
+	err = s.GetPage(&req, &list, &count, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+func (e IceRaftController) GetPageByCoolerBoxId(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.GetPageByCoolerBoxIdReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]string, 0)
+
+	err = s.GetPageByCoolerBoxId(req.CoolerBoxId, &list, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(list, "查询成功")
+}
+
+// GetNewestRecordPage 获取冰排记录列表
+// @Summary 获取冰排列表
+// @Description 获取冰排列表
+// @Tags 冰排
+// @Param name query string false "冰排名称"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.IceRaft}} "{"code": 200, "data": [...]}"
+// @Router /api/ice-raft [get]
+// @Security Bearer
+func (e IceRaftController) GetNewestRecordPage(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftGetNewestRecordPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]model.IceRaft, 0)
+	var count int64
+
+	err = s.GetRecordPage(&req, &list, &count, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+func (e IceRaftController) GetNewestRecordPageSSE(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftGetNewestRecordPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]model.IceRaft, 0)
+	var count int64
+
+	// Set the response header to indicate SSE content type
+	c.Header("Content-Type", "text/event-stream")
+	c.Header("Cache-Control", "no-cache")
+	c.Header("Connection", "keep-alive")
+	// Create a channel to send events to the client
+	println("Client connected")
+	// Listen for client close and remove the client from the list
+	notify := c.Writer.CloseNotify()
+	go func() {
+		<-notify
+		fmt.Println("Client disconnected")
+	}()
+	type Page struct {
+		Count    int         `json:"count"`    //总数
+		Page     int         `json:"page"`     //页码
+		PageSize int         `json:"pageSize"` //页条数
+		List     interface{} `json:"list"`
+	}
+	type Response struct {
+		RequestId string `protobuf:"bytes,1,opt,name=requestId,proto3" json:"requestId,omitempty"`
+		Code      int32  `protobuf:"varint,2,opt,name=code,proto3" json:"code,omitempty"`
+		Msg       string `protobuf:"bytes,3,opt,name=msg,proto3" json:"msg,omitempty"`
+		Status    string `protobuf:"bytes,4,opt,name=status,proto3" json:"status,omitempty"`
+		Data      Page   `json:"data"`
+	}
+	// Continuously send data to the client
+	for {
+		err = s.GetRecordPage(&req, &list, &count, p)
+		if err != nil {
+			e.Error(500, err, err.Error())
+			return
+		}
+
+		var response Response
+		var page Page
+		response.Code = 200
+		response.Msg = "查询成功"
+		response.Msg = "查询成功"
+		page.Count = int(count)
+		page.Page = req.GetPageIndex()
+		page.PageSize = req.GetPageSize()
+		page.List = list
+		response.Data = page
+		//data := <-eventChan
+		res, _ := json.Marshal(&response)
+		fmt.Fprintf(c.Writer, "data: %s\n\n", string(res))
+		c.Writer.Flush()
+		time.Sleep(10 * time.Second)
+	}
+}
+
+
+// Get 通过id获取冰排
+// @Summary 通过id获取冰排
+// @Description 通过id获取冰排
+// @Tags 冰排
+// @Param id path string true "冰排id"
+// @Success 200 {object} response.Response{data=model.IceRaft} "{"code": 200, "data": [...]}"
+// @Router /api/ice-raft/{id} [get]
+// @Security Bearer
+func (e IceRaftController) Get(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftGetReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	var object model.IceRaft
+	p := actions.GetPermissionFromContext(c)
+
+	//数据权限检查
+	err = s.Get(&req, &object, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	e.OK(object, "查询成功")
+}
+
+// Insert 添加冰排
+// @Summary 添加冰排
+// @Description 添加冰排
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftInsertReq true "data"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/ice-raft [post]
+// @Security Bearer
+func (e IceRaftController) Insert(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftInsertReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	p := actions.GetPermissionFromContext(c)
+	// 设置创建人
+	req.SetCreateBy(user.GetUserId(c))
+	req.SetDeptId(p.DeptId)
+	err = s.Insert(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "创建成功")
+}
+
+// Update 修改冰排
+// @Summary 修改冰排
+// @Description 修改冰排
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftUpdateReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/ice-raft [put]
+// @Security Bearer
+func (e IceRaftController) Update(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftUpdateReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	p := actions.GetPermissionFromContext(c)
+
+	req.SetUpdateBy(user.GetUserId(c))
+	err = s.Update(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "更新成功")
+}
+
+// Delete 删除冰排
+// @Summary 删除冰排
+// @Description 删除冰排
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftDeleteReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/ice-raft [delete]
+// @Security Bearer
+func (e IceRaftController) Delete(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftDeleteReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	err = s.Remove(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "删除成功")
+}
+
+// InStorage 冰排入库
+// @Summary 冰排入库
+// @Description 冰排入库
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftInStorageReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/ice-raft [delete]
+// @Security Bearer
+func (e IceRaftController) InStorage(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftInStorageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	err = s.InStorage(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(nil, "入库成功")
+}
+
+// OutStorage 冰排出库
+// @Summary 冰排出库
+// @Description 冰排出库
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftOutStorageReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/ice-raft [delete]
+// @Security Bearer
+func (e IceRaftController) OutStorage(c *gin.Context) {
+	s := service.IceRaft{}
+	req := dto.IceRaftOutStorageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	err = s.OutStorage(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(nil, "出库成功")
+}
+
+type IceRaftRecordController struct {
+	api.Api
+}
+
+// GetPage 获取冰排历史记记录列表
+// @Summary 获取冰排列表
+// @Description 获取冰排列表
+// @Tags 冰排
+// @Param name query string false "冰排名称"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.IceRaft}} "{"code": 200, "data": [...]}"
+// @Router /api/ice-raft [get]
+// @Security Bearer
+func (e IceRaftRecordController) GetPage(c *gin.Context) {
+	s := service.IceRaftRecord{}
+	req := dto.IceRaftRecordGetPageReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Query).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	list := make([]model.IceRaftRecord, 0)
+	var count int64
+
+	err = s.GetPage(&req, &list, &count, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// Update 修改冰排出入库历史记录
+// @Summary 修改冰排出入库历史记录
+// @Description 修改冰排出入库历史记录
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftUpdateReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/ice-raft [put]
+// @Security Bearer
+func (e IceRaftRecordController) Update(c *gin.Context) {
+	s := service.IceRaftRecord{}
+	req := dto.IceRaftRecordUpdateReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	p := actions.GetPermissionFromContext(c)
+
+	req.SetUpdateBy(user.GetUserId(c))
+	err = s.Update(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "更新成功")
+}
+
+// Delete 删除冰排出入库历史记录
+// @Summary 删除冰排出入库历史记录
+// @Description 删除冰排出入库历史记录
+// @Tags 冰排
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.IceRaftDeleteReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/ice-raft [delete]
+// @Security Bearer
+func (e IceRaftRecordController) Delete(c *gin.Context) {
+	s := service.IceRaftRecord{}
+	req := dto.IceRaftRecordDeleteReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON, nil).
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	err = s.Remove(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "删除成功")
+}

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

@@ -455,7 +455,7 @@ func (e SysUser) VerifyCode(c *gin.Context) {
 	res, err := ss.Send(req.Phone, content)
 	if err != nil || res.Status != sms.SUCCESS {
 		e.Logger.Error("发送短信验证码出现异常", zap.Any("res", res), zap.Error(err))
-		e.Error(500, err, "验证发送失败,请重试")
+		e.Error(500, err, "验证发送失败,请重试")
 		return
 	}
 	_ = e.Cache.Set(key, code, 600)

+ 93 - 17
app/admin/controller/waybill.go

@@ -14,6 +14,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin/binding"
 	"github.com/golang/freetype/truetype"
+	"github.com/pborman/uuid"
 	"github.com/signintech/gopdf"
 	"github.com/wcharczuk/go-chart/v2"
 	"github.com/wcharczuk/go-chart/v2/drawing"
@@ -78,11 +79,11 @@ func (e WaybillController) GetPage(c *gin.Context) {
 
 	list := make([]model.Waybill, 0)
 	var count int64
-	//if userObj.Type == model.SysUserTypeSys {
-	err = s.GetPage(&req, &list, &count, p)
-	//} else {
-	//	err = s.GetUserPage(&req, &list, &count, p)
-	//}
+	if userObj.Type == model.SysUserTypeSys {
+		err = s.GetPage(&req, &list, &count, p)
+	} else {
+		err = s.GetUserPage(&req, &list, &count, p)
+	}
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return
@@ -197,11 +198,13 @@ func (e WaybillController) Export(c *gin.Context) {
 // @Security Bearer
 func (e WaybillController) Home(c *gin.Context) {
 	s := service.Waybill{}
+	userSvc := service.SysUser{}
 	req := dto.WaybillStatsReq{}
 	err := e.MakeContext(c).
 		MakeOrm().
 		Bind(&req, binding.Query).
 		MakeService(&s.Service).
+		MakeService(&userSvc.Service).
 		Errors
 	if err != nil {
 		e.Logger.Error(err)
@@ -211,8 +214,62 @@ func (e WaybillController) Home(c *gin.Context) {
 
 	//数据权限检查
 	p := actions.GetPermissionFromContext(c)
+	var userObj model.SysUser
+	err = userSvc.Get(&dto.SysUserGetReq{Id: p.UserId}, nil, &userObj)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	var res dto.WaybillStatsRes
+	if userObj.Type == model.SysUserTypeSys {
+		res = s.GetBasicsStats(&req, p)
+	} else {
+		res = s.GetUserBasicsStats(&req, p, service.GetUserBasicsStatsScopes(p.UserId))
+	}
+	e.OK(res, "查询成功")
+}
 
-	res := s.GetBasicsStats(&req, p)
+// Home 用户运单数量统计
+// @Summary 首页统计
+// @Description 首页统计
+// @Tags 运单
+// @Param no query string false "运单号"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.Waybill}} "{"code": 200, "data": [...]}"
+// @Router /api/waybill [get]
+// @Security Bearer
+func (e WaybillController) UserStats(c *gin.Context) {
+	s := service.Waybill{}
+	userSvc := service.SysUser{}
+	req := dto.WaybillUserStatsReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.JSON).
+		MakeService(&s.Service).
+		MakeService(&userSvc.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+	var userObj model.SysUser
+	err = userSvc.Get(&dto.SysUserGetReq{Id: p.UserId}, nil, &userObj)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	var res []dto.WaybillUserStats
+	if userObj.Type == model.SysUserTypeSys {
+		res = s.GetReCheckStats(&req, p)
+	} else {
+		req.UserIds = []int{p.UserId}
+		res = s.GetReCheckStats(&req, p)
+	}
 	e.OK(res, "查询成功")
 }
 
@@ -511,11 +568,13 @@ func (e WaybillController) Update(c *gin.Context) {
 // @Security Bearer
 func (e WaybillController) Delivery(c *gin.Context) {
 	s := service.Waybill{}
+	IceRaftSvc := service.IceRaft{}
 	req := dto.WaybillDeliveryReq{}
 	err := e.MakeContext(c).
 		MakeOrm().
 		Bind(&req).
 		MakeService(&s.Service).
+		MakeService(&IceRaftSvc.Service).
 		Errors
 	if err != nil {
 		e.Logger.Error(err)
@@ -524,12 +583,33 @@ func (e WaybillController) Delivery(c *gin.Context) {
 	}
 	p := actions.GetPermissionFromContext(c)
 
+	oldIceRaftCode := make([]string, 0)
+	missingIceRaftCode := make([]string, 0)
+
+	err = IceRaftSvc.GetPageByCoolerBoxId(req.CoolerBoxId, &oldIceRaftCode, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+
 	req.SetUpdateBy(user.GetUserId(c))
+	req.OldIceRaftCode = oldIceRaftCode
 	err = s.Delivery(&req, p)
 	if err != nil {
 		e.Error(500, err, err.Error())
 		return
 	}
+	missingIceRaftCode = lib.FindStrListMissingData(oldIceRaftCode, req.IceRaftCode)
+	if len(missingIceRaftCode) > 0 {
+		c.JSON(200, map[string]interface{}{
+			"requestId": uuid.NewUUID(),
+			"code":      2000,
+			"msg":       "派单成功",
+			"status":    "success",
+			"data":      missingIceRaftCode,
+		})
+		return
+	}
 
 	e.OK(req.GetId(), "派单成功")
 }
@@ -904,17 +984,13 @@ func (e WaybillController) Import(c *gin.Context) {
 		//quantity, _ := strconv.Atoi(row[8])
 		obj := dto.WaybillInsertReq{
 			Status:                  1,
-			SenderAddressName:       row[0],
-			SenderAddressPhone:      row[1],
-			SenderAddressDetails:    row[2],
-			ConsigneeAddressName:    row[3],
-			ConsigneeAddressPhone:   row[4],
-			ConsigneeAddressDetails: row[5],
-			DeliveryName:            row[6], // 防拆标签
-			DeliveryPhone:           row[7], // 防拆标签
-			//Quantity:                quantity,
-			TamperProofLabel: row[8], // 防拆标签
-			Remark:           row[9],
+			ConsigneeAddressName:    row[0],
+			ConsigneeAddressPhone:   row[1],
+			ConsigneeAddressDetails: row[2],
+			TamperProofLabel:        row[3], // 防拆标签
+			Remark:                  row[4],
+			ReCheckId:               p.UserId,
+			DeliveryId:              p.UserId,
 		}
 		obj.SetDeptId(p.DeptId)
 		obj.SetCreateBy(user.GetUserId(c))

+ 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"`

+ 7 - 2
app/admin/model/cooler_box.go

@@ -2,12 +2,17 @@ package model
 
 import model2 "cold-delivery/common/model"
 
-// 仓库
+const (
+	CoolerBoxStatusNormal  = "2"
+	CoolerBoxStatusDisable = "1"
+)
+
+// 保温箱
 type CoolerBox struct {
 	model2.Model
 	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

+ 30 - 0
app/admin/model/ice_locker.go

@@ -0,0 +1,30 @@
+package model
+
+import model2 "cold-delivery/common/model"
+
+const (
+	IceLockerStatusNormal  = "2"
+	IceLockerStatusDisable = "1"
+)
+
+// 仓库
+type IceLocker struct {
+	model2.Model
+	Name   string `json:"name" gorm:"size:128"`                                 // 商品名称
+	Status string `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-正常
+
+	model2.ControlBy
+	model2.ModelTime
+	model2.DeptBy
+}
+type IceLockerOmit struct {
+	Id   int    `json:"id,omitempty"`   // 主键编码
+	Name string `json:"name,omitempty"` // 商品名称
+}
+
+func (IceLocker) TableName() string {
+	return "ice_locker"
+}
+func (IceLockerOmit) TableName() string {
+	return "ice_locker"
+}

+ 92 - 0
app/admin/model/ice_raft.go

@@ -0,0 +1,92 @@
+package model
+
+import model2 "cold-delivery/common/model"
+
+const (
+	IceRaftStatusNormal  = "2"
+	IceRaftStatusDisable = "1"
+)
+
+// 冰排
+type IceRaft struct {
+	model2.Model
+	Code            string            `json:"code" gorm:"size:128"`                                 // 商品名称
+	Status          string            `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-停用 2-正常
+	IceRaftRecordId int               `json:"iceRaftRecordId" gorm:"size:128"`                      // 最新入库记录
+	IceRaftRecord   IceRaftRecordOmit `json:"iceRaftRecord" gorm:"->;foreignkey:IceRaftRecordId;references:Id"`
+	model2.ControlBy
+	model2.ModelTime
+	model2.DeptBy
+}
+type IceRaftOmit struct {
+	Id   int    `json:"id,omitempty"`   // 主键编码
+	Code string `json:"code,omitempty"` // 商品名称
+}
+
+func (IceRaft) TableName() string {
+	return "ice_raft"
+}
+func (IceRaftOmit) TableName() string {
+	return "ice_raft"
+}
+
+const (
+	IceRaftRecordStatusFreezing = "1" // 冷冻中
+	IceRaftRecordStatusWaitUse  = "2" // 待使用
+	IceRaftRecordStatusUsing    = "3" // 使用中
+	IceRaftRecordStatusFinish   = "4" // 已结束
+
+)
+
+func GetIceRaftRecordStatus(s string) string {
+	switch s {
+	case IceRaftRecordStatusFreezing:
+		return "冷冻中"
+	case IceRaftRecordStatusWaitUse:
+		return "待使用"
+	case IceRaftRecordStatusUsing:
+		return "使用中"
+	case IceRaftRecordStatusFinish:
+		return "已结束"
+	}
+	return ""
+}
+
+// 冰排记录
+type IceRaftRecord struct {
+	model2.Model
+	IceRaftId      int           `json:"iceRaftId" gorm:"size:128;comment:冰排id"`
+	Status         string        `json:"status" gorm:"size:4;not null;default:'2';comment:状态"` // 1-冷冻中 2-待使用 3-使用中
+	InStorageTime  model2.Time   `json:"inStorageTime"  gorm:"size:128;comment:入库时间"`          // 入库时间
+	OutStorageTime model2.Time   `json:"outStorageTime"  gorm:"size:128;comment:出库时间"`         // 出库时间
+	IceLockerId    int           `json:"iceLockerId" gorm:"size:128;comment:冰柜id"`             // 冰柜id
+	CoolerBoxId    int           `json:"coolerBoxId" gorm:"size:128;comment:保温箱id"`            // 保温箱id
+	FreezeClaim    float32       `json:"freezeClaim" gorm:"type:decimal(10,2);comment:冷冻要求"`   // 冷冻要求 单位小时
+	FreezeDuration int           `json:"freezeDuration" gorm:"size:128;comment:冷冻时长"`          // 冷冻时长 单位分钟
+	IceLocker      IceLockerOmit `json:"iceLocker" gorm:"->"`
+	CoolerBox      CoolerBoxOmit `json:"coolerBox" gorm:"->"`
+	model2.ControlBy
+	model2.ModelTime
+	model2.DeptBy
+}
+
+type IceRaftRecordOmit struct {
+	Id             int           `json:"id,omitempty"`             // 主键编码
+	IceRaftId      int           `json:"iceRaftId,omitempty"`      // 冰排id
+	Status         string        `json:"status,omitempty"`         // 1-停用 2-正常
+	InStorageTime  model2.Time   `json:"inStorageTime,omitempty"`  // 入库时间
+	OutStorageTime model2.Time   `json:"outStorageTime,omitempty"` // 出库时间
+	IceLockerId    int           `json:"iceLockerId,omitempty"`    // 冰柜id
+	CoolerBoxId    int           `json:"coolerBoxId,omitempty"`    // 保温箱id
+	FreezeClaim    float32       `json:"freezeClaim"`              // 冷冻要求 单位小时
+	FreezeDuration int           `json:"freezeDuration"`           // 冷冻时长 单位分钟
+	IceLocker      IceLockerOmit `json:"iceLocker" gorm:"->"`
+	CoolerBox      CoolerBoxOmit `json:"coolerBox" gorm:"->"`
+}
+
+func (IceRaftRecord) TableName() string {
+	return "ice_raft_record"
+}
+func (IceRaftRecordOmit) TableName() string {
+	return "ice_raft_record"
+}

+ 8 - 7
app/admin/model/sys_dept.go

@@ -13,13 +13,14 @@ var GetCompanyKeyErr = errors.New("获取公司秘钥失败")
 
 type SysDept struct {
 	model2.Model
-	ParentId int    `json:"parentId" gorm:""`                        // 上级部门
-	Path     string `json:"-" gorm:"size:255;" swaggerignore:"true"` // 路径
-	Name     string `json:"name"  gorm:"size:128;"`                  // 部门名称
-	Sort     int    `json:"-" gorm:"size:4;"`                        // 排序
-	Status   int    `json:"-" gorm:"size:4;"`                        // 状态 1-停用 2-正常
-	Remark   string `json:"remark"  gorm:"size:1024;"`               // 备注
-	ColdKey  string `json:"coldKey"  gorm:"size:128;"`               // 冷链3.0key
+	ParentId int       `json:"parentId" gorm:""`                        // 上级部门
+	Path     string    `json:"-" gorm:"size:255;" swaggerignore:"true"` // 路径
+	Name     string    `json:"name"  gorm:"size:128;"`                  // 部门名称
+	Sort     int       `json:"-" gorm:"size:4;"`                        // 排序
+	Status   int       `json:"-" gorm:"size:4;"`                        // 状态 1-停用 2-正常
+	Remark   string    `json:"remark"  gorm:"size:1024;"`               // 备注
+	ColdKey  string    `json:"coldKey"  gorm:"size:128;"`               // 冷链3.0key
+	Children []SysDept `json:"children" gorm:"-"`
 	model2.ControlBy
 	model2.ModelTime
 }

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

@@ -8,7 +8,7 @@ type Warehouse struct {
 	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-正启用
+	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
 

+ 42 - 23
app/admin/model/waybill.go

@@ -34,32 +34,51 @@ func (e *WaybillSendLog) Scan(src interface{}) error {
 	return json.Unmarshal(src.([]byte), e)
 }
 
+type IceRaftCode []string
+
+func (e IceRaftCode) Value() (driver.Value, error) {
+	d, err := json.Marshal(e)
+	return string(d), err
+}
+
+func (e *IceRaftCode) Scan(src interface{}) error {
+	return json.Unmarshal(src.([]byte), e)
+}
+
 // 运单
 type Waybill struct {
 	model2.Model
-	WaybillNo               string         `json:"waybillNo"  gorm:"size:128"`               // 单号
-	Status                  int            `json:"status"  gorm:"size:128"`                  // 订单状态:1已下单 2配送中 3已送达 4已拒收
-	SenderAddressDetails    string         `json:"senderAddressDetails"  gorm:"size:128"`    // 发货地址详情
-	SenderAddressName       string         `json:"senderAddressName"  gorm:"size:128"`       // 发货地址名称
-	SenderAddressPhone      string         `json:"senderAddressPhone"  gorm:"size:128"`      // 发货地址电话
-	ConsigneeAddressDetails string         `json:"consigneeAddressDetails"  gorm:"size:128"` // 收发货地址详情
-	ConsigneeAddressName    string         `json:"consigneeAddressName"  gorm:"size:128"`    // 收发货地址名称
-	ConsigneeAddressPhone   string         `json:"consigneeAddressPhone"  gorm:"size:128"`   // 收发货地址电话
-	DeliveryName            string         `json:"deliveryName"  gorm:"size:128"`            // 配送人名称
-	DeliveryPhone           string         `json:"deliveryPhone"  gorm:"size:128"`           // 配送人电话
-	Remark                  string         `json:"remark"  gorm:"size:4"`                    // 运输备注
-	OrderTime               model2.Time    `json:"orderTime"  gorm:"size:128"`               // 下单时间
-	DeliveryTime            model2.Time    `json:"deliveryTime"  gorm:"size:128"`            // 配送时间
-	ReceiptTime             model2.Time    `json:"receiptTime"  gorm:"size:128"`             // 签收时间
-	Quantity                int            `json:"quantity"  gorm:"size:128"`                // 药品数量
-	CoolerBoxId             int            `json:"coolerBoxId"  gorm:"size:128"`             // 保温箱id
-	ReceiptImg              string         `json:"receiptImg"  gorm:"size:text"`             // 签收图片
-	TamperProofLabel        string         `json:"tamperProofLabel"  gorm:"size:128"`        // 防拆标签
-	TamperProofLabelImg     string         `json:"tamperProofLabelImg"  gorm:"size:text"`    // 防拆标签
-	RejectionReason         string         `json:"rejectionReason"  gorm:"size:128"`         // 拒收原因
-	SendLog                 WaybillSendLog `json:"sendLog"`                                  // 运单短信发送日志
-	CoolerBox               CoolerBoxOmit  `json:"coolerBox" gorm:"->"`                      // 保温箱
-	Dept                    SysDeptOmit    `json:"dept" gorm:"->"`                           // 部门
+	WaybillNo               string         `json:"waybillNo"  gorm:"size:128"`                    // 单号
+	Status                  int            `json:"status"  gorm:"size:128"`                       // 订单状态:1已下单 2配送中 3已送达 4已拒收
+	SenderAddressDetails    string         `json:"senderAddressDetails"  gorm:"size:128"`         // 发货地址详情
+	SenderAddressName       string         `json:"senderAddressName"  gorm:"size:128"`            // 发货地址名称
+	SenderAddressPhone      string         `json:"senderAddressPhone"  gorm:"size:128"`           // 发货地址电话
+	ConsigneeAddressDetails string         `json:"consigneeAddressDetails"  gorm:"size:128"`      // 收发货地址详情
+	ConsigneeAddressName    string         `json:"consigneeAddressName"  gorm:"size:128"`         // 收发货地址名称
+	ConsigneeAddressPhone   string         `json:"consigneeAddressPhone"  gorm:"size:128"`        // 收发货地址电话
+	DeliveryName            string         `json:"deliveryName"  gorm:"size:128"`                 // 配送人名称
+	DeliveryPhone           string         `json:"deliveryPhone"  gorm:"size:128"`                // 配送人电话
+	DeliveryId              int            `json:"deliveryId"  gorm:"size:128"`                   // 配送人id
+	ReCheckId               int            `json:"reCheckId"  gorm:"size:128"`                    // 复核id
+	Remark                  string         `json:"remark"  gorm:"size:text"`                      // 运输备注
+	OrderTime               model2.Time    `json:"orderTime"  gorm:"size:128"`                    // 下单时间
+	DeliveryTime            model2.Time    `json:"deliveryTime"  gorm:"size:128"`                 // 配送时间
+	DeliveryDuration        int            `json:"deliveryDuration" gorm:"size:128;comment:冷冻时长"` // 配送耗时 单位分钟
+	ReceiptTime             model2.Time    `json:"receiptTime"  gorm:"size:128"`                  // 签收时间
+	Quantity                int            `json:"quantity"  gorm:"size:128"`                     // 药品数量
+	CoolerBoxId             int            `json:"coolerBoxId"  gorm:"size:128"`                  // 保温箱id
+	ReceiptImg              string         `json:"receiptImg"  gorm:"size:text"`                  // 签收图片
+	TamperProofLabel        string         `json:"tamperProofLabel"  gorm:"size:128"`             // 防拆标签
+	TamperProofLabelImg     string         `json:"tamperProofLabelImg"  gorm:"size:text"`         // 防拆标签
+	RejectionReason         string         `json:"rejectionReason"  gorm:"size:128"`              // 拒收原因
+	SendLog                 WaybillSendLog `json:"sendLog" gorm:"size:text"`                      // 运单短信发送日志
+	CoolerBox               CoolerBoxOmit  `json:"coolerBox" gorm:"->"`                           // 保温箱
+	Dept                    SysDeptOmit    `json:"dept" gorm:"->"`                                // 部门
+	Delivery                SysUserOmit    `json:"delivery" gorm:"->"`                            // 部门
+	ReCheck                 SysUserOmit    `json:"reCheck" gorm:"->"`                             // 部门
+	IceRaftCode             IceRaftCode    `json:"iceRaftCode" gorm:"size:text"`
+	AssessStar              int            `json:"assessStar"  gorm:"size:128"`    // 评价星星
+	AssessContent           string         `json:"assessContent"  gorm:"size:128"` // 评价内容
 	model2.ControlBy
 	model2.ModelTime
 	model2.DeptBy

+ 25 - 0
app/admin/router/ice_locker.go

@@ -0,0 +1,25 @@
+package router
+
+import (
+	"cold-delivery/app/admin/controller"
+	"cold-delivery/common/actions"
+	"github.com/gin-gonic/gin"
+	jwt "gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth"
+)
+
+func init() {
+	routerCheckRole = append(routerCheckRole, registerIceLockerRouter)
+}
+
+// 需认证的路由代码
+func registerIceLockerRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+	cont := controller.IceLockerController{}
+	r := v1.Group("/ice-locker").Use(authMiddleware.MiddlewareFunc()).Use(actions.PermissionAction())
+	{
+		r.GET("", cont.GetPage)
+		r.GET("/:id", cont.Get)
+		r.POST("", cont.Insert)
+		r.PUT("", cont.Update)
+		r.DELETE("", cont.Delete)
+	}
+}

+ 38 - 0
app/admin/router/ice_raft.go

@@ -0,0 +1,38 @@
+package router
+
+import (
+	"cold-delivery/app/admin/controller"
+	"cold-delivery/common/actions"
+	"github.com/gin-gonic/gin"
+	jwt "gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth"
+)
+
+func init() {
+	routerCheckRole = append(routerCheckRole, registerIceRaftRouter)
+}
+
+// 需认证的路由代码
+func registerIceRaftRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+	cont := controller.IceRaftController{}
+	r := v1.Group("/ice-raft").Use(authMiddleware.MiddlewareFunc()).Use(actions.PermissionAction())
+	{
+		r.GET("", cont.GetPage)
+		r.GET("/newest-record", cont.GetNewestRecordPage)
+		r.GET("/:id", cont.Get)
+		r.POST("", cont.Insert)
+		r.PUT("", cont.Update)
+		r.DELETE("", cont.Delete)
+		r.POST("/in-storage", cont.InStorage)
+		r.POST("/out-storage", cont.OutStorage)
+		r.GET("/newest-record-sse", cont.GetNewestRecordPageSSE)
+		r.GET("/cooler-box", cont.GetPageByCoolerBoxId)
+	}
+
+	cont2 := controller.IceRaftRecordController{}
+	r2 := v1.Group("/ice-raft-record").Use(authMiddleware.MiddlewareFunc()).Use(actions.PermissionAction())
+	{
+		r2.GET("", cont2.GetPage)
+		r2.PUT("", cont2.Update)
+		r2.DELETE("", cont2.Delete)
+	}
+}

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

@@ -38,6 +38,7 @@ func registerWaybillRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle
 		r.POST("/import", cont.Import)                 // 导入运单
 		r.GET("/export-template", cont.ExportTemplate) // 导出运单模板
 		r.GET("/home", cont.Home)                      // 首页统计
+		r.GET("/user-stats", cont.UserStats)           // 用户运单统计
 		r.GET("/export", cont.Export)                  // 运单管理-导出
 		r.GET("/customer/export", cont.CustomerExport) // 客户下单
 		r.GET("/temperature-pdf", cont.TemperaturePDF) // 导出温湿度pdf

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

@@ -54,7 +54,7 @@ func (e *Address) GetPage(c *dto.AddressGetPageReq, list *[]model.Address, count
 			Scopes(
 				AddressNameScopes(c.Name),
 				actions.Permission(data.TableName(), p),
-				//AddressDataScopes(c.DataType, p.UserId),
+				//AddressDataScopes(c.DataType, p.UserIds),
 				cDto.MakeCondition(c.GetNeedSearch()),
 				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 			).
@@ -66,7 +66,7 @@ func (e *Address) GetPage(c *dto.AddressGetPageReq, list *[]model.Address, count
 			Scopes(
 				AddressNameScopes(c.Name),
 				actions.Permission(data.TableName(), p),
-				//AddressDataScopes(c.DataType, p.UserId),
+				//AddressDataScopes(c.DataType, p.UserIds),
 				cDto.MakeCondition(c.GetNeedSearch()),
 				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 			).

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

@@ -4,7 +4,9 @@ import (
 	"cold-delivery/common/actions"
 	cDto "cold-delivery/common/dto"
 	"cold-delivery/common/global"
+	"encoding/json"
 	"errors"
+	"fmt"
 	"gogs.baozhida.cn/zoie/OAuth-core/service"
 	"gorm.io/gorm"
 	"gorm.io/gorm/clause"
@@ -222,3 +224,146 @@ func (e *Company) Remove(d *dto.CompanyDeleteReq, p *actions.DataPermission) err
 
 	return nil
 }
+
+// SetDeptPage 设置Dept页面数据
+func (e *Company) SetDeptPage(c *dto.CompanyGetPageReq, p *actions.DataPermission) (m []model.SysDept, err error) {
+	var list []model.SysDept
+	err = e.getList(c, &list, p)
+	for i := 0; i < len(list); i++ {
+		if list[i].ParentId != 0 {
+			continue
+		}
+		info := e.DeptPageCall(&list, list[i])
+		m = append(m, info)
+	}
+
+	if len(m) == 0 {
+		parentMap, flag := getDeptParent(list)
+		if flag {
+			for i := 0; i < len(list); i++ {
+				if parentMap[list[i].ParentId] {
+					continue
+				}
+				info := e.DeptPageCall(&list, list[i])
+				m = append(m, info)
+			}
+		} else {
+			for i := 0; i < len(list); i++ {
+				info := e.DeptPageCall(&list, list[i])
+				m = append(m, info)
+			}
+		}
+	}
+
+	return
+}
+func getDeptParent(deptList []model.SysDept) (map[int]bool, bool) {
+	list := deptList
+	var flag = false
+	var parentMap = map[int]bool{}
+	for j := 0; j < len(list); j++ {
+		parentMap[list[j].ParentId] = false
+	}
+
+	for j := 0; j < len(list); j++ {
+		if _, ok := parentMap[list[j].Id]; !ok {
+			continue
+		}
+		parentMap[list[j].Id] = true
+		flag = true
+	}
+	return parentMap, flag
+}
+
+// getList 获取组织数据
+func (e *Company) getList(c *dto.CompanyGetPageReq, list *[]model.SysDept, p *actions.DataPermission) error {
+	var err error
+	var data model.SysDept
+	if len(c.Name) == 0 && p.DeptId == 0 {
+		err = e.Orm.Model(&data).
+			Scopes(
+				cDto.MakeCondition(c.GetNeedSearch()),
+			).
+			Order("id desc").
+			Find(list).Error
+		if err != nil {
+			e.Log.Errorf("db error:%s", err)
+			return err
+		}
+
+		return nil
+	}
+
+	var tempList []model.SysDept
+	err = e.Orm.Model(&data).Where("id in (select id from sys_dept where dept_path like ? )", "%/"+pkg.IntToString(p.DeptId)+"/%").
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+		).
+		Order("id desc").
+		Find(&tempList).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+
+	// 获取部门及其子部门下ID
+	for _, dept := range tempList {
+		var deptList = make([]model.SysDept, 0)
+		err = e.Orm.Where("dept_path like ?", dept.Path+"%").Order(fmt.Sprintf("sort %s", c.SortOrder)).Find(&deptList).Error
+		*list = append(*list, deptList...)
+	}
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+
+	*list = DeduplicateDept(*list)
+
+	return nil
+}
+
+func (e *Company) DeptPageCall(deptList *[]model.SysDept, menu model.SysDept) model.SysDept {
+	list := *deptList
+	min := make([]model.SysDept, 0)
+	for j := 0; j < len(list); j++ {
+		if menu.Id != list[j].ParentId {
+			continue
+		}
+		mi := model.SysDept{}
+		mi.Id = list[j].Id
+		mi.ParentId = list[j].ParentId
+		mi.ColdKey = list[j].ColdKey
+		mi.Path = list[j].Path
+		mi.Name = list[j].Name
+		mi.Sort = list[j].Sort
+		mi.Status = list[j].Status
+		mi.Remark = list[j].Remark
+		mi.CreatedAt = list[j].CreatedAt
+		mi.Children = []model.SysDept{}
+		ms := e.DeptPageCall(deptList, mi)
+		min = append(min, ms)
+	}
+	menu.Children = min
+	return menu
+}
+
+func DeduplicateDept(list []model.SysDept) []model.SysDept {
+	seen := make(map[string]bool)
+	deduplicated := []model.SysDept{}
+
+	for _, v := range list {
+		// 将结构体编码为 JSON 字符串
+		key, err := json.Marshal(v)
+		if err != nil {
+			// 处理错误
+			continue
+		}
+		// 将 JSON 字符串作为键插入到 map 中
+		if _, ok := seen[string(key)]; !ok {
+			seen[string(key)] = true
+			deduplicated = append(deduplicated, v)
+		}
+	}
+
+	return deduplicated
+}

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

@@ -24,10 +24,10 @@ 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-正启用
-	UserId           int    `json:"userId"`                               // 绑定的用户id
+	CarNo            string `json:"carNo"`                                  // 商品名称
+	Sn               string `json:"sn"`                                     // sn
+	Status           string `json:"status"`                                 // 1-停用 2-正常
+	UserId           int    `json:"userId"`                                 // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
 }
@@ -57,10 +57,10 @@ 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
+	CarNo            string `json:"carNo"`             // 商品名称
+	Sn               string `json:"sn"`                // sn
+	Status           string `json:"status"`            // 1-停用 2-正常
+	UserId           int    `json:"userId"`            // 绑定的用户id
 	common.ControlBy `swaggerignore:"true"`
 }
 

+ 12 - 5
app/admin/service/dto/company.go

@@ -31,10 +31,11 @@ func (m *CompanyGetAllReq) GetNeedSearch() interface{} {
 }
 
 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
+	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
+	ParentId int    `json:"parentId" example:"0" vd:"?"`                    //上级公司
 
 	common.ControlBy `swaggerignore:"true"`
 }
@@ -44,7 +45,7 @@ func (s *CompanyInsertReq) Generate(model *model.SysDept) {
 		model.Id = s.Id
 	}
 	model.Name = s.Name
-	model.ParentId = 0
+	model.ParentId = s.ParentId
 	model.Remark = s.Remark
 	model.ColdKey = s.ColdKey
 }
@@ -113,3 +114,9 @@ type AppletCompanyGetPageReq struct {
 func (m *AppletCompanyGetPageReq) GetNeedSearch() interface{} {
 	return *m
 }
+
+type DeptLabel struct {
+	Id       int         `gorm:"-" json:"id"`
+	Label    string      `gorm:"-" json:"label"`
+	Children []DeptLabel `gorm:"-" json:"children"`
+}

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

@@ -25,7 +25,7 @@ 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-正启用
+	Status           string `json:"status"`                               // 1-停用 2-正
 	common.ControlBy `swaggerignore:"true"`
 	common.DeptBy    `swaggerignore:"true"`
 }
@@ -54,9 +54,9 @@ func (s *CoolerBoxInsertReq) GetId() interface{} {
 
 type CoolerBoxUpdateReq struct {
 	Id               int    `json:"id" comment:"编码"` // 编码
-	Name             string `json:"Name"`            // 保温箱名称
+	Name             string `json:"name"`            // 保温箱名称
 	Sn               string `json:"sn"`              // sn
-	Status           string `json:"status"`          // 1-停用 2-正启用
+	Status           string `json:"status"`          // 1-停用 2-正
 	common.ControlBy `swaggerignore:"true"`
 }
 
@@ -103,7 +103,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"`
 }

+ 93 - 0
app/admin/service/dto/ice_locker.go

@@ -0,0 +1,93 @@
+package dto
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/common/dto"
+	common "cold-delivery/common/model"
+)
+
+type IceLockerGetPageReq struct {
+	dto.Pagination `search:"-"`
+	Name           string `form:"name" search:"type:contains;column:name;table:ice_locker"`  // 冰柜名称
+	Status         string `form:"status" search:"type:exact;column:status;table:ice_locker"` // 状态
+	IceLockerOrder
+}
+
+type IceLockerOrder struct {
+	CreatedAtOrder string `search:"type:order;column:created_at;table:ice_locker" form:"createdAtOrder" default:"desc"`
+}
+
+func (m *IceLockerGetPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+type IceLockerInsertReq struct {
+	Id               int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
+	Name             string `json:"name" vd:"len($)>0;msg:'冷藏柜不能为空'"`     // 冰柜名称
+	Status           string `json:"status"`                               // 1-停用 2-正常
+	common.ControlBy `swaggerignore:"true"`
+	common.DeptBy    `swaggerignore:"true"`
+}
+
+func (s *IceLockerInsertReq) Generate(model *model.IceLocker) {
+	if s.Id != 0 {
+		model.Id = s.Id
+	}
+	model.Name = s.Name
+	model.Status = s.Status
+	if s.ControlBy.UpdateBy != 0 {
+		model.UpdateBy = s.UpdateBy
+	}
+	if s.ControlBy.CreateBy != 0 {
+		model.CreateBy = s.CreateBy
+	}
+	if s.DeptBy.DeptId != 0 {
+		model.DeptId = s.DeptId
+	}
+}
+
+func (s *IceLockerInsertReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceLockerUpdateReq struct {
+	Id               int    `json:"id" comment:"编码"`                    // 编码
+	Name             string `json:"name" vd:"len($)>0;msg:'冷藏柜名称不能为空'"` // 冰柜名称
+	Status           string `json:"status"`                             // 1-停用 2-正常
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceLockerUpdateReq) Generate(model *model.IceLocker) {
+	if s.Id != 0 {
+		model.Id = s.Id
+	}
+	model.Name = s.Name
+	model.Status = s.Status
+	if s.ControlBy.UpdateBy != 0 {
+		model.UpdateBy = s.UpdateBy
+	}
+	if s.ControlBy.CreateBy != 0 {
+		model.CreateBy = s.CreateBy
+	}
+}
+
+func (s *IceLockerUpdateReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceLockerGetReq struct {
+	Id int `uri:"id"`
+}
+
+func (s *IceLockerGetReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceLockerDeleteReq struct {
+	Id               int `json:"id"`
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceLockerDeleteReq) GetId() interface{} {
+	return s.Id
+}

+ 169 - 0
app/admin/service/dto/ice_raft.go

@@ -0,0 +1,169 @@
+package dto
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/common/dto"
+	common "cold-delivery/common/model"
+)
+
+type IceRaftGetPageReq struct {
+	dto.Pagination `search:"-"`
+	Code           string `form:"code" search:"type:contains;column:code;table:ice_raft"`  // 冰排名称
+	Status         string `form:"status" search:"type:exact;column:status;table:ice_raft"` // 状态
+	IceRaftOrder
+}
+type IceRaftGetNewestRecordPageReq struct {
+	dto.Pagination `search:"-"`
+	Code           string `form:"code" search:"type:contains;column:code;table:ice_raft"` // 冰排名称
+	Status         string `form:"status" search:"-"`                                      // 状态
+	IceLockerId    int    `form:"iceLockerId" search:"-"`                                 // 冰柜id
+	CoolerBoxId    int    `form:"coolerBoxId" search:"-"`                                 // 保温箱id
+	IceRaftOrder
+}
+type GetPageByCoolerBoxIdReq struct {
+	CoolerBoxId int `form:"coolerBoxId" search:"-"` // 保温箱id
+	IceRaftOrder
+}
+
+type IceRaftOrder struct {
+	CreatedAtOrder string `search:"type:order;column:created_at;table:ice_raft" form:"createdAtOrder" default:"desc"`
+}
+
+func (m *IceRaftGetPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+func (m *IceRaftGetNewestRecordPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+type IceRaftInsertReq struct {
+	Id               int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
+	Code             string `json:"code" vd:"len($)>0;msg:'冰排编号不能为空'"`    // 冰排名称
+	Status           string `json:"status"`                               // 1-停用 2-正常
+	common.ControlBy `swaggerignore:"true"`
+	common.DeptBy    `swaggerignore:"true"`
+}
+
+func (s *IceRaftInsertReq) Generate(model *model.IceRaft) {
+	if s.Id != 0 {
+		model.Id = s.Id
+	}
+	model.Code = s.Code
+	model.Status = s.Status
+	if s.ControlBy.UpdateBy != 0 {
+		model.UpdateBy = s.UpdateBy
+	}
+	if s.ControlBy.CreateBy != 0 {
+		model.CreateBy = s.CreateBy
+	}
+	if s.DeptBy.DeptId != 0 {
+		model.DeptId = s.DeptId
+	}
+}
+
+func (s *IceRaftInsertReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceRaftUpdateReq struct {
+	Id               int    `json:"id" comment:"编码"`                   // 编码
+	Code             string `json:"code" vd:"len($)>0;msg:'冰排编号不能为空'"` // 冰排名称
+	Status           string `json:"status"`                            // 1-停用 2-正常
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceRaftUpdateReq) Generate(model *model.IceRaft) {
+	if s.Id != 0 {
+		model.Id = s.Id
+	}
+	model.Code = s.Code
+	model.Status = s.Status
+	if s.ControlBy.UpdateBy != 0 {
+		model.UpdateBy = s.UpdateBy
+	}
+	if s.ControlBy.CreateBy != 0 {
+		model.CreateBy = s.CreateBy
+	}
+}
+
+func (s *IceRaftUpdateReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceRaftGetReq struct {
+	Id int `uri:"id"`
+}
+
+func (s *IceRaftGetReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceRaftDeleteReq struct {
+	Id               int `json:"id"`
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceRaftDeleteReq) GetId() interface{} {
+	return s.Id
+}
+
+type IceRaftInStorageReq struct {
+	Code             []string `json:"code"`
+	IceLockerId      int      `json:"iceLockerId"` // 冰柜id
+	FreezeClaim      float32  `json:"freezeClaim"` // 冷冻要求
+	common.ControlBy `swaggerignore:"true"`
+}
+type IceRaftOutStorageReq struct {
+	Code             []string `json:"code"`
+	CoolerBoxId      int      `json:"coolerBoxId"` // 保温箱id
+	common.ControlBy `swaggerignore:"true"`
+}
+
+type IceRaftRecordGetPageReq struct {
+	dto.Pagination `search:"-"`
+	IceRaftId      int    `form:"iceRaftId" search:"type:exact;column:ice_raft_id;table:ice_raft_record" vd:"$>0;msg:'冰排id不能为空'"` // 冰排id
+	Status         string `form:"status" search:"type:exact;column:status;table:ice_raft_record"`                                 // 状态
+	IceRaftRecordOrder
+}
+type IceRaftRecordOrder struct {
+	CreatedAtOrder string `search:"type:order;column:created_at;table:ice_raft_record" form:"createdAtOrder" default:"desc"`
+}
+
+func (m *IceRaftRecordGetPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+type IceRaftRecordUpdateReq struct {
+	Id               int     `json:"id" comment:"编码"` // 编码
+	IceLockerId      int     `json:"iceLockerId"`     // 冰柜id
+	CoolerBoxId      int     `json:"coolerBoxId"`     // 保温箱id
+	FreezeClaim      float32 `json:"freezeClaim"`     // 冷冻要求 单位小时
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceRaftRecordUpdateReq) GetId() interface{} {
+	return s.Id
+}
+func (s *IceRaftRecordUpdateReq) Generate(model *model.IceRaftRecord) {
+	if s.Id != 0 {
+		model.Id = s.Id
+	}
+	model.IceLockerId = s.IceLockerId
+	model.CoolerBoxId = s.CoolerBoxId
+	model.FreezeClaim = s.FreezeClaim
+	if s.ControlBy.UpdateBy != 0 {
+		model.UpdateBy = s.UpdateBy
+	}
+	if s.ControlBy.CreateBy != 0 {
+		model.CreateBy = s.CreateBy
+	}
+}
+
+type IceRaftRecordDeleteReq struct {
+	Id               int `json:"id"`
+	common.ControlBy `swaggerignore:"true"`
+}
+
+func (s *IceRaftRecordDeleteReq) GetId() interface{} {
+	return s.Id
+}

+ 3 - 3
app/admin/service/dto/warehouse.go

@@ -8,7 +8,7 @@ import (
 
 type WarehouseGetPageReq struct {
 	dto.Pagination `search:"-"`
-	//Name           string `form:"name" search:"type:contains;column:name;table:warehouse"` // 仓库名称
+	//Code           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:"-"`                                   // 是否绑定司机
@@ -28,7 +28,7 @@ type WarehouseInsertReq 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"`
 	common.DeptBy    `swaggerignore:"true"`
@@ -63,7 +63,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"`
 }

+ 33 - 7
app/admin/service/dto/waybill.go

@@ -55,6 +55,8 @@ type WaybillInsertReq struct {
 	ConsigneeAddressPhone   string `json:"consigneeAddressPhone"`                //收发货地址电话
 	DeliveryName            string `json:"deliveryName"`                         // 配送姓名
 	DeliveryPhone           string `json:"deliveryPhone"`                        // 配送电话
+	DeliveryId              int    `json:"deliveryId"`                           // 配送人id
+	ReCheckId               int    `json:"reCheckId"`                            // 复核人id
 	Quantity                int    `json:"quantity"`                             // 药品数量
 	Remark                  string `json:"remark"`                               // 运输备注
 	TamperProofLabel        string `json:"tamperProofLabel"`                     // 防拆码标签
@@ -77,6 +79,8 @@ func (s *WaybillInsertReq) Generate(m *model.Waybill) {
 	m.ConsigneeAddressPhone = s.ConsigneeAddressPhone
 	m.DeliveryName = s.DeliveryName
 	m.DeliveryPhone = s.DeliveryPhone
+	m.DeliveryId = s.DeliveryId
+	m.ReCheckId = s.ReCheckId
 	m.Quantity = s.Quantity
 	m.Remark = s.Remark
 	m.TamperProofLabel = s.TamperProofLabel
@@ -109,6 +113,8 @@ type WaybillUpdateReq struct {
 	ConsigneeAddressPhone   string `json:"consigneeAddressPhone"`                //收发货地址电话
 	DeliveryName            string `json:"deliveryName"`                         // 配送姓名
 	DeliveryPhone           string `json:"deliveryPhone"`                        // 配送电话
+	DeliveryId              int    `json:"deliveryId"`                           // 配送id
+	ReCheckId               int    `json:"reCheckId"`                            // 复核人id
 	TamperProofLabel        string `json:"tamperProofLabel"`                     //防拆标签
 	TamperProofLabelImg     string `json:"tamperProofLabelImg"`                  //防拆码标签图片
 	DeliveryCondition       string `json:"deliveryCondition"`                    //配送要求
@@ -131,6 +137,8 @@ func (s *WaybillUpdateReq) Generate(m *model.Waybill) {
 	m.ConsigneeAddressPhone = s.ConsigneeAddressPhone
 	m.DeliveryName = s.DeliveryName
 	m.DeliveryPhone = s.DeliveryPhone
+	m.DeliveryId = s.DeliveryId
+	m.ReCheckId = s.ReCheckId
 	m.Quantity = s.Quantity
 	m.Remark = s.Remark
 	m.TamperProofLabel = s.TamperProofLabel
@@ -176,8 +184,10 @@ func (s *WaybillUpdateStatusReq) GetId() interface{} {
 }
 
 type WaybillDeliveryReq struct {
-	WaybillIds       []int `json:"waybillIds"`  // 运单id
-	CoolerBoxId      int   `json:"coolerBoxId"` // 保温箱id
+	WaybillIds       []int    `json:"waybillIds"`                        // 运单id
+	CoolerBoxId      int      `json:"coolerBoxId"`                       // 保温箱id
+	IceRaftCode      []string `json:"iceRaftCode"`                       // 冰排code
+	OldIceRaftCode   []string `json:"oldIceRaftCode" swaggerignore:"true"` // 旧的冰排code
 	model2.ControlBy `swaggerignore:"true"`
 	model2.DeptBy    `swaggerignore:"true"`
 }
@@ -206,15 +216,17 @@ 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 WaybillReceiptReq struct {
-	Status          int    `json:"status"`                      // 3 已签收 5 已拒收
-	RejectionReason string `json:"rejectionReason"`             // 拒收原因
-	WaybillNo       string `json:"waybillNo"  gorm:"size:128"`  // 订单编号
-	ReceiptImg      string `json:"receiptImg"  gorm:"size:128"` // 签收图片
+	Status          int    `json:"status"`          // 3 已签收 5 已拒收
+	RejectionReason string `json:"rejectionReason"` // 拒收原因
+	WaybillNo       string `json:"waybillNo"`       // 订单编号
+	ReceiptImg      string `json:"receiptImg"`      // 签收图片
+	AssessStar      int    `json:"assessStar"`      // 评价星星
+	AssessContent   string `json:"assessContent"`   // 评价内容
 }
 
 type WaybillGetAppletPageReq struct {
@@ -241,6 +253,20 @@ type WaybillStatsReq struct {
 	Type string `form:"type"` // 类型 month-月 year-年
 }
 
+type WaybillUserStatsReq struct {
+	Date     string `form:"date"`     // 日期
+	Type     string `form:"type"`     // 类型 month-月 year-年
+	UserType string `form:"userType"` // 配送员 delivery-配送员 reCheck-复核员
+	UserIds  []int  `form:"userId"`   // 用户id
+}
+
+type WaybillUserStats struct {
+	Name  string
+	Stats []struct {
+		Date string `json:"date"` // 日期 2024-01-01
+		Num  int64  `json:"num"`  // 数量
+	} `json:"stats"`
+}
 type WaybillStatsRes struct {
 	TodayNum        int64 `json:"todayNum"`        // 今日总运单数
 	WaitDeliveryNum int64 `json:"waitDeliveryNum"` // 已下单

+ 190 - 0
app/admin/service/ice_locker.go

@@ -0,0 +1,190 @@
+package service
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/app/admin/service/dto"
+	"cold-delivery/common/actions"
+	cDto "cold-delivery/common/dto"
+	"cold-delivery/common/global"
+	"errors"
+	"gogs.baozhida.cn/zoie/OAuth-core/service"
+	"gorm.io/gorm"
+)
+
+type IceLocker struct {
+	service.Service
+}
+
+// GetPage 获取IceLocker列表
+func (e *IceLocker) GetPage(c *dto.IceLockerGetPageReq, list *[]model.IceLocker, count *int64, p *actions.DataPermission) error {
+	var err error
+	var data model.IceLocker
+
+	if c.PageSize == 9999 {
+		err = e.Orm.Model(&data).
+			Scopes(
+				actions.Permission(data.TableName(), p),
+			).
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	} else {
+		err = e.Orm.Model(&data).
+			Scopes(
+				cDto.MakeCondition(c.GetNeedSearch()),
+				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+				actions.Permission(data.TableName(), p),
+			).
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	}
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}
+
+// Get 获取IceLocker对象
+func (e *IceLocker) Get(d *dto.IceLockerGetReq, iceLockerModel *model.IceLocker, p *actions.DataPermission) error {
+	err := e.Orm.
+		Scopes(actions.Permission(iceLockerModel.TableName(), p)).
+		First(iceLockerModel, d.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.GetNotFoundOrNoPermissionErr
+		}
+		return global.GetFailedErr
+	}
+
+	return nil
+}
+
+// Insert 创建IceLocker对象
+func (e *IceLocker) Insert(c *dto.IceLockerInsertReq) error {
+	var err error
+	var data model.IceLocker
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var k int64
+	err = tx.Model(&data).Where("name = ? and dept_id = ?", c.Name,c.DeptId).Count(&k).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.CreateFailedErr
+	}
+	if k > 0 {
+		err = errors.New("冰柜名称已存在!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+
+	// 添加冰柜
+	c.Generate(&data)
+	err = tx.Create(&data).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.CreateFailedErr
+	}
+	c.Id = data.Id
+
+	return nil
+
+}
+
+// Update 修改IceLocker对象
+func (e *IceLocker) Update(c *dto.IceLockerUpdateReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceLockerModel = model.IceLocker{}
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceLockerModel.TableName(), p)).
+		First(&iceLockerModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.UpdateNotFoundOrNoPermissionErr
+		}
+		return global.UpdateFailedErr
+	}
+
+	if iceLockerModel.Name != c.Name {
+		var k int64
+		var data = model.IceLocker{}
+		err = tx.Model(&data).Where("name = ? and dept_id = ?", c.Name,iceLockerModel.DeptId).Count(&k).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return global.CreateFailedErr
+		}
+		if k > 0 {
+			err = errors.New("冰柜名称已存在!")
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+	}
+
+	c.Generate(&iceLockerModel)
+	err = tx.Save(&iceLockerModel).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.UpdateFailedErr
+	}
+
+	c.Id = iceLockerModel.Id
+	return nil
+}
+
+// Remove 删除IceLocker
+func (e *IceLocker) Remove(c *dto.IceLockerDeleteReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceLockerModel model.IceLocker
+
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceLockerModel.TableName(), p)).
+		First(&iceLockerModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.DeleteNotFoundOrNoPermissionErr
+		}
+		return global.DeleteFailedErr
+	}
+
+	db := tx.Delete(&iceLockerModel)
+
+	if err = db.Error; err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.DeleteFailedErr
+	}
+	if db.RowsAffected == 0 {
+		return global.DeleteNotFoundOrNoPermissionErr
+	}
+
+	return nil
+}

+ 588 - 0
app/admin/service/ice_raft.go

@@ -0,0 +1,588 @@
+package service
+
+import (
+	"cold-delivery/app/admin/model"
+	"cold-delivery/app/admin/service/dto"
+	"cold-delivery/common/actions"
+	cDto "cold-delivery/common/dto"
+	"cold-delivery/common/global"
+	model2 "cold-delivery/common/model"
+	"errors"
+	"fmt"
+	"gogs.baozhida.cn/zoie/OAuth-core/service"
+	"gorm.io/gorm"
+	"time"
+)
+
+type IceRaft struct {
+	service.Service
+}
+
+// GetPage 获取IceRaft列表
+func (e *IceRaft) GetPage(c *dto.IceRaftGetPageReq, list *[]model.IceRaft, count *int64, p *actions.DataPermission) error {
+	var err error
+	var data model.IceRaft
+
+	if c.PageSize == 9999 {
+		err = e.Orm.Model(&data).
+			Scopes(
+				actions.Permission(data.TableName(), p),
+			).
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	} else {
+		err = e.Orm.Model(&data).
+			Scopes(
+				cDto.MakeCondition(c.GetNeedSearch()),
+				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+				actions.Permission(data.TableName(), p),
+			).
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	}
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}
+func IceRaftNameRecordStatusScopes(status string) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		if len(status) == 0 {
+			return db
+		}
+		return db.Where("ice_raft_record.status = ?", status)
+	}
+}
+func IceRaftNameRecordIceLockerIdScopes(iceLockerId int) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		if iceLockerId == 0 {
+			return db
+		}
+		return db.Where("ice_raft_record.ice_locker_id = ? and ice_raft_record.cooler_box_id = 0", iceLockerId)
+	}
+}
+
+func IceRaftNameRecordCoolerBoxIdScopes(coolerBoxId int) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		if coolerBoxId == 0 {
+			return db
+		}
+		return db.Where("ice_raft_record.cooler_box_id = ?", coolerBoxId)
+	}
+}
+
+func (e *IceRaft) GetRecordPage(c *dto.IceRaftGetNewestRecordPageReq, list *[]model.IceRaft, count *int64, p *actions.DataPermission) error {
+	var err error
+	var data model.IceRaft
+
+	if c.PageSize == 9999 {
+		err = e.Orm.Model(&data).
+			Scopes(
+				actions.Permission(data.TableName(), p),
+			).
+			Joins("left join ice_raft_record on ice_raft.ice_raft_record_id = ice_raft_record.id").
+			Preload("IceRaftRecord.IceLocker").
+			Preload("IceRaftRecord.CoolerBox").
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	} else {
+		err = e.Orm.Model(&data).
+			Scopes(
+				cDto.MakeCondition(c.GetNeedSearch()),
+				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+				actions.Permission(data.TableName(), p),
+				IceRaftNameRecordStatusScopes(c.Status),
+				IceRaftNameRecordIceLockerIdScopes(c.IceLockerId),
+				IceRaftNameRecordCoolerBoxIdScopes(c.CoolerBoxId),
+			).
+			Where("ice_raft_record.deleted_at is null").
+			Joins("left join ice_raft_record on ice_raft.ice_raft_record_id = ice_raft_record.id").
+			Preload("IceRaftRecord.IceLocker").
+			Preload("IceRaftRecord.CoolerBox").
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	}
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}
+
+// Get 获取IceRaft对象
+func (e *IceRaft) Get(d *dto.IceRaftGetReq, iceRaftModel *model.IceRaft, p *actions.DataPermission) error {
+	err := e.Orm.
+		Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+		First(iceRaftModel, d.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.GetNotFoundOrNoPermissionErr
+		}
+		return global.GetFailedErr
+	}
+
+	return nil
+}
+
+// Insert 创建IceRaft对象
+func (e *IceRaft) Insert(c *dto.IceRaftInsertReq) error {
+	var err error
+	var data model.IceRaft
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var k int64
+	err = tx.Model(&data).Where("code = ? and dept_id = ?", c.Code, c.DeptId).Count(&k).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.CreateFailedErr
+	}
+	if k > 0 {
+		err = errors.New("冰排编号已存在!")
+		e.Log.Errorf("db error: %s", err)
+		return err
+	}
+
+	// 添加冰柜
+	c.Generate(&data)
+	err = tx.Create(&data).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.CreateFailedErr
+	}
+	c.Id = data.Id
+
+	return nil
+
+}
+
+// Update 修改IceRaft对象
+func (e *IceRaft) Update(c *dto.IceRaftUpdateReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceRaftModel = model.IceRaft{}
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+		First(&iceRaftModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.UpdateNotFoundOrNoPermissionErr
+		}
+		return global.UpdateFailedErr
+	}
+
+	if iceRaftModel.Code != c.Code {
+		var k int64
+		var data = model.IceRaft{}
+		err = tx.Model(&data).Where("code = ? and dept_id = ?", c.Code, iceRaftModel.DeptId).Count(&k).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return global.CreateFailedErr
+		}
+		if k > 0 {
+			err = errors.New("冰排编号已存在!")
+			e.Log.Errorf("db error: %s", err)
+			return err
+		}
+	}
+
+	c.Generate(&iceRaftModel)
+	err = tx.Save(&iceRaftModel).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.UpdateFailedErr
+	}
+
+	c.Id = iceRaftModel.Id
+	return nil
+}
+
+// Remove 删除IceRaft
+func (e *IceRaft) Remove(c *dto.IceRaftDeleteReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceRaftModel model.IceRaft
+
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+		First(&iceRaftModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.DeleteNotFoundOrNoPermissionErr
+		}
+		return global.DeleteFailedErr
+	}
+
+	db := tx.Delete(&iceRaftModel)
+
+	if err = db.Error; err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.DeleteFailedErr
+	}
+	if db.RowsAffected == 0 {
+		return global.DeleteNotFoundOrNoPermissionErr
+	}
+
+	return nil
+}
+func (e *IceRaft) InStorage(c *dto.IceRaftInStorageReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	// 查询冷藏柜信息
+	var iceLockerModel model.IceLocker
+	err = e.Orm.Scopes(actions.Permission(iceLockerModel.TableName(), p)).
+		First(&iceLockerModel, c.IceLockerId).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return errors.New("获取冷藏柜信息失败")
+	}
+	if iceLockerModel.Status != model.IceLockerStatusNormal {
+		err = errors.New("冷藏柜状态异常")
+		return err
+	}
+
+	for _, code := range c.Code {
+		// 查询冰排是否存在
+		var iceRaftModel model.IceRaft
+		err = e.Orm.Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+			Where("code = ?", code).
+			Preload("IceRaftRecord").
+			First(&iceRaftModel).Error
+		if err != nil {
+			// 冰排编号不存在,添加
+			if errors.Is(err, gorm.ErrRecordNotFound) {
+				iceRaftModel.Code = code
+				iceRaftModel.Status = model.IceRaftStatusNormal
+				iceRaftModel.DeptId = p.DeptId
+				iceRaftModel.CreateBy = p.UserId
+				err = tx.Create(&iceRaftModel).Error
+				if err != nil {
+					e.Log.Errorf("db error: %s", err)
+					return errors.New(fmt.Sprintf("添加冰排【%s】信息失败", code))
+				}
+			}else {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New("获取冰排信息失败")
+			}
+
+		}
+
+		if iceRaftModel.Status != model.IceRaftStatusNormal {
+			err = errors.New(fmt.Sprintf("冰排【%s】状态异常", code))
+			return err
+		}
+
+		if iceRaftModel.IceRaftRecord.Status == model.IceRaftRecordStatusFreezing || iceRaftModel.IceRaftRecord.Status == model.IceRaftRecordStatusWaitUse {
+			err = errors.New(fmt.Sprintf("冰排【%s】状态为%s,入库失败!", code, model.GetIceRaftRecordStatus(iceRaftModel.IceRaftRecord.Status)))
+			return err
+		}
+
+		// 更新冰排状态 使用中 -> 已完成
+		err = tx.Model(&model.IceRaftRecord{}).
+			Where("ice_raft_id = ? and status = ?", iceRaftModel.Id, model.IceRaftRecordStatusUsing).
+			Updates(map[string]interface{}{
+				"status": model.IceRaftRecordStatusFinish,
+			}).Error
+
+		// 添加冰排记录
+		var iceRaftRecordModel = model.IceRaftRecord{
+			IceLockerId:   iceLockerModel.Id,
+			IceRaftId:     iceRaftModel.Id,
+			Status:        model.IceRaftRecordStatusFreezing,
+			InStorageTime: model2.Time(time.Now()),
+			FreezeClaim:   c.FreezeClaim,
+			DeptBy: model2.DeptBy{
+				DeptId: p.DeptId,
+			},
+		}
+		err = tx.Create(&iceRaftRecordModel).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New("添加冰排记录失败")
+		}
+
+		// 更新冰排使用记录
+		err = tx.Model(&iceRaftModel).
+			Where("id = ?", iceRaftModel.Id).
+			Updates(map[string]interface{}{
+				"ice_raft_record_id": iceRaftRecordModel.Id,
+			}).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New("关联冰排记录失败")
+		}
+
+	}
+
+	return nil
+}
+func (e *IceRaft) OutStorage(c *dto.IceRaftOutStorageReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	// 查询保温箱信息
+	var coolerBoxModel model.CoolerBox
+	err = e.Orm.Scopes(actions.Permission(coolerBoxModel.TableName(), p)).
+		First(&coolerBoxModel, c.CoolerBoxId).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return errors.New("获取保温箱信息失败")
+	}
+	if coolerBoxModel.Status != model.CoolerBoxStatusNormal {
+		err = errors.New("保温箱状态异常")
+		return err
+	}
+
+	for _, code := range c.Code {
+		// 查询冰排是否存在
+		var iceRaftModel model.IceRaft
+		err = e.Orm.Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+			Where("code = ?", code).
+			Preload("IceRaftRecord").
+			First(&iceRaftModel).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New("获取冰排信息失败")
+		}
+
+		if iceRaftModel.Status != model.IceRaftStatusNormal {
+			err = errors.New(fmt.Sprintf("冰排【%s】状态异常", code))
+			return err
+		}
+
+		// 获取冰排记录
+		var iceRaftRecordModel model.IceRaftRecord
+		err = e.Orm.Scopes(actions.Permission(iceRaftRecordModel.TableName(), p)).
+			Where("ice_raft_id = ?", iceRaftModel.Id).
+			First(&iceRaftRecordModel, iceRaftModel.IceRaftRecordId).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New("获取冰排记录失败")
+		}
+
+		if len(iceRaftRecordModel.OutStorageTime.String()) > 0 {
+			err = errors.New(fmt.Sprintf("冰排【%s】已出库,禁止出库!", code))
+			return err
+		}
+
+		inTime := iceRaftRecordModel.InStorageTime.Local()
+		if inTime.Add(time.Hour * time.Duration(iceRaftRecordModel.FreezeClaim)).After(time.Now()) {
+			err = errors.New(fmt.Sprintf("冰排【%s】未达到冷冻时长,禁止出库", code))
+			return err
+		}
+
+		iceRaftRecordModel.Status = model.IceRaftRecordStatusUsing
+		iceRaftRecordModel.CoolerBoxId = coolerBoxModel.Id
+		iceRaftRecordModel.OutStorageTime = model2.Time(time.Now())
+		err = tx.Save(&iceRaftRecordModel).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New("保存冰排记录失败")
+		}
+
+	}
+
+	return nil
+}
+
+// 查询保温箱关联的冰排
+func (e *IceRaft) GetPageByCoolerBoxId(coolerBoxId int, list *[]string, p *actions.DataPermission) error {
+	var data model.IceRaft
+	err := e.Orm.Model(&data).
+		Select("code").
+		Scopes(
+			actions.Permission(data.TableName(), p),
+			IceRaftNameRecordStatusScopes(model.IceRaftRecordStatusUsing),
+			IceRaftNameRecordCoolerBoxIdScopes(coolerBoxId),
+		).
+		Joins("left join ice_raft_record on ice_raft.ice_raft_record_id = ice_raft_record.id").
+		Find(list).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}
+
+type IceRaftRecord struct {
+	service.Service
+}
+
+// GetPage 获取IceRaft列表
+func (e *IceRaftRecord) GetPage(c *dto.IceRaftRecordGetPageReq, list *[]model.IceRaftRecord, count *int64, p *actions.DataPermission) error {
+	var err error
+	var data model.IceRaftRecord
+
+	if c.PageSize == 9999 {
+		err = e.Orm.Model(&data).
+			Scopes(
+				actions.Permission(data.TableName(), p),
+			).
+			Preload("IceLocker").
+			Preload("CoolerBox").
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	} else {
+		err = e.Orm.Model(&data).
+			Scopes(
+				cDto.MakeCondition(c.GetNeedSearch()),
+				cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+				actions.Permission(data.TableName(), p),
+			).
+			Preload("IceLocker").
+			Preload("CoolerBox").
+			Find(list).Limit(-1).Offset(-1).
+			Count(count).Error
+	}
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}
+
+// Update 修改IceRaft对象
+func (e *IceRaftRecord) Update(c *dto.IceRaftRecordUpdateReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceRaftRecordModel = model.IceRaftRecord{}
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceRaftRecordModel.TableName(), p)).
+		First(&iceRaftRecordModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.UpdateNotFoundOrNoPermissionErr
+		}
+		return global.UpdateFailedErr
+	}
+
+	iceRaftRecordModel.IceLockerId = c.IceLockerId
+	iceRaftRecordModel.FreezeClaim = c.FreezeClaim
+	if len(iceRaftRecordModel.OutStorageTime.String()) > 0 {
+		iceRaftRecordModel.CoolerBoxId = c.CoolerBoxId
+	}
+	err = tx.Save(&iceRaftRecordModel).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.UpdateFailedErr
+	}
+
+	c.Id = iceRaftRecordModel.Id
+	return nil
+}
+
+// Remove 删除IceRaft
+func (e *IceRaftRecord) Remove(c *dto.IceRaftRecordDeleteReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+
+	var iceRaftRecordModel model.IceRaftRecord
+
+	// 查询冰柜是否存在
+	err = e.Orm.Scopes(actions.Permission(iceRaftRecordModel.TableName(), p)).
+		First(&iceRaftRecordModel, c.GetId()).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return global.DeleteNotFoundOrNoPermissionErr
+		}
+		return global.DeleteFailedErr
+	}
+
+	db := tx.Delete(&iceRaftRecordModel)
+
+	if err = db.Error; err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.DeleteFailedErr
+	}
+	if db.RowsAffected == 0 {
+		return global.DeleteNotFoundOrNoPermissionErr
+	}
+	// 更改冰排关联状态
+
+	var iceRaftModel model.IceRaft
+	var secondIceRaftRecordModel model.IceRaftRecord
+	err = e.Orm.Scopes(actions.Permission(iceRaftModel.TableName(), p)).
+		First(&iceRaftModel, iceRaftRecordModel.IceRaftId).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.DeleteFailedErr
+	}
+	err = e.Orm.Scopes(actions.Permission(secondIceRaftRecordModel.TableName(), p)).
+		Where("ice_raft_id = ? and id != ?", iceRaftRecordModel.IceRaftId, iceRaftRecordModel.Id).
+		First(&secondIceRaftRecordModel).Error
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			iceRaftModel.IceRaftRecordId = 0
+		}
+	}
+	iceRaftModel.IceRaftRecordId = secondIceRaftRecordModel.Id
+	err = tx.Save(&iceRaftModel).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.UpdateFailedErr
+	}
+	return nil
+}

+ 288 - 32
app/admin/service/waybill.go

@@ -38,6 +38,9 @@ func (e *Waybill) GetPage(c *dto.WaybillGetPageReq, list *[]model.Waybill, count
 				cDto.MakeCondition(c.GetNeedSearch()),
 				actions.Permission(data.TableName(), p),
 			).
+			Preload("CoolerBox").
+			Preload("Delivery").
+			Preload("ReCheck").
 			Find(list).Limit(-1).Offset(-1).
 			Count(count).Error
 		if err != nil {
@@ -54,6 +57,8 @@ func (e *Waybill) GetPage(c *dto.WaybillGetPageReq, list *[]model.Waybill, count
 			actions.Permission(data.TableName(), p),
 		).
 		Preload("CoolerBox").
+		Preload("Delivery").
+		Preload("ReCheck").
 		Find(list).Limit(-1).Offset(-1).
 		Count(count).Error
 	if err != nil {
@@ -85,8 +90,10 @@ func (e *Waybill) GetUserPage(c *dto.WaybillGetPageReq, list *[]model.Waybill, c
 			cDto.MakeCondition(c.GetNeedSearch()),
 			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 		).
-		Where("create_by = ?", p.UserId).
+		Where("delivery_id = ? or re_check_id = ?", p.UserId, p.UserId).
 		Preload("CoolerBox").
+		Preload("Delivery").
+		Preload("ReCheck").
 		Find(list).Limit(-1).Offset(-1).
 		Count(count).Error
 	if err != nil {
@@ -240,24 +247,24 @@ func (e *Waybill) Insert(c *dto.WaybillInsertReq) error {
 	c.Id = data.Id
 
 	// 保存收货地址
-	if c.SaveSenderAddress {
-		// 查询地址是否存在
-		address := model.Address{}
-		err = e.Orm.Where("address = ? and phone = ? and name = ? and dept_id = ? and address_type = 'sender'",
-			c.SenderAddressDetails, c.SenderAddressPhone, c.SenderAddressName, c.DeptId).First(&address).Error
-		if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
-			address.Address = c.SenderAddressDetails
-			address.Phone = c.SenderAddressPhone
-			address.Name = c.SenderAddressName
-			address.DeptId = c.DeptId
-			address.AddressType = "sender"
-			err = tx.Create(&address).Error
-			if err != nil {
-				e.Log.Errorf("创建地址失败: %s", err)
-				return global.CreateFailedErr
-			}
-		}
-	}
+	//if c.SaveSenderAddress {
+	//	// 查询地址是否存在
+	//	address := model.Address{}
+	//	err = e.Orm.Where("address = ? and phone = ? and name = ? and dept_id = ? and address_type = 'sender'",
+	//		c.SenderAddressDetails, c.SenderAddressPhone, c.SenderAddressName, c.DeptId).First(&address).Error
+	//	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
+	//		address.Address = c.SenderAddressDetails
+	//		address.Phone = c.SenderAddressPhone
+	//		address.Name = c.SenderAddressName
+	//		address.DeptId = c.DeptId
+	//		address.AddressType = "sender"
+	//		err = tx.Create(&address).Error
+	//		if err != nil {
+	//			e.Log.Errorf("创建地址失败: %s", err)
+	//			return global.CreateFailedErr
+	//		}
+	//	}
+	//}
 
 	// 保存发货地址
 	if c.SaveConsigneeAddress {
@@ -352,6 +359,8 @@ func (e *Waybill) UpdateStatus(c *dto.WaybillUpdateStatusReq, p *actions.DataPer
 	c.Id = waybillModel.Id
 	return nil
 }
+
+// 派单
 func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission) error {
 	var err error
 
@@ -364,6 +373,64 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 		}
 	}()
 
+	if !lib.StrListEqual(c.IceRaftCode, c.OldIceRaftCode) {
+		// 获取删除的冰排列表
+		//missingIceRaftCode = lib.FindStrListMissingData(c.OldIceRaftCode, c.IceRaftCode)
+
+		//if len(missingIceRaftCode) > 0 {
+		//	// 删除冰排出库记录
+		//	IceRaftList := []model.IceRaft{}
+		//	err = tx.Model(&model.IceRaft{}).Where("code in (?)", missingIceRaftCode).Find(&IceRaftList).Error
+		//	if err != nil {
+		//		e.Log.Errorf("db error: %s", err)
+		//		return errors.New("获取冰排列表失败")
+		//	}
+		//	for _, iceRaft := range IceRaftList {
+		//		// 获取冰排记录
+		//		err = tx.Model(&model.IceRaftRecord{}).
+		//			Where("ice_raft_id = ? and status = ?", iceRaft.Id, model.IceRaftRecordStatusUsing).
+		//			Updates(map[string]interface{}{
+		//				"status":           model.IceRaftRecordStatusWaitUse,
+		//				"out_storage_time": nil,
+		//				"cooler_box_id":    0,
+		//			}).Error
+		//		if err != nil {
+		//			e.Log.Errorf("db error: %s", err)
+		//			return errors.New(fmt.Sprintf("冰排【%s】返库失败", iceRaft.Code))
+		//		}
+		//	}
+		//}
+		// 获取新增的冰排列表
+		extraData := lib.FindStrListExtraData(c.OldIceRaftCode, c.IceRaftCode)
+		if len(extraData) > 0 {
+			for _, code := range extraData {
+				var iceRaft model.IceRaft
+				err = tx.Model(&model.IceRaft{}).Where("code = ?", code).First(&iceRaft).Error
+				if err != nil {
+					e.Log.Errorf("db error: %s", err)
+					if errors.Is(err, gorm.ErrRecordNotFound) {
+						return errors.New(fmt.Sprintf("冰排【%s】不存在", code))
+					} else {
+						return errors.New(fmt.Sprintf("获取冰排【%s】信息失败", code))
+					}
+
+				}
+				// 获取冰排记录
+				err = tx.Model(&model.IceRaftRecord{}).
+					Where("ice_raft_id = ? and status = ?", iceRaft.Id, model.IceRaftRecordStatusWaitUse).
+					Updates(map[string]interface{}{
+						"status":           model.IceRaftRecordStatusUsing,
+						"out_storage_time": time.Now(),
+						"cooler_box_id":    c.CoolerBoxId,
+					}).Error
+				if err != nil {
+					e.Log.Errorf("db error: %s", err)
+					return errors.New(fmt.Sprintf("冰排【%s】出库失败", iceRaft.Code))
+				}
+			}
+		}
+	}
+
 	var coolerBox = model.CoolerBox{}
 	// 查询保温箱信息
 	err = tx.Scopes(actions.Permission(coolerBox.TableName(), p)).
@@ -386,10 +453,14 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 			}
 			return global.UpdateFailedErr
 		}
-		if waybillModel.Status == model.WaybillStatusWaitDelivery || (waybillModel.Status == model.WaybillStatusInDelivery && waybillModel.CoolerBoxId != c.CoolerBoxId) {
+		waybillModeStatus := waybillModel.Status
+		if waybillModel.Status == model.WaybillStatusWaitDelivery ||
+			(waybillModel.Status == model.WaybillStatusInDelivery && waybillModel.CoolerBoxId != c.CoolerBoxId) ||
+			(waybillModel.Status == model.WaybillStatusInDelivery && !lib.StrListEqual(c.IceRaftCode, c.OldIceRaftCode)) {
 			waybillModel.Status = model.WaybillStatusInDelivery
 			waybillModel.CoolerBoxId = c.CoolerBoxId
 			waybillModel.DeliveryTime = model2.Time(time.Now())
+			waybillModel.IceRaftCode = c.IceRaftCode
 			err = tx.Save(&waybillModel).Error
 			if err != nil {
 				e.Log.Errorf("db error: %s", err)
@@ -472,21 +543,16 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 				return errors.New(fmt.Sprintf("保存运单任务信息失败:%s", err))
 			}
 
-			//if logistics.Id == 0 {
-			//	ss := sms.NewSMS(conf.ExtConfig.SubMail.Appid, conf.ExtConfig.SubMail.Signature)
-			//	res, err1 := ss.SmsXSend(waybillModel.ConsigneeAddressPhone, lib.AesEncryptCBC(waybillModel.WaybillNo, lib.AesKey))
-			//	if err1 != nil || res.Status != sms.SUCCESS {
-			//		e.Log.Errorf("发送短信验证码出现异常", zap.Any("res", res), zap.Error(err1))
-			//		err = errors.New("验证吗发送失败,请重试")
-			//		return err
-			//	}
-			//}
+		}
+
+		// 待派单状态下才发送短信
+		if waybillModeStatus == model.WaybillStatusWaitDelivery {
 			ss := sms.NewSMS(conf.ExtConfig.SubMail.Appid, conf.ExtConfig.SubMail.Signature)
 			addr := conf.ExtConfig.Applet.WaybillUrl + url.QueryEscape(lib.AesEncryptCBC(waybillModel.WaybillNo, lib.AesKey))
 			res, err1 := ss.SmsXSend(waybillModel.ConsigneeAddressPhone, addr)
 			if err1 != nil || res.Status != sms.SUCCESS {
-				e.Log.Errorf("发送短信验证码出现异常", zap.Any("res", res), zap.Error(err1))
-				err = errors.New("验证吗发送失败,请重试")
+				e.Log.Errorf("派单短信发送失败", zap.Any("res", res), zap.Error(err1))
+				err = errors.New("派单短信发送失败,请重试")
 				return err
 			}
 			waybillModel.SendLog = model.WaybillSendLog{
@@ -587,6 +653,9 @@ func (e *Waybill) Receipt(c *dto.WaybillReceiptReq, p *actions.DataPermission) e
 	waybillModel.RejectionReason = c.RejectionReason
 	waybillModel.ReceiptTime = model2.Time(time.Now())
 	waybillModel.ReceiptImg = c.ReceiptImg
+	waybillModel.AssessContent = c.AssessContent
+	waybillModel.AssessStar = c.AssessStar
+	waybillModel.DeliveryDuration = int(time.Now().Sub(waybillModel.DeliveryTime.Local()).Minutes())
 	err = tx.Save(&waybillModel).Error
 	if err != nil {
 		e.Log.Errorf("db error: %s", err)
@@ -611,7 +680,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.UpdateBy = p.UserId
+		//task.UpdateBy = p.UserIds
 		err = tx.Save(&task).Error
 		if err != nil {
 			e.Log.Errorf("db error: %s", err)
@@ -764,6 +833,193 @@ func (e *Waybill) GetBasicsStats(c *dto.WaybillStatsReq, p *actions.DataPermissi
 	return res
 }
 
+func GetDeliveryStatsScopes(userId int) func(db *gorm.DB) *gorm.DB {
+
+	return func(db *gorm.DB) *gorm.DB {
+		if userId == 0 {
+			return db
+		}
+		return db.Where("delivery_id = ?", userId)
+	}
+}
+func GetReCheckStatsScopes(userId int) func(db *gorm.DB) *gorm.DB {
+
+	return func(db *gorm.DB) *gorm.DB {
+		if userId == 0 {
+			return db
+		}
+		return db.Where("re_check_id = ?", userId)
+	}
+}
+
+// 获取配送员统计信息
+func (e *Waybill) GetDeliveryStats(c *dto.WaybillUserStatsReq, p *actions.DataPermission) (res []dto.WaybillUserStats) {
+	// 获取用户列表
+	var userList []model.SysUser
+	if err := e.Orm.Model(&userList).Scopes(actions.Permission(model.SysUser{}.TableName(), p), WaybillUserStatsUserIdScopes(c.UserIds)).
+		Where("status = 2").Find(&userList).Error; err != nil {
+		return nil
+	}
+	for _, user := range userList {
+		stats := e.GetUserStats(c, p, GetDeliveryStatsScopes(user.Id))
+		stats.Name = user.NickName
+		res = append(res, stats)
+	}
+	return
+}
+func (e *Waybill) GetReCheckStats(c *dto.WaybillUserStatsReq, p *actions.DataPermission) (res []dto.WaybillUserStats) {
+	// 获取用户列表
+	var userList []model.SysUser
+	if err := e.Orm.Model(&userList).Scopes(actions.Permission(model.SysUser{}.TableName(), p), WaybillUserStatsUserIdScopes(c.UserIds)).
+		Where("status = 2").Find(&userList).Error; err != nil {
+		return nil
+	}
+	for _, user := range userList {
+		stats := e.GetUserStats(c, p, GetReCheckStatsScopes(user.Id))
+		stats.Name = user.NickName
+		res = append(res, stats)
+	}
+	return
+}
+
+func WaybillUserStatsUserIdScopes(userIds []int) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		if len(userIds) == 0 {
+			return db
+		}
+		if len(userIds) == 1 {
+			return db.Where("user_id = ?", userIds[0])
+		}
+		return db.Where("user_id in (?)", userIds)
+	}
+}
+
+func GetUserBasicsStatsScopes(userId int) func(db *gorm.DB) *gorm.DB {
+
+	return func(db *gorm.DB) *gorm.DB {
+		if userId == 0 {
+			return db
+		}
+		return db.Where("delivery_id = ? or re_check_id = ? ", userId, userId)
+	}
+}
+func (e *Waybill) GetUserBasicsStats(c *dto.WaybillStatsReq, p *actions.DataPermission, scopes func(*gorm.DB) *gorm.DB) dto.WaybillStatsRes {
+	var res dto.WaybillStatsRes
+	var data model.Waybill
+	type DateCount struct {
+		Date  string
+		Count int64
+	}
+	yearCount := make([]DateCount, 0)
+	monthCount := make([]DateCount, 0)
+	now := time.Now()
+	todayStartTime := now.Format("2006-01-02") + " 00:00:00"
+	todayEndTime := now.Format("2006-01-02") + " 23:59:59"
+
+	// 获取上个月第一天
+	firstDayOfLastMonth := time.Date(now.Year(), now.Month()-1, 1, 0, 0, 0, 0, now.Location())
+
+	// 获取去年的第一天
+	firstDayOfLastYear := time.Date(now.Year()-1, time.January, 1, 0, 0, 0, 0, now.Location())
+	monthStartTime := firstDayOfLastMonth.Format("2006-01-02") + " 00:00:00"
+	yearStartTime := firstDayOfLastYear.Format("2006-01-02") + " 00:00:00"
+
+	// 今日总运单数
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("order_time between ? and ?", todayStartTime, todayEndTime).Count(&res.TodayNum)
+	// 待派单
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("status = ?", model.WaybillStatusWaitDelivery).Count(&res.WaitDeliveryNum)
+	// 配送中
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("status = ?", model.WaybillStatusInDelivery).Count(&res.InDeliveryNum)
+	// 已送达
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("status = ?", model.WaybillStatusReceipt).Count(&res.ReceiptNum)
+	// 已取消
+	e.Orm.Model(&data).Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("status = ?", model.WaybillStatusRejection).Count(&res.RejectionNum)
+	// 获取本月,上月数据
+	e.Orm.Model(&data).Select("date_format(order_time,'%Y%m') date,count(1) as count ").Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("order_time between ? and ?", monthStartTime, now).Group("date").Find(&monthCount)
+
+	for _, month := range monthCount {
+		if month.Date == now.Format("200601") {
+			res.ThisMonthNum = month.Count
+		}
+		if month.Date == firstDayOfLastMonth.Format("200601") {
+			res.LastMonthNum = month.Count
+		}
+	}
+
+	// 获取本年,上年数据
+	e.Orm.Model(&data).Select("date_format(order_time,'%Y') date,count(1) as count ").Scopes(actions.Permission(data.TableName(), p), scopes).
+		Where("order_time between ? and ?", yearStartTime, now).Group("date").Find(&yearCount)
+
+	for _, month := range yearCount {
+		if month.Date == now.Format("2006") {
+			res.ThisYearNum = month.Count
+		}
+		if month.Date == firstDayOfLastYear.Format("2006") {
+			res.LastYearNum = month.Count
+		}
+	}
+
+	if c.Type == "year" {
+		// 获取上个月第一天
+		year, _ := strconv.Atoi(c.Date)
+		firstDayOfyear := time.Date(year, 1, 1, 0, 0, 0, 0, now.Location())
+		lastDayOfyear := time.Date(year, 12, 31, 23, 59, 59, 0, now.Location())
+
+		// 年度数据统计
+		e.Orm.Model(&data).Select("date_format(order_time,'%Y-%m') date,count(1) as num ").Scopes(actions.Permission(data.TableName(), p), scopes).
+			Where("order_time between ? and ?", firstDayOfyear, lastDayOfyear).Group("date").Find(&res.Stats)
+	}
+
+	if c.Type == "month" {
+		// 获取上个月第一天
+		month, _ := time.Parse("2006-01", c.Date)
+		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), scopes).
+			Where("order_time between ? and ?", firstDayOfMonth, lastDayOfMonth).Group("date").Find(&res.Stats)
+	}
+
+	return res
+}
+func (e *Waybill) GetUserStats(c *dto.WaybillUserStatsReq, p *actions.DataPermission, scopes func(*gorm.DB) *gorm.DB) dto.WaybillUserStats {
+	var res dto.WaybillUserStats
+	var data model.Waybill
+	type DateCount struct {
+		Date  string
+		Count int64
+	}
+
+	now := time.Now()
+
+	if c.Type == "year" {
+		// 获取上个月第一天
+		year, _ := strconv.Atoi(c.Date)
+		firstDayOfyear := time.Date(year, 1, 1, 0, 0, 0, 0, now.Location())
+		lastDayOfyear := time.Date(year, 12, 31, 23, 59, 59, 0, now.Location())
+
+		// 年度数据统计
+		e.Orm.Model(&data).Select("date_format(order_time,'%Y-%m') date,count(1) as num ").Scopes(actions.Permission(data.TableName(), p), scopes).
+			Where("order_time between ? and ?", firstDayOfyear, lastDayOfyear).Group("date").Find(&res.Stats)
+	}
+
+	if c.Type == "month" {
+		// 获取上个月第一天
+		month, _ := time.Parse("2006-01", c.Date)
+		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), scopes).
+			Where("order_time between ? and ?", firstDayOfMonth, lastDayOfMonth).Group("date").Find(&res.Stats)
+	}
+
+	return res
+}
+
 // 获取运单所有温湿度素具
 func (e *Waybill) GetAllData(c *dto.WaybillGetByWaybillNoReq) ([]nats_server.DeviceData_R, []WaybillPDF, error) {
 	var err error

+ 205 - 0
app/jobs/jobbase.go

@@ -0,0 +1,205 @@
+package jobs
+
+import (
+	"fmt"
+	"github.com/robfig/cron/v3"
+	"gorm.io/gorm"
+	"sync"
+	"time"
+
+	models2 "cold-delivery/app/jobs/model"
+	log "gogs.baozhida.cn/zoie/OAuth-core/logger"
+	"gogs.baozhida.cn/zoie/OAuth-core/pkg"
+	"gogs.baozhida.cn/zoie/OAuth-core/pkg/cronjob"
+	"gogs.baozhida.cn/zoie/OAuth-core/sdk"
+)
+
+var timeFormat = "2006-01-02 15:04:05"
+var retryCount = 3
+
+var jobList map[string]JobsExec
+var lock sync.Mutex
+
+type JobCore struct {
+	InvokeTarget   string
+	Name           string
+	JobId          int
+	EntryId        int
+	CronExpression string
+	Args           string
+}
+
+// 任务类型 http
+type HttpJob struct {
+	JobCore
+}
+
+type ExecJob struct {
+	JobCore
+}
+
+func (e *ExecJob) Run() {
+	startTime := time.Now()
+	var obj = jobList[e.InvokeTarget]
+	if obj == nil {
+		log.Warn("[Job] ExecJob Run job nil")
+		return
+	}
+	err := CallExec(obj.(JobsExec), e.Args)
+	if err != nil {
+		// 如果失败暂停一段时间重试
+		fmt.Println(time.Now().Format(timeFormat), " [ERROR] mission failed! ", err)
+	}
+	// 结束时间
+	endTime := time.Now()
+
+	// 执行时间
+	latencyTime := endTime.Sub(startTime)
+	//TODO: 待完善部分
+	//str := time.Now().Format(timeFormat) + " [INFO] JobCore " + string(e.EntryId) + "exec success , spend :" + latencyTime.String()
+	//ws.SendAll(str)
+	log.Infof("[Job] JobCore %s exec success , spend :%v", e.Name, latencyTime)
+	return
+}
+
+// http 任务接口
+func (h *HttpJob) Run() {
+
+	startTime := time.Now()
+	var count = 0
+	var err error
+	var str string
+	/* 循环 */
+LOOP:
+	if count < retryCount {
+		/* 跳过迭代 */
+		str, err = pkg.Get(h.InvokeTarget)
+		if err != nil {
+			// 如果失败暂停一段时间重试
+			fmt.Println(time.Now().Format(timeFormat), " [ERROR] mission failed! ", err)
+			fmt.Printf(time.Now().Format(timeFormat)+" [INFO] Retry after the task fails %d seconds! %s \n", (count+1)*5, str)
+			time.Sleep(time.Duration(count+1) * 5 * time.Second)
+			count = count + 1
+			goto LOOP
+		}
+	}
+	// 结束时间
+	endTime := time.Now()
+
+	// 执行时间
+	latencyTime := endTime.Sub(startTime)
+	//TODO: 待完善部分
+
+	log.Infof("[Job] JobCore %s exec success , spend :%v", h.Name, latencyTime)
+	return
+}
+
+// 初始化
+func Setup(dbs map[string]*gorm.DB) {
+
+	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Starting...")
+
+	for k, db := range dbs {
+		sdk.Runtime.SetCrontab(k, cronjob.NewWithSeconds())
+		setup(k, db)
+	}
+}
+
+func setup(key string, db *gorm.DB) {
+	crontab := sdk.Runtime.GetCrontabKey(key)
+	sysJob := models2.SysJob{}
+	jobs := make([]models2.SysJob, 0)
+	err := sysJob.GetList(db, &jobs)
+	if err != nil {
+		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore init error", err)
+	}
+	if len(jobs) == 0 {
+		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore total:0")
+	}
+
+	_, err = sysJob.RemoveAllEntryID(db)
+	if err != nil {
+		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore remove entry_id error", err)
+	}
+
+	for i := 0; i < len(jobs); i++ {
+		if !jobs[i].Auto {
+			continue
+		}
+		if jobs[i].JobType == 1 {
+			j := &HttpJob{}
+			j.InvokeTarget = jobs[i].InvokeTarget
+			j.CronExpression = jobs[i].CronExpression
+			j.JobId = jobs[i].JobId
+			j.Name = jobs[i].JobName
+
+			sysJob.EntryId, err = AddJob(crontab, j)
+		} else if jobs[i].JobType == 2 {
+			j := &ExecJob{}
+			j.InvokeTarget = jobs[i].InvokeTarget
+			j.CronExpression = jobs[i].CronExpression
+			j.JobId = jobs[i].JobId
+			j.Name = jobs[i].JobName
+			j.Args = jobs[i].Args
+			sysJob.EntryId, err = AddJob(crontab, j)
+		}
+		err = sysJob.Update(db, jobs[i].JobId)
+	}
+
+	// 启动任务
+	crontab.Start()
+	fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore start success.")
+	// 关闭任务
+	defer crontab.Stop()
+	select {}
+}
+
+// 添加任务 AddJob(invokeTarget string, jobId int, jobName string, cronExpression string)
+func AddJob(c *cron.Cron, job Job) (int, error) {
+	if job == nil {
+		fmt.Println("unknown")
+		return 0, nil
+	}
+	return job.addJob(c)
+}
+
+func (h *HttpJob) addJob(c *cron.Cron) (int, error) {
+	id, err := c.AddJob(h.CronExpression, h)
+	if err != nil {
+		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore AddJob error", err)
+		return 0, err
+	}
+	EntryId := int(id)
+	return EntryId, nil
+}
+
+func (h *ExecJob) addJob(c *cron.Cron) (int, error) {
+	id, err := c.AddJob(h.CronExpression, h)
+	if err != nil {
+		fmt.Println(time.Now().Format(timeFormat), " [ERROR] JobCore AddJob error", err)
+		return 0, err
+	}
+	EntryId := int(id)
+	return EntryId, nil
+}
+
+// 移除任务
+func Remove(c *cron.Cron, entryID int) chan bool {
+	ch := make(chan bool)
+	go func() {
+		c.Remove(cron.EntryID(entryID))
+		fmt.Println(time.Now().Format(timeFormat), " [INFO] JobCore Remove success ,info entryID :", entryID)
+		ch <- true
+	}()
+	return ch
+}
+
+// 任务停止
+//func Stop() chan bool {
+//	ch := make(chan bool)
+//	go func() {
+//		global.GADMCron.Stop()
+//		ch <- true
+//	}()
+//	return ch
+//}

+ 85 - 0
app/jobs/jobs.go

@@ -0,0 +1,85 @@
+package jobs
+
+import (
+	sysModel "cold-delivery/app/admin/model"
+	"cold-delivery/common/global"
+	"cold-delivery/db"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	log "gogs.baozhida.cn/zoie/OAuth-core/logger"
+	"time"
+)
+
+// 需要将定义的struct 添加到字典中;
+// 字典 key 可以配置到 自动任务 调用目标 中;
+func InitJob() {
+	jobList = map[string]JobsExec{
+		//"ExamplesOne":     ExamplesOne{},
+		"CountIceRaftRecordFreezeDuration": CountIceRaftRecordFreezeDuration{},
+		// ...
+	}
+}
+
+// 新添加的job 必须按照以下格式定义,并实现Exec函数
+type ExamplesOne struct {
+}
+
+func (t ExamplesOne) Exec(arg interface{}) error {
+	str := time.Now().Format(timeFormat) + " [INFO] JobCore ExamplesOne exec success"
+	// TODO: 这里需要注意 Examples 传入参数是 string 所以 arg.(string);请根据对应的类型进行转化;
+	switch arg.(type) {
+
+	case string:
+		if arg.(string) != "" {
+			fmt.Println("string", arg.(string))
+			fmt.Println(str, arg.(string))
+		} else {
+			fmt.Println("arg is nil")
+			fmt.Println(str, "arg is nil")
+		}
+		break
+	}
+
+	return nil
+}
+
+// 统计冰排冷冻时长
+type CountIceRaftRecordFreezeDuration struct {
+}
+
+// 统计冰排冷冻时长
+func (t CountIceRaftRecordFreezeDuration) Exec(arg interface{}) error {
+
+	iceRaftRecordList := make([]sysModel.IceRaftRecord, 0)
+	// GetOrm 获取orm连接
+	orm, _ := db.GetOrm(&gin.Context{})
+
+	err := orm.Where("status = ? or status = ? ", sysModel.IceRaftRecordStatusFreezing, sysModel.IceRaftRecordStatusWaitUse).
+		Find(&iceRaftRecordList).Error
+	if err != nil {
+		log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+
+	for _, record := range iceRaftRecordList {
+
+		inTime := record.InStorageTime.Local()
+		//if inTime.Add(time.Hour * time.Duration(record.FreezeClaim)).After(time.Now()) {
+		if inTime.Add(time.Duration(float64(time.Hour) * float64(record.FreezeClaim))).After(time.Now()) {
+			// 未达到冷冻时长 不修改状态 只修改时间
+			record.FreezeDuration = int(time.Now().Sub(inTime).Minutes())
+		} else {
+			record.FreezeDuration = int(time.Now().Sub(inTime).Minutes())
+			record.Status = sysModel.IceRaftRecordStatusWaitUse
+		}
+
+		if err := orm.Save(&record).Error; err != nil {
+			log.Errorf("db error: %s", err)
+			return global.UpdateFailedErr
+		}
+
+	}
+
+	return nil
+
+}

+ 62 - 0
app/jobs/model/sys_job.go

@@ -0,0 +1,62 @@
+package model
+
+import (
+	model2 "cold-delivery/common/model"
+	"gorm.io/gorm"
+)
+
+type SysJob struct {
+	JobId          int    `json:"jobId" gorm:"primaryKey;autoIncrement"` // 编码
+	JobName        string `json:"jobName" gorm:"size:255;"`              // 名称
+	JobGroup       string `json:"jobGroup" gorm:"size:255;"`             // 任务分组
+	JobType        int    `json:"jobType" gorm:"size:1;"`                // 任务类型 1-接口 2-函数
+	CronExpression string `json:"cronExpression" gorm:"size:255;"`       // cron表达式
+	InvokeTarget   string `json:"invokeTarget" gorm:"size:255;"`         // 调用目标
+	Args           string `json:"args" gorm:"size:255;"`                 // 目标参数
+	MisfirePolicy  int    `json:"misfirePolicy" gorm:"size:255;"`        // 执行策略 1-立即执行 2-执行一次 3-放弃执行
+	Concurrent     int    `json:"concurrent" gorm:"size:1;"`             // 是否并发 1-禁止
+	Status         int    `json:"status" gorm:"size:1;"`                 // 状态 1-停用 2-正常
+	EntryId        int    `json:"entry_id" gorm:"size:11;"`              // job启动时返回的id
+	Auto           bool   `json:"auto" gorm:"size:4;"`                   // 是否开机自启动
+	model2.ControlBy
+	model2.ModelTime
+
+	DataScope string `json:"dataScope" gorm:"-"`
+}
+
+func (SysJob) TableName() string {
+	return "sys_job"
+}
+
+func (e *SysJob) Generate() model2.ActiveRecord {
+	o := *e
+	return &o
+}
+
+func (e *SysJob) GetId() interface{} {
+	return e.JobId
+}
+
+func (e *SysJob) SetCreateBy(createBy int) {
+	e.CreateBy = createBy
+}
+
+func (e *SysJob) SetUpdateBy(updateBy int) {
+	e.UpdateBy = updateBy
+}
+
+func (e *SysJob) GetList(tx *gorm.DB, list interface{}) (err error) {
+	return tx.Table(e.TableName()).Where("status = ?", 2).Find(list).Error
+}
+
+// 更新SysJob
+func (e *SysJob) Update(tx *gorm.DB, id interface{}) (err error) {
+	return tx.Table(e.TableName()).Where(id).Updates(&e).Error
+}
+
+func (e *SysJob) RemoveAllEntryID(tx *gorm.DB) (update SysJob, err error) {
+	if err = tx.Table(e.TableName()).Where("entry_id > ?", 0).Update("entry_id", 0).Error; err != nil {
+		return
+	}
+	return
+}

+ 16 - 0
app/jobs/type.go

@@ -0,0 +1,16 @@
+package jobs
+
+import "github.com/robfig/cron/v3"
+
+type Job interface {
+	Run()
+	addJob(*cron.Cron) (int, error)
+}
+
+type JobsExec interface {
+	Exec(arg interface{}) error
+}
+
+func CallExec(e JobsExec, arg interface{}) error {
+	return e.Exec(arg)
+}

+ 5 - 4
cmd/api/server.go

@@ -2,6 +2,7 @@ package api
 
 import (
 	"cold-delivery/app/admin/model"
+	"cold-delivery/app/jobs"
 	"cold-delivery/common/file_store"
 	global2 "cold-delivery/common/global"
 	"cold-delivery/common/middleware"
@@ -101,10 +102,10 @@ func run() error {
 	//}()
 
 	// fixme 定时任务,需要时再配置
-	//go func() {
-	//	jobs.InitJob()
-	//	jobs.Setup(sdk.Runtime.GetDb())
-	//}()
+	go func() {
+		jobs.InitJob()
+		jobs.Setup(sdk.Runtime.GetDb())
+	}()
 
 	go func() {
 		// 服务连接

+ 1 - 1
common/actions/permission.go

@@ -73,7 +73,7 @@ func UserPermission(tableName string, p *DataPermission) func(db *gorm.DB) *gorm
 		}
 		switch p.DataScope {
 		//case "2":
-		//	return db.Where(tableName+".create_by in (select sys_user.id from sys_role_dept left join sys_user on sys_user.dept_id=sys_role_dept.dept_id where sys_role_dept.role_id = ?) id = ?", p.RoleId, p.UserId)
+		//	return db.Where(tableName+".create_by in (select sys_user.id from sys_role_dept left join sys_user on sys_user.dept_id=sys_role_dept.dept_id where sys_role_dept.role_id = ?) id = ?", p.RoleId, p.UserIds)
 		case "3":
 			return db.Where(tableName+".dept_id = ? ", p.DeptId)
 		case "4":

+ 58 - 0
common/lib/lib.go

@@ -426,3 +426,61 @@ func GetDiffDays(t1, t2 time.Time) int {
 
 	return int(t1.Sub(t2).Hours() / 24)
 }
+
+func FindStrListMissingData(data1, data2 []string) []string {
+	set1 := make(map[string]struct{})
+	set2 := make(map[string]struct{})
+
+	for _, item := range data1 {
+		set1[item] = struct{}{}
+	}
+
+	for _, item := range data2 {
+		set2[item] = struct{}{}
+	}
+
+	var missing []string
+	for item := range set1 {
+		if _, found := set2[item]; !found {
+			missing = append(missing, item)
+		}
+	}
+
+	return missing
+}
+
+func FindStrListExtraData(data1, data2 []string) []string {
+	set1 := make(map[string]struct{})
+	set2 := make(map[string]struct{})
+
+	for _, item := range data1 {
+		set1[item] = struct{}{}
+	}
+
+	for _, item := range data2 {
+		set2[item] = struct{}{}
+	}
+
+	var extra []string
+	for item := range set2 {
+		if _, found := set1[item]; !found {
+			extra = append(extra, item)
+		}
+	}
+
+	return extra
+}
+
+func StrListEqual(slice1, slice2 []string) bool {
+	if len(slice1) != len(slice2) {
+		return false
+	}
+
+	for i := range slice1 {
+		if slice1[i] != slice2[i] {
+			return false
+		}
+	}
+
+	return true
+}

+ 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)
 }

+ 1 - 1
common/nats/nats_server/NatsColdApi.go

@@ -179,7 +179,7 @@ func Cold_ReadDevice_List(name, T_key string, page, page_z int) (data []Device_R
 
 	type T_Req struct {
 		Key    string `xml:"Key"`
-		Name   string `xml:"Name"`
+		Name   string `xml:"Code"`
 		Page   int    `xml:"Page"`
 		Page_z int    `xml:"Page_z"`
 	}

+ 5 - 0
db/migration.go

@@ -4,6 +4,7 @@ import (
 	"go.uber.org/zap"
 
 	"cold-delivery/app/admin/model"
+	jobModel "cold-delivery/app/jobs/model"
 )
 
 // 执行数据迁移
@@ -11,6 +12,7 @@ func AutoMigrateDB() {
 	//自动迁移模式
 	err := DB.Set("gorm:table_options", "charset=utf8mb4").
 		AutoMigrate(
+			&jobModel.SysJob{},
 			&model.SysUser{},
 			//&model.SysRole{},
 			//&model.SysRoleMenu{},
@@ -26,6 +28,9 @@ func AutoMigrateDB() {
 			&model.Waybill{},
 			&model.WaybillLogistics{},
 			&model.WaybillTask{},
+			&model.IceRaft{},
+			&model.IceLocker{},
+			&model.IceRaftRecord{},
 		)
 	if err != nil {
 		zap.L().Panic("migrate db fail", zap.Error(err))

+ 7 - 5
go.mod

@@ -14,7 +14,7 @@ require (
 	github.com/mssola/user_agent v0.5.3
 	github.com/pkg/errors v0.9.1
 	github.com/qiniu/go-sdk/v7 v7.13.0
-	github.com/robfig/cron/v3 v3.0.1 // indirect
+	github.com/robfig/cron/v3 v3.0.1
 	github.com/spf13/cobra v1.5.0
 	github.com/swaggo/files v1.0.1
 	github.com/swaggo/gin-swagger v1.6.0
@@ -30,10 +30,15 @@ require (
 	github.com/beego/beego/v2 v2.1.0
 	github.com/go-redis/redis/v7 v7.4.0
 	github.com/go-resty/resty/v2 v2.7.0
+	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
 	github.com/nats-io/nats.go v1.34.0
+	github.com/pborman/uuid v1.2.0
+	github.com/signintech/gopdf v0.25.0
 	github.com/vmihailenco/msgpack/v5 v5.4.1
+	github.com/wcharczuk/go-chart/v2 v2.1.1
 	github.com/xuri/excelize/v2 v2.8.1
 	gogs.baozhida.cn/zoie/OAuth-core v0.0.0-00010101000000-000000000000
+	gonum.org/v1/plot v0.14.0
 )
 
 require (
@@ -44,6 +49,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.20220411.3 // 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
@@ -69,7 +75,6 @@ require (
 	github.com/go-playground/universal-translator v0.18.1 // indirect
 	github.com/go-playground/validator/v10 v10.11.2 // indirect
 	github.com/goccy/go-json v0.10.0 // indirect
-	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
 	github.com/henrylee2cn/ameda v1.4.10 // indirect
@@ -107,7 +112,6 @@ require (
 	github.com/shamsher31/goimgext v1.0.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 	github.com/shirou/gopsutil v2.19.12+incompatible // indirect
-	github.com/signintech/gopdf v0.25.0 // indirect
 	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/tsuyoshiwada/go-gitcmd v0.0.0-20180205145712-5f1f5f9475df // indirect
@@ -115,7 +119,6 @@ require (
 	github.com/ugorji/go/codec v1.2.9 // indirect
 	github.com/urfave/cli v1.22.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
-	github.com/wcharczuk/go-chart/v2 v2.1.1 // indirect
 	github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
 	github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
 	go.uber.org/atomic v1.9.0 // indirect
@@ -128,7 +131,6 @@ require (
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect
 	golang.org/x/tools v0.7.0 // indirect
-	gonum.org/v1/plot v0.14.0 // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/AlecAivazis/survey.v1 v1.8.5 // indirect
 	gopkg.in/kyokomi/emoji.v1 v1.5.1 // indirect

+ 8 - 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.20220411.3 h1:GFV4/FQX5UzXLPwWV03gP811pj7B8J2sbuq+GJQofXc=
+github.com/blend/go-sdk v1.20220411.3/go.mod h1:7lnH8fTi6U4i1fArEXRyOIY2E1X4MALg09qsQqY1+ak=
 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/bsm/redislock v0.5.0 h1:ODM11/cbuUXQqLgZWK6XQnufaTjsBE2UcwBc2EAFNDA=
@@ -177,6 +180,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=
@@ -728,12 +733,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=
@@ -910,6 +914,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=
@@ -1000,5 +1005,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=