Explorar o código

FUNC:运单温湿度记录

zoie hai 1 ano
pai
achega
9a53bffac0

+ 74 - 0
app/admin/controller/device.go

@@ -0,0 +1,74 @@
+package controller
+
+import (
+	"cold-logistics/app/admin/service"
+	"cold-logistics/app/admin/service/dto"
+	"cold-logistics/common/actions"
+	"github.com/gin-gonic/gin"
+	"github.com/gin-gonic/gin/binding"
+	"gogs.baozhida.cn/zoie/OAuth-core/api"
+)
+
+type DeviceController struct {
+	api.Api
+}
+
+// GetSensorList 获取传感器列表
+// @Summary 获取传感器列表
+// @Description 获取传感器列表
+// @Tags 设备
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.Device}} "{"code": 200, "data": [...]}"
+// @Router /api/device/sensor-list [get]
+// @Security Bearer
+func (e DeviceController) GetSensorList(c *gin.Context) {
+	s := service.Device{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//数据权限检查
+	p := actions.GetPermissionFromContext(c)
+
+	// 获取传感器信息
+	deviceSensorList, count, _ := s.GetSensorList(p)
+	e.PageOK(deviceSensorList, int(count), 0, 0, "查询成功")
+}
+
+// GetData 获取车辆温湿度信息
+// @Summary 获取车辆温湿度信息
+// @Description 获取车辆温湿度信息
+// @Tags 设备
+// @Param t_sn query string false "sn"
+// @Param t_id query string false "传感器id"
+// @Param pageSize query int false "页条数"
+// @Param page query int false "页码"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.Device}} "{"code": 200, "data": [...]}"
+// @Router /api/device/sensor-list [get]
+// @Security Bearer
+func (e DeviceController) GetData(c *gin.Context) {
+	s := service.Device{}
+	req := dto.DeviceGetDataReq{}
+	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)
+
+	// 获取传感器信息
+	dataList, count, _ := s.GetData(req, p)
+	e.PageOK(dataList, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}

+ 302 - 1
app/admin/controller/waybill.go

@@ -1,15 +1,22 @@
 package controller
 
 import (
+	"bufio"
 	"cold-logistics/app/admin/model"
 	"cold-logistics/app/admin/service"
 	"cold-logistics/app/admin/service/dto"
 	"cold-logistics/common/actions"
+	"errors"
+	"fmt"
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin/binding"
+	"github.com/xuri/excelize/v2"
 	"gogs.baozhida.cn/zoie/OAuth-core/api"
 	"gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth/user"
 	_ "gogs.baozhida.cn/zoie/OAuth-core/pkg/response"
+	"net/url"
+	"os"
+	"path"
 )
 
 type WaybillController struct {
@@ -54,13 +61,50 @@ func (e WaybillController) GetPage(c *gin.Context) {
 	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
 }
 
+// GetPage 获取运单列表
+// @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) GetAppletPage(c *gin.Context) {
+	s := service.Waybill{}
+	req := dto.WaybillGetAppletPageReq{}
+	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.Waybill, 0)
+	var count int64
+	err = s.GetAppletPage(&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.Waybill} "{"code": 200, "data": [...]}"
-// @Router /api/warehouse/{id} [get]
+// @Router /api/waybill/{id} [get]
 // @Security Bearer
 func (e WaybillController) Get(c *gin.Context) {
 	s := service.Waybill{}
@@ -370,3 +414,260 @@ func (e WaybillController) CarOut(c *gin.Context) {
 	}
 	e.OK(req.WaybillNoList, "下车成功")
 }
+
+// Receipt 签收
+// @Summary 签收
+// @Description 签收
+// @Tags 运单
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.WaybillInOutReq true "body"
+// @Success 200 {string} string	"{"code": 200, "message": "删除成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "删除失败"}"
+// @Router /api/waybill/car-out [post]
+// @Security Bearer
+func (e WaybillController) Receipt(c *gin.Context) {
+	s := service.Waybill{}
+	req := dto.WaybillInOutReq{}
+	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.Receipt(&req, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.WaybillNoList, "下车成功")
+}
+
+// GetCustomerPage 获取客户运单列表
+// @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/customer [get]
+// @Security Bearer
+func (e WaybillController) GetCustomerPage(c *gin.Context) {
+	s := service.Waybill{}
+	req := dto.WaybillGetCustomerPageReq{}
+	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.Waybill, 0)
+	var count int64
+	req.CustomerId = p.UserId
+	err = s.GetCustomerPage(&req, &list, &count, p)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+// CustomerInsert 客户添加运单
+// @Summary 客户添加运单
+// @Description 客户添加运单
+// @Tags 运单
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.WaybillInsertReq true "data"
+// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
+// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
+// @Router /api/waybill/customer [post]
+// @Security Bearer
+func (e WaybillController) CustomerInsert(c *gin.Context) {
+	s := service.Waybill{}
+	userSvc := service.SysUser{}
+	req := dto.WaybillInsertReq{}
+	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, "获取用户信息失败")
+		return
+	}
+	if p.DeptId == 0 && req.DeptId == 0 {
+		e.Error(500, err, "请先选择运输公司")
+		return
+	}
+	// 设置创建人
+	req.SetCreateBy(user.GetUserId(c))
+	req.CustomerId = p.UserId
+	req.CustomerName = userObj.NickName
+	if p.DeptId > 0 {
+		req.SetDeptId(p.DeptId)
+	}
+	err = s.Insert(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(req.GetId(), "创建成功")
+}
+func (e WaybillController) Import(c *gin.Context) {
+	s := service.Waybill{}
+	userSvc := service.SysUser{}
+	req := dto.WaybillImportReq{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		Bind(&req, binding.Form).
+		MakeService(&s.Service).
+		MakeService(&userSvc.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	//读取第一fileName的文件
+	fileHeader, err := c.FormFile("file")
+	if err != nil {
+		err = errors.New("文件格式错误" + err.Error())
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	if fileHeader.Size > 1024*1024*2 {
+		err = errors.New("文件大小超过2M")
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	file, err := fileHeader.Open()
+	if err != nil {
+		err = errors.New("文件格式错误" + err.Error())
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	defer file.Close()
+
+	xlsx, err := excelize.OpenReader(bufio.NewReader(file))
+	if err != nil {
+		err = errors.New("文件格式错误" + err.Error())
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	p := actions.GetPermissionFromContext(c)
+	if p.DeptId == 0 {
+		e.Error(500, err, "获取用户信息失败")
+		return
+	}
+
+	var userObj model.SysUser
+	err = userSvc.Get(&dto.SysUserGetReq{Id: p.UserId}, nil, &userObj)
+	if err != nil {
+		e.Error(500, err, "获取用户信息失败")
+		return
+	}
+
+	var customerId int
+	customerName := req.CustomerName
+	if userObj.UserType == model.UserTypeCustomer {
+		customerId = userObj.Id
+		customerName = userObj.NickName
+	}
+
+	rows, _ := xlsx.GetRows("Sheet1")
+	for indexRow, row := range rows {
+		if indexRow == 0 {
+			continue
+		}
+		for i, colCell := range row {
+			fmt.Println(i, ":", colCell)
+		}
+		obj := dto.WaybillInsertReq{
+			Status:                  1,
+			SenderAddressName:       row[0],
+			SenderAddressPhone:      row[1],
+			SenderAddressDetails:    row[2],
+			ConsigneeAddressName:    row[3],
+			ConsigneeAddressPhone:   row[4],
+			ConsigneeAddressDetails: row[5],
+			TemperatureInterval:     row[6],
+			DeliveryCondition:       row[7],
+			CargoType:               row[8],
+			Remark:                  row[9],
+			CustomerId:              customerId,
+			CustomerName:            customerName,
+		}
+		obj.SetDeptId(p.DeptId)
+		obj.SetCreateBy(user.GetUserId(c))
+		err = s.Insert(&obj)
+		if err != nil {
+			e.Error(500, err, err.Error())
+			return
+		}
+	}
+
+	e.OK(len(rows)-1, "导入成功")
+}
+func (e WaybillController) ExportTemplate(c *gin.Context) {
+	s := service.Waybill{}
+	err := e.MakeContext(c).
+		MakeOrm().
+		MakeService(&s.Service).
+		Errors
+	if err != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+
+	filePath := "./ofile/运单导入模板.xlsx"
+	//打开文件
+	fileTmp, errByOpenFile := os.Open(filePath)
+	defer fileTmp.Close()
+
+	//获取文件的名称
+	fileName := path.Base(filePath)
+	if errByOpenFile != nil {
+		e.Logger.Error(err)
+		e.Error(500, err, err.Error())
+		return
+	}
+	c.Header("Content-Type", "application/vnd.ms-excel;charset=utf8")
+	// PathEscape 函数对中文做处理
+	c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fileName))
+	c.Header("Content-Transfer-Encoding", "binary")
+	c.File(filePath)
+
+}

+ 51 - 0
app/admin/controller/waybill_logistics.go

@@ -0,0 +1,51 @@
+package controller
+
+import (
+	"cold-logistics/app/admin/model"
+	"cold-logistics/app/admin/service"
+	"cold-logistics/app/admin/service/dto"
+	"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/response"
+)
+
+type WaybillLogisticsController struct {
+	api.Api
+}
+
+// GetPage 获取运单列表
+// @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.WaybillLogistics}} "{"code": 200, "data": [...]}"
+// @Router /api/waybill [get]
+// @Security Bearer
+func (e WaybillLogisticsController) GetPage(c *gin.Context) {
+	s := service.WaybillLogistics{}
+	req := dto.WaybillLogisticsGetPageReq{}
+	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
+	}
+
+	list := make([]model.WaybillLogistics, 0)
+	var count int64
+
+	err = s.GetPage(&req, &list, &count)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	//e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+	e.PageOK(list, int(count), 0, 0, "查询成功")
+}

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

@@ -0,0 +1,108 @@
+package controller
+
+import (
+	"cold-logistics/app/admin/model"
+	"cold-logistics/app/admin/service"
+	"cold-logistics/app/admin/service/dto"
+	"cold-logistics/common/nats/nats_server"
+	"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/response"
+)
+
+type WaybillTaskController struct {
+	api.Api
+}
+
+// GetPage 获取运单任务列表
+// @Summary 获取运单任务列表
+// @Description 获取运单任务列表
+// @Tags 运单任务
+// @Param no query string true "运单号"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.WaybillTask}} "{"code": 200, "data": [...]}"
+// @Router /api/waybill-task [get]
+// @Security Bearer
+func (e WaybillTaskController) GetPage(c *gin.Context) {
+	s := service.WaybillTask{}
+	req := dto.WaybillTaskGetPageReq{}
+	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
+	}
+
+	list := make([]model.WaybillTask, 0)
+	var count int64
+
+	err = s.GetPage(&req, &list, &count)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	//e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+	e.PageOK(list, int(count), 0, 0, "查询成功")
+}
+
+// GetData 获取温湿度记录
+// @Summary 获取传感器列表
+// @Description 获取传感器列表
+// @Tags 运单任务
+// @Accept  application/json
+// @Product application/json
+// @Param data body dto.WaybillTaskGetDataPageReq true "body"
+// @Success 200 {object} response.Response{data=response.Page{list=[]model.WaybillTask}} "{"code": 200, "data": [...]}"
+// @Router /api/waybill-task/data [post]
+// @Security Bearer
+func (e WaybillTaskController) GetData(c *gin.Context) {
+	s := service.WaybillTask{}
+	req := dto.WaybillTaskGetDataPageReq{}
+	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
+	}
+
+	list := make([]nats_server.DeviceData_R, 0)
+	var count int64
+
+	list, count, err = s.GetDataPage(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.PageOK(list, int(count), req.GetPageIndex(), req.GetPageSize(), "查询成功")
+}
+
+func (e WaybillTaskController) GetLocus(c *gin.Context) {
+	s := service.WaybillTask{}
+	req := dto.WaybillGetLocusReq{}
+	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
+	}
+	list := make([]nats_server.DeviceData_R2, 0)
+
+	list, err = s.GetLocus(&req)
+	if err != nil {
+		e.Error(500, err, err.Error())
+		return
+	}
+	e.OK(list, "获取轨迹信息成功")
+}

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

@@ -18,9 +18,11 @@ type Car struct {
 type CarOmit struct {
 	Id    int    `json:"id,omitempty"`    // 主键编码
 	CarNo string `json:"carNo,omitempty"` // 商品名称
-	Sn    string `json:"sn,omitempty"`    // sn
 }
 
 func (Car) TableName() string {
 	return "car"
 }
+func (CarOmit) TableName() string {
+	return "car"
+}

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

@@ -74,7 +74,7 @@ func SaveOperaLog(message storage.Messager) (err error) {
 		return nil
 	}
 	// 外部调用获取身份信息,操作频繁,不做日志记录
-	if l.OperaUrl == "/api/service/userinfo" || l.OperaUrl == "/api/upload" {
+	if l.OperaUrl == "/api/service/userinfo" || l.OperaUrl == "/api/upload" || l.OperaUrl == "/api/waybill/import" {
 		return nil
 	}
 	// 超出100个字符返回值截断

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

@@ -18,9 +18,11 @@ type Warehouse struct {
 type WarehouseOmit struct {
 	Id   int    `json:"id,omitempty"`   // 主键编码
 	Name string `json:"name,omitempty"` // 商品名称
-	Sn   string `json:"sn,omitempty"`   // sn
 }
 
 func (Warehouse) TableName() string {
 	return "warehouse"
 }
+func (WarehouseOmit) TableName() string {
+	return "warehouse"
+}

+ 9 - 10
app/admin/model/waybill.go

@@ -26,13 +26,13 @@ var (
 // 运单
 type Waybill struct {
 	model2.Model
-	WaybillNo               string      `json:"waybillNo"  gorm:"size:128"`               //单号
-	Status                  int         `json:"status"  gorm:"size:128"`                  //订单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
-	SenderAddressId         int         `json:"senderAddressId"  gorm:"size:128"`         //发货地址Id
-	SenderAddressDetails    string      `json:"senderAddressDetails"  gorm:"size:128"`    //发货地址详情
-	SenderAddressName       string      `json:"senderAddressName"  gorm:"size:128"`       //发货地址名称
-	SenderAddressPhone      string      `json:"senderAddressPhone"  gorm:"size:128"`      //发货地址电话
-	ConsigneeAddressId      int         `json:"consigneeAddressId"  gorm:"size:128"`      //收货地址id
+	WaybillNo string `json:"waybillNo"  gorm:"size:128"` //单号
+	Status    int    `json:"status"  gorm:"size:128"`    //订单状态:1待派单 2待装车 3待入库 4已装车 5已入库 6已下车 7已出库 8已签收
+	//SenderAddressId         int         `json:"senderAddressId"  gorm:"size:128"`         //发货地址Id
+	SenderAddressDetails string `json:"senderAddressDetails"  gorm:"size:128"` //发货地址详情
+	SenderAddressName    string `json:"senderAddressName"  gorm:"size:128"`    //发货地址名称
+	SenderAddressPhone   string `json:"senderAddressPhone"  gorm:"size:128"`   //发货地址电话
+	//ConsigneeAddressId      int         `json:"consigneeAddressId"  gorm:"size:128"`      //收货地址id
 	ConsigneeAddressDetails string      `json:"consigneeAddressDetails"  gorm:"size:128"` //收发货地址详情
 	ConsigneeAddressName    string      `json:"consigneeAddressName"  gorm:"size:128"`    //收发货地址名称
 	ConsigneeAddressPhone   string      `json:"consigneeAddressPhone"  gorm:"size:128"`   //收发货地址电话
@@ -48,9 +48,8 @@ type Waybill struct {
 	WarehouseId             int         `json:"warehouseId"  gorm:"size:128"`             // 仓库id
 	CarId                   int         `json:"carId"  gorm:"size:128"`                   // 仓库id
 
-	Freight      float64 `json:"freight"  gorm:"size:9"`      //运费
-	LatestLogId  int     `json:"latestLogId"  gorm:"size:9"`  //最近一次定位
-	LatestTaskId int     `json:"latestTaskId"  gorm:"size:9"` //最近一次任务Id
+	Freight   float64     `json:"freight"  gorm:"size:9"` //运费
+	PrintUser SysUserOmit `json:"printUser" gorm:"->;foreignkey:PrintUserId;references:Id"`
 	model2.ControlBy
 	model2.ModelTime
 	model2.DeptBy

+ 2 - 2
app/admin/model/waybill_logistics.go

@@ -12,8 +12,8 @@ type WaybillLogistics struct {
 	UserId      int           `json:"userId"  gorm:"size:128"`      // 司机/仓管id
 	Lng         string        `json:"lng" gorm:"size:128;"`         // 经度
 	Lat         string        `json:"lat" gorm:"size:128;"`         // 纬度
-	Warehouse   WarehouseOmit `json:"warehouse"`
-	Car         CarOmit       `json:"car"`
+	Warehouse   WarehouseOmit `json:"warehouse" gorm:"->;foreignkey:WarehouseId;references:Id"`
+	Car         CarOmit       `json:"car" gorm:"->;foreignkey:CarId;references:Id"`
 	model2.ControlBy
 	model2.ModelTime
 	model2.DeptBy

+ 15 - 10
app/admin/model/waybill_task.go

@@ -1,19 +1,24 @@
 package model
 
-import model2 "cold-logistics/common/model"
+import (
+	model2 "cold-logistics/common/model"
+	"cold-logistics/common/nats/nats_server"
+)
 
 // 运单任务
 type WaybillTask struct {
 	model2.Model
-	WaybillNo   string        `json:"waybillNo"  gorm:"size:128"`   // 单号
-	WarehouseId int           `json:"warehouseId"  gorm:"size:128"` // 仓库id
-	CarId       int           `json:"carId"  gorm:"size:128"`       // 仓库id
-	UserId      int           `json:"userId"  gorm:"size:128"`      // 司机/仓管id
-	Sn          string        `json:"sn" gorm:"size:128"`           // sn
-	StartTime   model2.Time   `json:"startTime"  gorm:"size:128"`   // 签收时间
-	EndTime     model2.Time   `json:"endTime"  gorm:"size:128"`     // 签收时间
-	Warehouse   WarehouseOmit `json:"warehouse"`
-	Car         CarOmit       `json:"car"`
+	WaybillNo        string                       `json:"waybillNo"  gorm:"size:128"`   // 单号
+	WarehouseId      int                          `json:"warehouseId"  gorm:"size:128"` // 仓库id
+	CarId            int                          `json:"carId"  gorm:"size:128"`       // 仓库id
+	UserId           int                          `json:"userId"  gorm:"size:128"`      // 司机/仓管id
+	Sn               string                       `json:"sn" gorm:"size:128"`           // sn
+	StartTime        model2.Time                  `json:"startTime"  gorm:"size:128"`   // 签收时间
+	EndTime          model2.Time                  `json:"endTime"  gorm:"size:128"`     // 签收时间
+	Warehouse        WarehouseOmit                `json:"warehouse"`
+	Car              CarOmit                      `json:"car"`
+	DeviceSensorList []nats_server.DeviceSensor_R `json:"deviceSensorList,omitempty"  gorm:"-"` // 传感器列表
+	DataList         []nats_server.DeviceData_R   `json:"dataList,omitempty"  gorm:"-"`         // 传感器列表
 	model2.ControlBy
 	model2.ModelTime
 	model2.DeptBy

+ 23 - 0
app/admin/router/device.go

@@ -0,0 +1,23 @@
+package router
+
+import (
+	"cold-logistics/app/admin/controller"
+	"cold-logistics/common/actions"
+	"github.com/gin-gonic/gin"
+	jwt "gogs.baozhida.cn/zoie/OAuth-core/pkg/jwtauth"
+)
+
+func init() {
+	routerCheckRole = append(routerCheckRole, registerDeviceRouter)
+
+}
+
+// 需认证的路由代码
+func registerDeviceRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) {
+	cont := controller.DeviceController{}
+	r := v1.Group("/device").Use(authMiddleware.MiddlewareFunc()).Use(actions.PermissionAction())
+	{
+		r.GET("/sensor-list", cont.GetSensorList)
+		r.GET("/data", cont.GetData)
+	}
+}

+ 10 - 4
app/admin/router/waybill.go

@@ -23,9 +23,15 @@ func registerWaybillRouter(v1 *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddle
 		r.POST("/delivery", cont.Delivery)
 		r.PUT("", cont.Update)
 		r.DELETE("", cont.Delete)
-		r.POST("/warehouse-in", cont.WarehouseIn)   // 入库
-		r.POST("/warehouse-out", cont.WarehouseOut) // 出库
-		r.POST("/car-in", cont.CarIn)               // 入库
-		r.POST("/car-out", cont.CarIn)              // 出库
+		r.POST("/warehouse-in", cont.WarehouseIn)      // 入库
+		r.POST("/warehouse-out", cont.WarehouseOut)    // 出库
+		r.POST("/car-in", cont.CarIn)                  // 装车
+		r.POST("/car-out", cont.CarOut)                // 下车
+		r.POST("/receipt", cont.Receipt)               // 签收
+		r.GET("/customer", cont.GetCustomerPage)       // 客户下单列表
+		r.POST("/customer", cont.CustomerInsert)       // 客户下单
+		r.GET("/applet", cont.GetAppletPage)           // app 运单列表
+		r.POST("/import", cont.Import)                 // 导入运单
+		r.GET("/export-template", cont.ExportTemplate) // 导出运单模板
 	}
 }

+ 20 - 0
app/admin/router/waybill_logistics.go

@@ -0,0 +1,20 @@
+package router
+
+import (
+	"cold-logistics/app/admin/controller"
+	"github.com/gin-gonic/gin"
+)
+
+func init() {
+	routerNoCheckRole = append(routerNoCheckRole, registerWaybillLogisticsRouter)
+}
+
+// 物流详情
+// 需认证的路由代码
+func registerWaybillLogisticsRouter(v1 *gin.RouterGroup) {
+	cont := controller.WaybillLogisticsController{}
+	r := v1.Group("/waybill-logistics")
+	{
+		r.GET("", cont.GetPage)
+	}
+}

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

@@ -0,0 +1,22 @@
+package router
+
+import (
+	"cold-logistics/app/admin/controller"
+	"github.com/gin-gonic/gin"
+)
+
+func init() {
+	routerNoCheckRole = append(routerNoCheckRole, registerWaybillTaskRouter)
+}
+
+// 物流详情
+// 需认证的路由代码
+func registerWaybillTaskRouter(v1 *gin.RouterGroup) {
+	cont := controller.WaybillTaskController{}
+	r := v1.Group("/waybill-task")
+	{
+		r.GET("", cont.GetPage)
+		r.POST("/data", cont.GetData)
+		r.GET("/locus", cont.GetLocus)
+	}
+}

+ 59 - 0
app/admin/service/device.go

@@ -0,0 +1,59 @@
+package service
+
+import (
+	"cold-logistics/app/admin/model"
+	"cold-logistics/app/admin/service/dto"
+	"cold-logistics/common/actions"
+	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
+	"fmt"
+	"gogs.baozhida.cn/zoie/OAuth-core/service"
+)
+
+type Device struct {
+	service.Service
+}
+
+// GetPage 获取Device列表
+func (e *Device) GetSensorList(p *actions.DataPermission) (list []nats_server.DeviceSensor_R, count int64, err error) {
+	var userObj model.SysUser
+
+	err = e.Orm.Model(&userObj).First(&userObj, p.UserId).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return list, count, global.GetFailedErr
+	}
+	var sn string
+	// 仓管2 司机3
+	if userObj.Type == 2 {
+		err = e.Orm.Model(&model.Warehouse{}).Select("sn").Where("user_id = ?", userObj.Id).Scan(&sn).Error
+	}
+	if userObj.Type == 3 {
+		err = e.Orm.Model(&model.Car{}).Select("sn").Where("user_id = ?", userObj.Id).Scan(&sn).Error
+	}
+
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return list, count, global.GetFailedErr
+	}
+
+	if len(sn) == 0 {
+		return list, count, nil
+	}
+
+	list, count, err = nats_server.Cold_CompanyDeviceSensor_List_ByKey(sn)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return list, count, err
+	}
+	return list, count, nil
+}
+func (e *Device) GetData(c dto.DeviceGetDataReq, p *actions.DataPermission) (list []nats_server.DeviceData_R, count int64, err error) {
+	T_snid := fmt.Sprintf("%s,%d|", c.T_sn, c.T_id)
+	list, count, err = nats_server.Cold_ReadDeviceDataListBy_T_snid(T_snid, c.StartTime, c.EndTime, c.Page, c.PageSize)
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return list, count, err
+	}
+	return list, count, nil
+}

+ 11 - 0
app/admin/service/dto/device.go

@@ -0,0 +1,11 @@
+package dto
+
+import "cold-logistics/common/dto"
+
+type DeviceGetDataReq struct {
+	dto.Pagination `search:"-"`
+	T_sn           string `form:"t_sn"`
+	T_id           int    `form:"t_id"`
+	StartTime      string `form:"startTime"`
+	EndTime        string `form:"endTime"`
+}

+ 34 - 4
app/admin/service/dto/waybill.go

@@ -12,9 +12,8 @@ import (
 
 type WaybillGetPageReq struct {
 	dto.Pagination `search:"-"`
-	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill"`                      // 运单编号
-	Status         int    `form:"status" search:"type:exact;column:status;table:waybill"`                                // 状态
-	CustomerId     string `form:"customerId" search:"type:exact;column:customer_id;table:waybill"  swaggerignore:"true"` // 客户id
+	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill"` // 运单编号
+	Status         int    `form:"status" search:"type:exact;column:status;table:waybill"`           // 状态
 	WaybillOrder
 }
 
@@ -26,6 +25,18 @@ func (m *WaybillGetPageReq) GetNeedSearch() interface{} {
 	return *m
 }
 
+type WaybillGetCustomerPageReq struct {
+	dto.Pagination `search:"-"`
+	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill"`                      // 运单编号
+	Status         int    `form:"status" search:"-"`                                                                     // 状态
+	CustomerId     int    `form:"customerId" search:"type:exact;column:customer_id;table:waybill"  swaggerignore:"true"` // 客户id
+	WaybillOrder
+}
+
+func (m *WaybillGetCustomerPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
 type WaybillInsertReq struct {
 	Id                      int    `json:"id" comment:"编码" swaggerignore:"true"` // 编码
 	No                      string `json:"no" swaggerignore:"true"`              //单号
@@ -43,7 +54,7 @@ type WaybillInsertReq struct {
 	CustomerId              int    `json:"customerId"`                           //下单客户id
 	CustomerName            string `json:"customerName"`                         //下单客户名称
 	model2.ControlBy        `swaggerignore:"true"`
-	model2.DeptBy           `swaggerignore:"true"`
+	model2.DeptBy
 }
 
 func (s *WaybillInsertReq) Generate(m *model.Waybill) {
@@ -158,3 +169,22 @@ func (s *WaybillDeleteReq) GetId() interface{} {
 type WaybillInOutReq struct {
 	WaybillNoList []string `json:"waybillNoList"  gorm:"size:128"` // 订单编号
 }
+
+type WaybillGetAppletPageReq struct {
+	dto.Pagination `search:"-"`
+	WaybillNo      string `form:"waybillNo" search:"type:contains;column:waybill_no;table:waybill_logistics"` // 运单编号
+	Status         int    `form:"status" search:"type:exact;column:status;table:waybill_logistics"`           // 状态
+	WaybillOrder
+}
+
+type WaybillGetAppletPageOrder struct {
+	CreatedAtOrder string `search:"type:order;column:created_at;table:waybill_logistics" form:"createdAtOrder" default:"desc"`
+}
+
+func (m *WaybillGetAppletPageReq) GetNeedSearch() interface{} {
+	return *m
+}
+
+type WaybillImportReq struct {
+	CustomerName string `form:"customerName"` //下单客户名称
+}

+ 18 - 0
app/admin/service/dto/waybill_logistics.go

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

+ 42 - 0
app/admin/service/dto/waybill_task.go

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

+ 8 - 7
app/admin/service/sys_user.go

@@ -68,11 +68,12 @@ func (e *SysUser) GetPage(c *dto.SysUserGetPageReq, p *actions.DataPermission, l
 // Get 获取SysUser对象
 func (e *SysUser) Get(d *dto.SysUserGetReq, p *actions.DataPermission, userModel *model.SysUser) error {
 	var data model.SysUser
-	var dept model.SysDept
-	var role model.SysRole
+	//var dept model.SysDept
+	//var role model.SysRole
 	//var post model.SysPost
 	err := e.Orm.Model(&data).
 		Scopes(actions.UserPermission(data.TableName(), p)).
+		Preload("Dept").
 		First(userModel, d.GetId()).Error
 	if err != nil {
 		e.Log.Errorf("db error: %s", err)
@@ -81,11 +82,11 @@ func (e *SysUser) Get(d *dto.SysUserGetReq, p *actions.DataPermission, userModel
 		}
 		return global.GetFailedErr
 	}
-	err = e.Orm.First(&dept, userModel.DeptId).Error
-	userModel.Dept = dept
-
-	err = e.Orm.First(&role, userModel.RoleId).Error
-	userModel.Role = role
+	//err = e.Orm.First(&dept, userModel.DeptId).Error
+	//userModel.Dept = dept
+	//
+	//err = e.Orm.First(&role, userModel.RoleId).Error
+	//userModel.Role = role
 
 	//err = e.Orm.First(&post, userModel.PostId).Error
 	//userModel.Post = post

+ 254 - 5
app/admin/service/waybill.go

@@ -21,6 +21,39 @@ type Waybill struct {
 	service.Service
 }
 
+func WaybillCustomerStatusScopes(status int) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		// 未发货
+		if status == 1 {
+			statusList := []int{
+				model.WaybillStatusWaitDelivery,
+				model.WaybillStatusTruck,
+				model.WaybillStatusWaitStorage,
+			}
+			return db.Where("status in (?)", statusList)
+		}
+		// 已发货
+		if status == 2 {
+			statusList := []int{
+				model.WaybillStatusTruck,
+				model.WaybillStatusStorage,
+				model.WaybillStatusTruckOut,
+				model.WaybillStatusStorageOut,
+			}
+			return db.Where("status in (?)", statusList)
+		}
+		// 已签收
+		if status == 3 {
+			statusList := []int{
+				model.WaybillStatusReceipt,
+			}
+			return db.Where("status in (?)", statusList)
+		}
+		return db
+	}
+
+}
+
 // GetPage 获取Waybill列表
 func (e *Waybill) GetPage(c *dto.WaybillGetPageReq, list *[]model.Waybill, count *int64, p *actions.DataPermission) error {
 	var err error
@@ -32,6 +65,54 @@ func (e *Waybill) GetPage(c *dto.WaybillGetPageReq, list *[]model.Waybill, count
 			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
 			actions.Permission(data.TableName(), p),
 		).
+		Preload("PrintUser").
+		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 (e *Waybill) GetAppletPage(c *dto.WaybillGetAppletPageReq, list *[]model.Waybill, count *int64, p *actions.DataPermission) error {
+	var err error
+	//var data model.Waybill
+	var logistics model.WaybillLogistics
+	err = e.Orm.Table("waybill").
+		Select("waybill.*,waybill_logistics.status as status").
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+			cDto.Paginate(c.GetPageSize(), c.GetPageIndex()),
+			actions.Permission(logistics.TableName(), p)).
+		Where("waybill_logistics.id in (SELECT MAX(id) FROM waybill_logistics where user_id = ? group by waybill_no )", p.UserId).
+		Joins("left join waybill_logistics on waybill.waybill_no = waybill_logistics.waybill_no").
+		Find(&list).Limit(-1).Offset(-1).Count(count).Error
+
+	//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 (e *Waybill) GetCustomerPage(c *dto.WaybillGetCustomerPageReq, list *[]model.Waybill, count *int64, p *actions.DataPermission) error {
+	var err error
+	var data model.Waybill
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			WaybillCustomerStatusScopes(c.Status),
+			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 {
@@ -159,14 +240,36 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 			}
 			return global.UpdateFailedErr
 		}
-		if waybillModel.Status != model.WaybillStatusWaitDelivery {
+		if waybillModel.Status != model.WaybillStatusWaitDelivery &&
+			waybillModel.Status != model.WaybillStatusWaitTruck &&
+			waybillModel.Status != model.WaybillStatusWaitStorage {
 			return errors.New(fmt.Sprintf("运单状态为%s,禁止操作!", model.WaybillStatusMap[waybillModel.Status]))
 		}
+
+		var car = model.Car{}
 		if c.Type == model.SysUserTypeDriver {
 			waybillModel.Status = model.WaybillStatusWaitTruck
+			// 查询车辆库信息
+			err = tx.Scopes(actions.Permission(car.TableName(), p)).
+				Where("user_id = ?", c.PrintUserId).
+				First(&car).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New("获取车辆信息失败")
+			}
 		}
+
+		var warehouse = model.Warehouse{}
 		if c.Type == model.SysUserTypeWarehouse {
 			waybillModel.Status = model.WaybillStatusWaitStorage
+			// 查询仓库信息
+			err = tx.Scopes(actions.Permission(warehouse.TableName(), p)).
+				Where("user_id = ?",c.PrintUserId).
+				First(&warehouse).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New("获取仓库信息失败")
+			}
 		}
 		waybillModel.PrintUserId = c.PrintUserId
 		err = tx.Save(&waybillModel).Error
@@ -174,6 +277,27 @@ func (e *Waybill) Delivery(c *dto.WaybillDeliveryReq, p *actions.DataPermission)
 			e.Log.Errorf("db error: %s", err)
 			return global.UpdateFailedErr
 		}
+
+		// 添加物流
+		Logistics := model.WaybillLogistics{
+			WaybillNo:   waybillModel.WaybillNo,
+			Status:      waybillModel.Status,
+			CarId:       car.Id,
+			WarehouseId: warehouse.Id,
+			UserId:      c.PrintUserId,
+			ControlBy: model2.ControlBy{
+				CreateBy: p.UserId,
+			},
+			DeptBy: model2.DeptBy{
+				DeptId: p.DeptId,
+			},
+		}
+		err = tx.Create(&Logistics).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New(fmt.Sprintf("保存运单物流信息失败:%s", err))
+		}
+
 	}
 
 	return nil
@@ -439,7 +563,7 @@ func (e *Waybill) CarIn(c *dto.WaybillInOutReq, p *actions.DataPermission) error
 			tx.Commit()
 		}
 	}()
-	// 查询仓库信息
+	// 查询车辆信息
 	var car = model.Car{}
 	// 查询运单是否存在
 	err = tx.Scopes(actions.Permission(car.TableName(), p)).
@@ -447,7 +571,7 @@ func (e *Waybill) CarIn(c *dto.WaybillInOutReq, p *actions.DataPermission) error
 		First(&car).Error
 	if err != nil {
 		e.Log.Errorf("db error: %s", err)
-		return errors.New("获取仓库信息失败")
+		return errors.New("获取车辆信息失败")
 	}
 
 	for _, waybillNo := range c.WaybillNoList {
@@ -550,7 +674,7 @@ func (e *Waybill) CarOut(c *dto.WaybillInOutReq, p *actions.DataPermission) erro
 		First(&car).Error
 	if err != nil {
 		e.Log.Errorf("db error: %s", err)
-		return errors.New("获取仓库信息失败")
+		return errors.New("获取车辆信息失败")
 	}
 
 	for _, waybillNo := range c.WaybillNoList {
@@ -568,7 +692,7 @@ func (e *Waybill) CarOut(c *dto.WaybillInOutReq, p *actions.DataPermission) erro
 		}
 
 		if waybillModel.CarId != car.Id {
-			return errors.New("请选择正确仓库出库!")
+			return errors.New("请选择正确车辆下车!")
 		}
 
 		if waybillModel.CarId == car.Id && waybillModel.Status == model.WaybillStatusTruckOut {
@@ -635,3 +759,128 @@ func (e *Waybill) CarOut(c *dto.WaybillInOutReq, p *actions.DataPermission) erro
 
 	return nil
 }
+func (e *Waybill) Receipt(c *dto.WaybillInOutReq, p *actions.DataPermission) error {
+	var err error
+
+	tx := e.Orm.Begin()
+	defer func() {
+		if err != nil {
+			tx.Rollback()
+		} else {
+			tx.Commit()
+		}
+	}()
+	// 查询仓库信息
+	var car = model.Car{}
+	// 查询运单是否存在
+	err = tx.Scopes(actions.Permission(car.TableName(), p)).
+		Where("user_id = ?", p.UserId).
+		First(&car).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return errors.New("获取车辆信息失败")
+	}
+
+	for _, waybillNo := range c.WaybillNoList {
+		var waybillModel = model.Waybill{}
+		// 查询运单是否存在
+		err = tx.Scopes(actions.Permission(waybillModel.TableName(), p)).
+			Where("waybill_no = ?", waybillNo).
+			First(&waybillModel).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			if errors.Is(err, gorm.ErrRecordNotFound) {
+				return errors.New(fmt.Sprintf("运单号%s不存在", waybillNo))
+			}
+			return errors.New(fmt.Sprintf("运单号%s查询失败", waybillNo))
+		}
+
+		if waybillModel.Status == model.WaybillStatusReceipt {
+			continue
+		}
+		waybillModel.Status = model.WaybillStatusReceipt
+		waybillModel.CarId = car.Id
+		waybillModel.ReceiptTime = model2.Time(time.Now())
+		err = tx.Save(waybillModel).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New(fmt.Sprintf("保存运单信息失败:%s", err))
+		}
+		// 获取传感器信息
+		deviceSensorList, count, err := nats_server.Cold_CompanyDeviceSensor_List_ByKey(car.Sn)
+		if err != nil || count == 0 {
+			return errors.New(fmt.Sprintf("查询设备定位信息失败:%s", err.Error()))
+		}
+		var lng, Lat string
+		if len(deviceSensorList[0].T_DeviceSensorData.T_site) > 0 {
+			site := strings.Split(deviceSensorList[0].T_DeviceSensorData.T_site, ",")
+			if len(site) == 2 {
+				lng = site[0]
+				Lat = site[1]
+			}
+		}
+
+		// 查询任务
+		var task model.WaybillTask
+		err = tx.Model(&task).Where("waybill_no = ? and car_id = ?", waybillNo, car.Id).
+			Last(&task).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New(fmt.Sprintf("查询运单任务信息失败:%s", err))
+		}
+		// 未下车 直接点签收
+		if time.Time(task.EndTime).IsZero() {
+			task.EndTime = model2.Time(time.Now())
+			task.UpdateBy = p.UserId
+			err = tx.Save(&task).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单任务信息失败:%s", err))
+			}
+			// 添加下车物流记录
+			Logistics := model.WaybillLogistics{
+				WaybillNo: waybillNo,
+				Status:    model.WaybillStatusTruckOut,
+				CarId:     car.Id,
+				UserId:    p.UserId,
+				Lng:       lng,
+				Lat:       Lat,
+				ControlBy: model2.ControlBy{
+					CreateBy: p.UserId,
+				},
+				DeptBy: model2.DeptBy{
+					DeptId: p.DeptId,
+				},
+			}
+			err = tx.Create(&Logistics).Error
+			if err != nil {
+				e.Log.Errorf("db error: %s", err)
+				return errors.New(fmt.Sprintf("保存运单物流信息失败:%s", err))
+			}
+		}
+
+		// 添加物流
+		Logistics := model.WaybillLogistics{
+			WaybillNo: waybillNo,
+			Status:    model.WaybillStatusReceipt,
+			CarId:     car.Id,
+			UserId:    p.UserId,
+			Lng:       lng,
+			Lat:       Lat,
+			ControlBy: model2.ControlBy{
+				CreateBy: p.UserId,
+			},
+			DeptBy: model2.DeptBy{
+				DeptId: p.DeptId,
+			},
+		}
+		err = tx.Create(&Logistics).Error
+		if err != nil {
+			e.Log.Errorf("db error: %s", err)
+			return errors.New(fmt.Sprintf("保存运单物流信息失败:%s", err))
+		}
+
+	}
+
+	return nil
+}

+ 32 - 0
app/admin/service/waybill_logistics.go

@@ -0,0 +1,32 @@
+package service
+
+import (
+	"cold-logistics/app/admin/model"
+	"cold-logistics/app/admin/service/dto"
+	cDto "cold-logistics/common/dto"
+	"cold-logistics/common/global"
+	"gogs.baozhida.cn/zoie/OAuth-core/service"
+)
+
+type WaybillLogistics struct {
+	service.Service
+}
+
+// GetPage 获取WaybillLogistics列表
+func (e *WaybillLogistics) GetPage(c *dto.WaybillLogisticsGetPageReq, list *[]model.WaybillLogistics, count *int64) error {
+	var err error
+	var data model.WaybillLogistics
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+		).
+		Preload("Warehouse").Preload("Car").
+		Find(list).Limit(-1).Offset(-1).
+		Count(count).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	return nil
+}

+ 140 - 0
app/admin/service/waybill_task.go

@@ -0,0 +1,140 @@
+package service
+
+import (
+	"cold-logistics/app/admin/model"
+	"cold-logistics/app/admin/service/dto"
+	cDto "cold-logistics/common/dto"
+	"cold-logistics/common/global"
+	"cold-logistics/common/nats/nats_server"
+	"errors"
+	"fmt"
+	"gogs.baozhida.cn/zoie/OAuth-core/service"
+	"gorm.io/gorm"
+	"sort"
+	"time"
+)
+
+type WaybillTask struct {
+	service.Service
+}
+
+func WaybillTaskTimeScopes(startTime, endTime string) func(db *gorm.DB) *gorm.DB {
+	return func(db *gorm.DB) *gorm.DB {
+		if len(startTime) == 0 && len(endTime) == 0 {
+			return db
+		}
+		return db.Where("(start_time between ? and ?) or (end_time between ? and ?)",
+			startTime, endTime, startTime, endTime)
+	}
+}
+
+// GetPage 获取WaybillTask列表
+func (e *WaybillTask) GetPage(c *dto.WaybillTaskGetPageReq, list *[]model.WaybillTask, count *int64) error {
+	var err error
+	var data model.WaybillTask
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+		).
+		Preload("Warehouse").Preload("Car").
+		Find(list).Limit(-1).Offset(-1).
+		Count(count).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return global.GetFailedErr
+	}
+	for i := 0; i < len(*list); i++ {
+		// 获取传感器信息
+		deviceSensorList, _, _ := nats_server.Cold_CompanyDeviceSensor_List_ByKey((*list)[i].Sn)
+		(*list)[i].DeviceSensorList = deviceSensorList
+	}
+	return nil
+}
+
+// GetPage 获取WaybillTask列表
+func (e *WaybillTask) GetDataPage(c *dto.WaybillTaskGetDataPageReq) (list []nats_server.DeviceData_R, count int64, err error) {
+	var task model.WaybillTask
+	err = e.Orm.Where("id = ? and waybill_no = ?", c.TaskId, c.WaybillNo).First(&task).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		err = errors.New("获取运单信息错误!")
+		return
+	}
+
+	if len(c.StartTime) > 0 {
+		var st time.Time
+		st, err = time.Parse("2006-01-02 15:04:05", c.StartTime)
+		if err != nil {
+			err = errors.New("解析时间字符串出错!")
+			return
+		}
+		if st.Before(time.Time(task.StartTime)) {
+			c.StartTime = task.StartTime.String()
+		}
+	} else {
+		c.StartTime = task.StartTime.String()
+	}
+	if len(c.EndTime) > 0 {
+		var et time.Time
+		et, err = time.Parse("2006-01-02 15:04:05", c.EndTime)
+		if err != nil {
+			err = errors.New("解析时间字符串出错!")
+			return
+		}
+		if et.After(time.Time(task.EndTime)) {
+			c.EndTime = task.EndTime.String()
+		}
+	} else {
+		c.EndTime = task.EndTime.String()
+	}
+
+	T_snid := ""
+	for _, id := range c.T_ids {
+		T_snid += fmt.Sprintf("%s,%d|", task.Sn, id)
+	}
+	// 获取传感器信息
+	list, count, err = nats_server.Cold_ReadDeviceDataListBy_T_snid(T_snid, c.StartTime, c.EndTime, c.Page, c.PageSize)
+	return
+}
+func (e *WaybillTask) GetLocus(c *dto.WaybillGetLocusReq) ([]nats_server.DeviceData_R2, error) {
+	var err error
+	var data model.WaybillTask
+	var taskList []model.WaybillTask
+	locusList := make([]nats_server.DeviceData_R2, 0)
+
+	err = e.Orm.Model(&data).
+		Scopes(
+			cDto.MakeCondition(c.GetNeedSearch()),
+		).Where("car_id > 0").
+		Find(&taskList).Error
+	if err != nil {
+		e.Log.Errorf("db error: %s", err)
+		return locusList, global.GetFailedErr
+	}
+
+	for i := 0; i < len(taskList); i++ {
+		// 获取传感器信息
+		deviceSensorList, _, _ := nats_server.Cold_CompanyDeviceSensor_List_ByKey(taskList[i].Sn)
+		if len(deviceSensorList) > 0 {
+			T_snid := fmt.Sprintf("%s,%d|", taskList[i].Sn, deviceSensorList[0].T_id)
+			dataList, _, err := nats_server.Cold_ReadDeviceDataListBy_T_snidForLocus(T_snid, taskList[i].StartTime.String(), taskList[i].EndTime.String(), 0, 9999)
+			if err != nil {
+				e.Log.Errorf("nats 获取轨迹信息失败: %s", err)
+				return locusList, global.GetFailedErr
+			}
+
+			// 倒序
+			sort.Slice(dataList, func(i, j int) bool {
+				if dataList[i].T_time < dataList[j].T_time {
+					return true
+				}
+				return false
+			})
+			locusList = append(locusList, dataList...)
+
+		}
+
+	}
+	return locusList, nil
+}

+ 1 - 0
common/middleware/auth.go

@@ -35,3 +35,4 @@ func AuthInit() (*jwt.GinJWTMiddleware, error) {
 	})
 
 }
+

+ 6 - 6
common/middleware/handler/auth.go

@@ -142,10 +142,11 @@ func Authenticator(c *gin.Context) (interface{}, error) {
 
 	username = loginVals.Username
 
-	single, err := GetSingleLogin(c)
-	if err != nil {
-		return nil, err
-	}
+	var single bool
+	//single, err = GetSingleLogin(c)
+	//if err != nil {
+	//	return nil, err
+	//}
 
 	return map[string]interface{}{"user": u, "role": role, "dept": dept, "single": single, "mobile": loginVals.Mobile}, nil
 }
@@ -258,12 +259,11 @@ func GetSingleLogin(c *gin.Context) (bool, error) {
 	var result string
 	err = ormDB.Table("sys_config").Select("config_value").Where("config_key = ? ", "sys_single_login").Scan(&result).Error
 	if err != nil {
-
-		log.Errorf("get sys_config error, %s", err.Error())
 		if errors.Is(err, gorm.ErrRecordNotFound) || err.(*mysql.MySQLError).Number == 1146 {
 			// 默认为非单一登录
 			return false, nil
 		}
+		log.Errorf("get sys_config error, %s", err.Error())
 		return false, err
 	}
 

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

@@ -3,6 +3,7 @@ package nats_server
 import (
 	"cold-logistics/common/nats"
 	"cold-logistics/conf"
+	"errors"
 	"github.com/vmihailenco/msgpack/v5"
 	"time"
 )
@@ -36,6 +37,91 @@ func Cold_CompanyDeviceSensor_List_ByKey(T_sn string) (data []DeviceSensor_R, co
 	if err != nil {
 		return
 	}
+	if t_R.Code != 200 {
+		err = errors.New(t_R.Msg)
+	}
+
+	return t_R.Data, t_R.Count, nil
+}
+func Cold_ReadDeviceDataListBy_T_snid(T_snid, startTime, endTime string, page, page_z int) (data []DeviceData_R, count int64, err error) {
+
+	type T_Req struct {
+		T_snid     string `xml:"T_snid"`
+		Time_start string `xml:"Time_start"`
+		Time_end   string `xml:"Time_end"`
+		Page       int    `xml:"Page"`
+		Page_z     int    `xml:"Page_z"`
+	}
+	t_Req := T_Req{
+		T_snid:     T_snid,
+		Time_start: startTime,
+		Time_end:   endTime,
+		Page:       page,
+		Page_z:     page_z,
+	}
+	b, _ := msgpack.Marshal(&t_Req)
+
+	msg, err := nats.Nats.Request("Cold_ReadDeviceDataListBy_T_snid", b, 3*time.Second)
+	if err != nil {
+		return
+	}
+
+	type T_R struct {
+		Code  int16          `xml:"Code"`
+		Msg   string         `xml:"Msg"`
+		Count int64          `xml:"Count"`
+		Data  []DeviceData_R `xml:"Data"` // 泛型
+	}
+	var t_R T_R
+
+	err = msgpack.Unmarshal(msg.Data, &t_R)
+	if err != nil {
+		return
+	}
+	if t_R.Code != 200 {
+		err = errors.New(t_R.Msg)
+	}
+
+	return t_R.Data, t_R.Count, nil
+}
+func Cold_ReadDeviceDataListBy_T_snidForLocus(T_snid, startTime, endTime string, page, page_z int) (data []DeviceData_R2, count int64, err error) {
+
+	type T_Req struct {
+		T_snid     string `xml:"T_snid"`
+		Time_start string `xml:"Time_start"`
+		Time_end   string `xml:"Time_end"`
+		Page       int    `xml:"Page"`
+		Page_z     int    `xml:"Page_z"`
+	}
+	t_Req := T_Req{
+		T_snid:     T_snid,
+		Time_start: startTime,
+		Time_end:   endTime,
+		Page:       page,
+		Page_z:     page_z,
+	}
+	b, _ := msgpack.Marshal(&t_Req)
+
+	msg, err := nats.Nats.Request("Cold_ReadDeviceDataListBy_T_snid", b, 3*time.Second)
+	if err != nil {
+		return
+	}
+
+	type T_R struct {
+		Code  int16           `xml:"Code"`
+		Msg   string          `xml:"Msg"`
+		Count int64           `xml:"Count"`
+		Data  []DeviceData_R2 `xml:"Data"` // 泛型
+	}
+	var t_R T_R
+
+	err = msgpack.Unmarshal(msg.Data, &t_R)
+	if err != nil {
+		return
+	}
+	if t_R.Code != 200 {
+		err = errors.New(t_R.Msg)
+	}
 
 	return t_R.Data, t_R.Count, nil
 }

+ 46 - 39
common/nats/nats_server/models.go

@@ -37,40 +37,47 @@ type DeviceSensor_R struct {
 	T_id   int    // 传感器编号
 	T_name string // 标题
 
-	T_pid      int    // 公司id
-	T_pid_name string // 公司名称
+	//T_pid      int    // 公司id
+	//T_pid_name string // 公司名称
+	//
+	//T_3dview   string // 3D 视图ID
+	//T_sort     int    // 排序
+	//T_Dattery  int    // 电量
+	//T_Site     string // GPS
+	//T_monitor  int    // 记录状态
+	//T_online   int    // 在线状态 1 在线  0 离线
+	//T_online_s int    // 在线状态-备用  0 未启用  1 在线  2 离线
+	//T_datashow int    // 0 屏蔽数据展示  1 正常数据展示
+	//T_type     int    // 类型
+	//T_link     int    // 0:断开/故障 1连接 实时数据
+	//T_State    int    // 0 屏蔽   1 正常  (屏蔽后 只有内部管理员才能看到,用户 输入SN\名称 搜索时 也能看到)
 
-	T_3dview   string // 3D 视图ID
-	T_sort     int    // 排序
-	T_Dattery  int    // 电量
-	T_Site     string // GPS
-	T_monitor  int    // 记录状态
-	T_online   int    // 在线状态 1 在线  0 离线
-	T_online_s int    // 在线状态-备用  0 未启用  1 在线  2 离线
-	T_datashow int    // 0 屏蔽数据展示  1 正常数据展示
-	T_type     int    // 类型
-	T_link     int    // 0:断开/故障 1连接 实时数据
-	T_State    int    // 0 屏蔽   1 正常  (屏蔽后 只有内部管理员才能看到,用户 输入SN\名称 搜索时 也能看到)
-
-	T_DeviceSensorData      DeviceData_R            // 传感器最新数据
-	T_DeviceSensorParameter DeviceSensorParameter_R //  设备参数
+	T_DeviceSensorData DeviceData_R2 // 传感器最新数据
+	//T_DeviceSensorParameter DeviceSensorParameter_R //  设备参数
+}
+type DeviceData_R2 struct {
+	T_t    float32 // 温度
+	T_rh   float32 // 湿度
+	T_site string  // GPS
+	T_time string  // 采集时间
 }
+
 type DeviceData_R struct {
-	T_sn     string  // sn
-	T_id     int     // 传感器id
-	T_name   string  // 传感器名称
-	T_t      float32 // 温度
-	T_rh     float32 // 湿度
-	T_site   string  // GPS
-	T_tl     float32 // 温度下限
-	T_tu     float32 // 温度上限
-	T_rhl    float32 // 湿度下限
-	T_rhu    float32 // 湿度上限
-	T_time   string  // 采集时间
-	T_sp     int     // 传感器参数id
-	T_ist    int     // 温度   1开启   2关闭
-	T_ish    int     // 湿度   1开启   2关闭
-	T_remark string  // 备注
+	T_sn   string  // sn
+	T_id   int     // 传感器id
+	T_name string  // 传感器名称
+	T_t    float32 // 温度
+	T_rh   float32 // 湿度
+	T_site string  // GPS
+	T_tl   float32 // 温度下限
+	T_tu   float32 // 温度上限
+	T_rhl  float32 // 湿度下限
+	T_rhu  float32 // 湿度上限
+	T_time string  // 采集时间
+	//T_sp     int     // 传感器参数id
+	//T_ist    int     // 温度   1开启   2关闭
+	//T_ish    int     // 湿度   1开启   2关闭
+	//T_remark string  // 备注
 }
 
 type DeviceSensorParameter_R struct {
@@ -82,17 +89,17 @@ type DeviceSensorParameter_R struct {
 	T_RHlower float32 //  湿度下限
 	T_RHupper float32 //  湿度上限
 	// 预警
-	T_enprel     int     // 是否启用预警
-	T_tprel      float32 //  温度预警下限
-	T_tpreu      float32 //  温度预警上限
-	T_hprel      float32 //  湿度预警下限
-	T_hpreu      float32 //  温度预警上限
-	T_enprelnote int     // 预警记录数据
+	//T_enprel     int     // 是否启用预警
+	//T_tprel      float32 //  温度预警下限
+	//T_tpreu      float32 //  温度预警上限
+	//T_hprel      float32 //  湿度预警下限
+	//T_hpreu      float32 //  温度预警上限
+	//T_enprelnote int     // 预警记录数据
 
 	//T_speed int // 传感器采样率   s(1~240) 默认:15 *
 	//T_sense int // 传感器灵敏度   s(0~10) 默认:5
-	T_en   int // en:是否启用传感器,
-	T_free int // free:监测点是否为闲置状态(空库,只监测不报警)
+	//T_en   int // en:是否启用传感器,
+	//T_free int // free:监测点是否为闲置状态(空库,只监测不报警)
 
 	T_time JsonTime
 }

+ 11 - 5
go.mod

@@ -21,7 +21,7 @@ require (
 	github.com/swaggo/swag v1.8.12
 	github.com/unrolled/secure v1.12.0
 	go.uber.org/zap v1.19.1
-	golang.org/x/crypto v0.18.0
+	golang.org/x/crypto v0.19.0
 	gorm.io/driver/mysql v1.3.5
 	gorm.io/gorm v1.23.8
 )
@@ -29,6 +29,8 @@ require (
 require (
 	github.com/go-redis/redis/v7 v7.4.0
 	github.com/nats-io/nats.go v1.34.0
+	github.com/vmihailenco/msgpack/v5 v5.4.1
+	github.com/xuri/excelize/v2 v2.8.1
 	gogs.baozhida.cn/zoie/OAuth-core v0.0.0-00010101000000-000000000000
 )
 
@@ -82,12 +84,15 @@ require (
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
 	github.com/mojocn/base64Captcha v1.3.1 // indirect
 	github.com/nats-io/nkeys v0.4.7 // indirect
 	github.com/nats-io/nuid v1.0.1 // indirect
 	github.com/nsqio/go-nsq v1.0.8 // indirect
 	github.com/nyaruka/phonenumbers v1.0.55 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
+	github.com/richardlehane/mscfb v1.0.4 // indirect
+	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/robinjoseph08/redisqueue/v2 v2.1.0 // indirect
 	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/shamsher31/goimgext v1.0.0 // indirect
@@ -98,15 +103,16 @@ require (
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
 	github.com/ugorji/go/codec v1.2.9 // indirect
 	github.com/urfave/cli v1.22.1 // indirect
-	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
 	github.com/vmihailenco/tagparser/v2 v2.0.0 // 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
 	go.uber.org/multierr v1.7.0 // indirect
 	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
-	golang.org/x/image v0.0.0-20220722155232-062f8c9fd539 // indirect
-	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/image v0.14.0 // indirect
+	golang.org/x/net v0.21.0 // indirect
 	golang.org/x/sync v0.1.0 // indirect
-	golang.org/x/sys v0.16.0 // indirect
+	golang.org/x/sys v0.17.0 // indirect
 	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

+ 22 - 9
go.sum

@@ -443,6 +443,8 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
 github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0=
 github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY=
 github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@@ -530,6 +532,11 @@ github.com/qiniu/go-sdk/v7 v7.13.0/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFs
 github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
 github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
+github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
+github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
+github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
 github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
 github.com/robinjoseph08/redisqueue/v2 v2.1.0 h1:GactHlrxS8YSCJc4CbP1KbTObo14pieNmNWSUlquTGI=
@@ -582,8 +589,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
 github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
 github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+zy8M=
@@ -629,6 +636,12 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
 github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
 github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0=
+github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
+github.com/xuri/excelize/v2 v2.8.1 h1:pZLMEwK8ep+CLIUWpWmvW8IWE/yxqG0I1xcN6cVMGuQ=
+github.com/xuri/excelize/v2 v2.8.1/go.mod h1:oli1E4C3Pa5RXg1TBXn4ENCXDV5JUMlBluUhG7c+CEE=
+github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4=
+github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -682,8 +695,8 @@ golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -692,8 +705,8 @@ golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u0
 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.0.0-20220722155232-062f8c9fd539 h1:/eM0PCrQI2xd471rI+snWuu251/+/jpBpZqir2mPdnU=
-golang.org/x/image v0.0.0-20220722155232-062f8c9fd539/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
+golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
+golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -740,8 +753,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -806,8 +819,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=