Procházet zdrojové kódy

work_order: 工单管理异步

Yi před 6 měsíci
rodič
revize
79988fb3a4

+ 14 - 9
cmd/crontab.go

@@ -3,10 +3,10 @@ package cmd
 import (
 	"context"
 	"fmt"
-	"kpt-pasture/config"
-	"kpt-pasture/store/kptstore"
+	"kpt-pasture/dep"
 
 	"gitee.com/xuyiping_admin/pkg/cmd"
+	"gitee.com/xuyiping_admin/pkg/xerr"
 	"github.com/spf13/cobra"
 )
 
@@ -20,18 +20,23 @@ var CrontabCmd = &cobra.Command{
 }
 
 func init() {
-	// 每日同步脚本
+	// 每日零点更新牛只日龄脚本
 	cmd.Instant(JobCmd, "update:cowAge", UpdateCowDayAge, "更新牛只日龄")
+	cmd.Instant(JobCmd, "gen:cowAge", GenerateWorkOrder, "创建每天的工单")
 }
 
 func UpdateCowDayAge(ctx context.Context, args []string) error {
-	cfg := config.Options()
-	db := kptstore.MustMigrateStore(cfg)
-	sqlDB, err := db.DB()
-	if err != nil {
-		panic(err)
+	crontab := dep.DICrontabService()
+	if err := crontab.UpdateCowDayAge(); err != nil {
+		xerr.ReportSentry(ctx, err)
 	}
-	defer sqlDB.Close()
+	return nil
+}
 
+func GenerateWorkOrder(ctx context.Context, args []string) error {
+	crontab := dep.DICrontabService()
+	if err := crontab.GenerateWorkOrder(); err != nil {
+		xerr.ReportSentry(ctx, err)
+	}
 	return nil
 }

+ 1 - 0
config/app.develop.yaml

@@ -1,3 +1,4 @@
+farm_name: xdmy001
 app_name: kpt-pasture
 app_environment: test
 debug: true

+ 3 - 3
config/app.go

@@ -21,6 +21,7 @@ var (
 
 // AppConfig store all configuration options
 type AppConfig struct {
+	FarmName       string `json:"farm_name"`
 	AppName        string `yaml:"app_name"`
 	AppEnv         string `yaml:"app_environment"`
 	Debug          bool   `yaml:"debug" env:"APP_DEBUG"`
@@ -40,7 +41,6 @@ type AppConfig struct {
 
 	// asynq 相关配置
 	SideWorkSetting SideWorkSetting `yaml:"side_work_setting"`
-	CacheKeySuffix  string          `yaml:"cache_key_suffix"`
 }
 
 type JwtTokenKeyConfig struct {
@@ -152,8 +152,8 @@ func (a *AppConfig) Name() string {
 //	支持更多的key空间, 可以更灵活的定义ns
 func (a *AppConfig) CacheNameSpace() string {
 	cacheKeySpace := ""
-	if a.CacheKeySuffix != "" {
-		cacheKeySpace = fmt.Sprintf("%s-%s-%s", a.AppName, a.AppEnv, a.CacheKeySuffix)
+	if a.FarmName != "" {
+		cacheKeySpace = fmt.Sprintf("%s-%s-%s", a.AppName, a.AppEnv, a.FarmName)
 	} else {
 		cacheKeySpace = fmt.Sprintf("%s-%s", a.AppName, a.AppEnv)
 	}

+ 5 - 5
config/load_config.go

@@ -11,13 +11,13 @@ import (
 	"github.com/spf13/viper"
 )
 
-var workDir = os.Getenv("PASTURE_WORK_DIR")
+var WorkDir = os.Getenv("PASTURE_WORK_DIR")
 
 func Initialize(path string, cfgStruct interface{}) error {
-	if workDir == "" {
-		workDir = "."
+	if WorkDir == "" {
+		WorkDir = "."
 	}
-	dir := fmt.Sprintf("%s/config/%s", workDir, path)
+	dir := fmt.Sprintf("%s/config/%s", WorkDir, path)
 	viper.SetConfigType("yaml")
 	viper.SetConfigFile(dir)
 	if err := viper.ReadInConfig(); err != nil {
@@ -32,7 +32,7 @@ func Initialize(path string, cfgStruct interface{}) error {
 }
 
 func openPrivateKey() JwtTokenKeyConfig {
-	pathList := []string{fmt.Sprintf("%s/config/private.key", workDir), fmt.Sprintf("%s/config/public.key", workDir)}
+	pathList := []string{fmt.Sprintf("%s/config/private.key", WorkDir), fmt.Sprintf("%s/config/public.key", WorkDir)}
 	res := JwtTokenKeyConfig{PublicKey: nil, PrivateKey: nil}
 	for i, path := range pathList {
 		keyConfig, err := os.Open(path)

+ 2 - 2
dep/dep.go

@@ -3,8 +3,8 @@ package dep
 import (
 	"kpt-pasture/config"
 	"kpt-pasture/module/backend"
+	"kpt-pasture/module/consumer"
 	"kpt-pasture/module/crontab"
-	"kpt-pasture/module/job"
 	"kpt-pasture/service/asynqsvc"
 	"kpt-pasture/service/redis"
 	"kpt-pasture/service/sso"
@@ -33,7 +33,7 @@ func Options() []di.HubOption {
 		sso.Module,
 		wechat.Module,
 		asynqsvc.Module,
-		job.Module,
+		consumer.Module,
 		redis.Module,
 		crontab.Module,
 	}

+ 2 - 2
dep/di_asynq.go

@@ -3,7 +3,7 @@ package dep
 import (
 	"kpt-pasture/config"
 	"kpt-pasture/model"
-	"kpt-pasture/module/job"
+	"kpt-pasture/module/consumer"
 	"kpt-pasture/service/asynqsvc"
 
 	"go.uber.org/dig"
@@ -31,5 +31,5 @@ func AsynqWorkOrder(dep AsyncDependency) *asynqsvc.Server {
 type AsyncDependency struct {
 	dig.In
 
-	WorkOrder job.BizExec // BizExec 工单
+	WorkOrder consumer.BizExec // BizExec 工单
 }

+ 13 - 0
dep/di_crontab.go

@@ -1 +1,14 @@
 package dep
+
+import "kpt-pasture/module/crontab"
+
+func DICrontabService() crontab.Crontab {
+	var out crontab.Crontab
+	if err := DI().Invoke(func(in crontab.Crontab) {
+		out = in
+	}); err != nil {
+		panic(err)
+	}
+
+	return out
+}

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323
 	gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/eko/gocache v1.1.0

+ 4 - 0
go.sum

@@ -44,6 +44,10 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20240829061040-237e8a327a2a h1:ppuGlLKW
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240829061040-237e8a327a2a/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d h1:t78Yqy/oBGUvkYKNh4DAS4MmYBd9ey9RIdI8ghd4/wg=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240902063446-64deca9b4509 h1:iK8ZHFwsFQ8iyLHzSzrLRgYZlJOLHGiIJPyogZAtS8k=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240902063446-64deca9b4509/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323 h1:VcOc6Wjo/Gmd54v3bc9DhvwD/Cx8cwcjHvPvUjY5N+k=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240902071101-20469475f323/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015 h1:dfb5dRd57L2HKjdwLT93UFmPYFPOmEl56gtZmqcNnaE=
 gitee.com/xuyiping_admin/pkg v0.0.0-20231218082641-aac597b8a015/go.mod h1:Fk4GYI/v0IK3XFrm1Gn+VkgCz5Y7mfswD5hsTJYOG6A=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=

+ 6 - 2
http/handler/upload/upload.go

@@ -2,6 +2,7 @@ package upload
 
 import (
 	"fmt"
+	"kpt-pasture/config"
 	"net/http"
 	"os"
 	"time"
@@ -24,7 +25,9 @@ func Photos(c *gin.Context) {
 		return
 	}
 
-	saveDir := fmt.Sprintf("./files/photos/%s", time.Now().Format("20060102"))
+	workDir := fmt.Sprintf("%s", config.WorkDir)
+	pathDir := fmt.Sprintf("/files/photos/%s", time.Now().Format("20060102"))
+	saveDir := fmt.Sprintf("%s/%s", workDir, pathDir)
 	if _, err = os.Stat(saveDir); os.IsNotExist(err) {
 		if err = os.MkdirAll(saveDir, 0755); err != nil {
 			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("创建目录失败: %s", err.Error()))
@@ -47,11 +50,12 @@ func Photos(c *gin.Context) {
 			return
 		}
 		fpath := fmt.Sprintf("%s/%d_%d_%s", saveDir, timestamp, i+1, file.Filename)
+		urlPath := fmt.Sprintf("%s/%d_%d_%s", pathDir, timestamp, i+1, file.Filename)
 		if err = c.SaveUploadedFile(file, fpath); err != nil {
 			apierr.AbortBadRequest(c, http.StatusBadRequest, xerr.Customf("保存文件失败: %s", err.Error()))
 			return
 		}
-		filePaths[i] = fpath
+		filePaths[i] = urlPath
 	}
 	c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": "ok", "data": filePaths})
 }

+ 19 - 0
http/handler/work/order.go

@@ -85,3 +85,22 @@ func OrderIsShow(c *gin.Context) {
 		Data: &operationPb.Success{Success: true},
 	})
 }
+
+func UserOrderList(c *gin.Context) {
+	statusStr := c.Param("status")
+	workOrderStatus, _ := strconv.Atoi(statusStr)
+
+	pagination := &pasturePb.PaginationModel{
+		Page:       int32(c.GetInt(middleware.Page)),
+		PageSize:   int32(c.GetInt(middleware.PageSize)),
+		PageOffset: int32(c.GetInt(middleware.PageOffset)),
+	}
+
+	res, err := middleware.Dependency(c).StoreEventHub.OpsService.UserWorkOrderList(c, pasturePb.WorkOrderStatus_Kind(workOrderStatus), pagination)
+	if err != nil {
+		apierr.ClassifiedAbort(c, err)
+		return
+	}
+
+	ginutil.JSONResp(c, res)
+}

+ 17 - 1
http/middleware/cors.go

@@ -1,9 +1,12 @@
 package middleware
 
 import (
-	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 	"io/ioutil"
 	"net/http"
+	"path/filepath"
+	"runtime"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
 
 	"go.uber.org/zap"
 
@@ -43,10 +46,23 @@ func CORS(configs ...cors.Config) gin.HandlerFunc {
 		defer func() {
 			if err := recover(); err != nil {
 				body, _ := ioutil.ReadAll(c.Request.Body)
+
+				// 获取 panic 发生的位置
+				pc, file, line, ok := runtime.Caller(1)
+				funcName := ""
+				if ok {
+					funcName := runtime.FuncForPC(pc).Name()
+					// 去除包路径,只保留函数名
+					funcName = filepath.Base(funcName)
+					file = filepath.Base(file)
+				}
 				zaplog.Error("cors",
 					zap.Any("recover", err),
 					zap.Any("url", c.Request.URL),
 					zap.Any("method", method),
+					zap.Any("file", file),
+					zap.Any("line", line),
+					zap.Any("func", funcName),
 					zap.Any("request", string(body)),
 				)
 			}

+ 19 - 6
http/middleware/log.go

@@ -3,7 +3,6 @@ package middleware
 import (
 	"bytes"
 	"io/ioutil"
-
 	"net"
 	"net/http"
 	"net/http/httputil"
@@ -22,6 +21,15 @@ type responseBodyWriter struct {
 	body *bytes.Buffer
 }
 
+type stackErr struct {
+	Err   error
+	Stack string
+}
+
+func (s *stackErr) Error() string {
+	return s.Err.Error()
+}
+
 func (r responseBodyWriter) Write(b []byte) (int, error) {
 	r.body.Write(b)
 	return r.ResponseWriter.Write(b)
@@ -42,19 +50,24 @@ func GinLogger() gin.HandlerFunc {
 		start := time.Now()
 		c.Next()
 		cost := time.Since(start)
-
 		logFields := []zap.Field{
 			zap.Int("status", c.Writer.Status()),
 			zap.String("request", c.Request.Method+" "+c.Request.URL.String()),
 			zap.String("query", c.Request.URL.RawQuery),
 			zap.String("ip", c.ClientIP()),
 			zap.String("user-agent", c.Request.UserAgent()),
-			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
+			//zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
 			zap.String("time", cost.String()),
+			zap.String("Request body", string(requestBody)),
+			zap.String("Response body", w.body.String()),
+		}
+		if len(c.Errors) > 0 {
+			logFields = append(logFields, zap.Any("stack", string(debug.Stack())))
+			zaplog.Error("Http-Access-Error", logFields...)
+			c.Abort()
+		} else {
+			zaplog.Info("Http-Access-Log", logFields...)
 		}
-		logFields = append(logFields, zap.String("Request body", string(requestBody)))
-		logFields = append(logFields, zap.String("Response body", w.body.String()))
-		zaplog.Info("Http-Access-Log", logFields...)
 	}
 }
 

+ 1 - 1
http/route/root.go

@@ -20,7 +20,7 @@ func Root(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 			middleware.Pagination(),
 			requestid.New(),
 			gzip.Gzip(gzip.DefaultCompression),
-			gin.Recovery(),
+			middleware.GinRecovery(true),
 		)
 	}
 }

+ 1 - 1
http/route/system_api.go

@@ -59,6 +59,6 @@ func SystemAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 func authRouteGroup(s *gin.Engine, relativePath string) *gin.RouterGroup {
 	group := s.Group(relativePath)
 	// 中间件鉴权
-	group.Use(middleware.RequireAdmin(), middleware.GinLogger(), middleware.CORS())
+	group.Use(middleware.RequireAdmin(), middleware.CORS(), middleware.GinLogger())
 	return group
 }

+ 1 - 0
http/route/work_order.go

@@ -16,5 +16,6 @@ func WorkOrderAPI(opts ...func(engine *gin.Engine)) func(s *gin.Engine) {
 		workRoute.POST("/order/list", work.OrderList)
 		workRoute.POST("/order/createOrUpdate", work.OrderCreateOrUpdate)
 		workRoute.PUT("/order/is_show/:id", work.OrderIsShow)
+		workRoute.GET("/user/order/list/:status", work.UserOrderList)
 	}
 }

+ 1 - 0
model/cow.go

@@ -16,6 +16,7 @@ type Cow struct {
 	EarOldNumber        string                     `json:"ear_old_number"`
 	PenId               int64                      `json:"pen_id"`
 	Lact                int32                      `json:"lact"`
+	DayAge              int32                      `json:"day_age"`
 	CowType             pasturePb.CowType_Kind     `json:"cow_type"`
 	BreedStatus         pasturePb.BreedStatus_Kind `json:"breed_status"`
 	CowKind             pasturePb.CowKind_Kind     `json:"cow_kind"`

+ 22 - 1
model/work_order_sub.go

@@ -25,6 +25,7 @@ type WorkOrderSub struct {
 	ExecTime           int64                          `json:"execTime"`
 	Status             pasturePb.WorkOrderStatus_Kind `json:"status"`
 	ExecUserId         int64                          `json:"execUserId"`
+	SetUserIds         string                         `json:"setUserIds"`
 	IsShow             pasturePb.IsShow_Kind          `json:"isShow"`
 	FinishTime         int64                          `json:"finishTime"`
 	Remarks            string                         `json:"remarks"`
@@ -62,6 +63,7 @@ func NewWorkOrderSub(req *WorkOrderMaster) []*WorkOrderSub {
 			WorkOrderMasterId:  req.Id,
 			ExecTime:           currentExecTime,
 			ExecUserId:         0,
+			SetUserIds:         req.ExecPersons,
 			WorkOrderSubNumber: fmt.Sprintf("s%s%d", LayoutDate, time.Now().Unix()),
 			Status:             pasturePb.WorkOrderStatus_Created,
 			IsShow:             pasturePb.IsShow_Ok,
@@ -85,13 +87,13 @@ func NewWorkOrderSub(req *WorkOrderMaster) []*WorkOrderSub {
 					WorkOrderSubNumber: fmt.Sprintf("s%s%d", LayoutDate, execTime),
 					Status:             pasturePb.WorkOrderStatus_Created,
 					IsShow:             pasturePb.IsShow_Ok,
+					SetUserIds:         req.ExecPersons,
 					Remarks:            "",
 				}
 				workOrderSubList = append(workOrderSubList, sub)
 			}
 		}
 	}
-
 	return workOrderSubList
 }
 
@@ -113,3 +115,22 @@ func shouldGenerateSubOrder(frequency pasturePb.WorkOrderFrequency_Kind, t time.
 	}
 	return false
 }
+
+type WorkOrderSubSlice []*WorkOrderSub
+
+func (w WorkOrderSubSlice) ToPB(mMap map[int64]*WorkOrderMaster, priorityMap map[pasturePb.Priority_Kind]string) []*pasturePb.UserWorkOrderList {
+	res := make([]*pasturePb.UserWorkOrderList, len(w))
+	for i, v := range w {
+		res[i] = &pasturePb.UserWorkOrderList{
+			WorkOrderNumber:       v.WorkOrderSubNumber,
+			WorkOrderName:         mMap[v.WorkOrderMasterId].Name,
+			WorkOrderId:           int32(v.WorkOrderMasterId),
+			WorkOrderCategoryName: mMap[v.WorkOrderMasterId].CategoryName,
+			WorkOrderPriorityName: priorityMap[mMap[v.WorkOrderMasterId].Priority],
+			Photos:                strings.Split(mMap[v.WorkOrderMasterId].Photos, ","),
+			Remarks:               mMap[v.WorkOrderMasterId].Remarks,
+			Status:                v.Status,
+		}
+	}
+	return res
+}

+ 1 - 0
module/backend/interface.go

@@ -194,4 +194,5 @@ type WorkService interface {
 	OrderList(ctx context.Context, req *pasturePb.SearchWorkOrderRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchWorkOrderResponse, error)
 	OrderCreateOrUpdate(ctx context.Context, req *pasturePb.WorkOrderList) error
 	OrderIsShow(ctx context.Context, id int64) error
+	UserWorkOrderList(ctx context.Context, workOrderStatus pasturePb.WorkOrderStatus_Kind, pagination *pasturePb.PaginationModel) (*pasturePb.UserWorkOrderResponse, error)
 }

+ 65 - 8
module/backend/work.go

@@ -93,7 +93,19 @@ func (s *StoreEntry) OrderCreateOrUpdate(ctx context.Context, req *pasturePb.Wor
 		}
 	} else {
 		if err := s.DB.Transaction(func(tx *gorm.DB) error {
-			if err := tx.Model(newWorkOrderMaster).Where("id = ?", req.Id).Updates(newWorkOrderMaster).Error; err != nil {
+			if err := tx.Model(newWorkOrderMaster).Where("id = ?", req.Id).Updates(map[string]interface{}{
+				"name":              newWorkOrderMaster.Name,
+				"frequency":         newWorkOrderMaster.Frequency,
+				"priority":          newWorkOrderMaster.Priority,
+				"subscribe_unit":    newWorkOrderMaster.SubscribeUnit,
+				"category_id":       newWorkOrderMaster.CategoryId,
+				"exec_time":         newWorkOrderMaster.ExecTime,
+				"exec_persons":      newWorkOrderMaster.ExecPersons,
+				"exec_person_names": newWorkOrderMaster.ExecPersonNames,
+				"week_month_value":  newWorkOrderMaster.WeekMonthValue,
+				"remark":            newWorkOrderMaster.Remarks,
+				"photos":            newWorkOrderMaster.Photos,
+			}).Error; err != nil {
 				return xerr.WithStack(err)
 			}
 
@@ -119,17 +131,14 @@ func (s *StoreEntry) OrderIsShow(ctx context.Context, id int64) error {
 		}
 		return xerr.WithStack(err)
 	}
-
 	isShow := pasturePb.IsShow_No
 	if workOrderMaster.IsShow == pasturePb.IsShow_No {
 		isShow = pasturePb.IsShow_Ok
 	}
-
 	if err := s.DB.Transaction(func(tx *gorm.DB) error {
 		if err := tx.Model(workOrderMaster).Where("id = ?", id).Update("is_show", isShow).Error; err != nil {
 			return xerr.WithStack(err)
 		}
-
 		workOrderMaster.IsShow = isShow
 		if err := s.WorkOrderSubUpdate(tx, workOrderMaster); err != nil {
 			return xerr.WithStack(err)
@@ -146,6 +155,10 @@ func (s *StoreEntry) OrderIsShow(ctx context.Context, id int64) error {
 func (s *StoreEntry) WorkOrderSubUpdate(tx *gorm.DB, workOrderMaster *model.WorkOrderMaster) error {
 	if workOrderMaster.IsShow == pasturePb.IsShow_Ok {
 		newWorkOrderSubList := model.NewWorkOrderSub(workOrderMaster)
+		if len(newWorkOrderSubList) <= 0 {
+			return nil
+		}
+
 		if err := tx.Create(&newWorkOrderSubList).Error; err != nil {
 			return xerr.WithStack(err)
 		}
@@ -166,15 +179,16 @@ func (s *StoreEntry) WorkOrderSubUpdate(tx *gorm.DB, workOrderMaster *model.Work
 		if count <= 0 {
 			return nil
 		}
-
 		if err := tx.Model(new(model.WorkOrderSub)).
 			Where("work_order_master_id = ?", workOrderMaster.Id).
 			Where("is_show = ?", pasturePb.IsShow_Ok).
 			Where("exec_time > ?", zeroTime).
 			Where("status = ?", pasturePb.WorkOrderStatus_Distribute).
 			Update("status", pasturePb.WorkOrderStatus_Canceled).Error; err != nil {
+			fmt.Println("=====007==========", err)
 			return xerr.WithStack(err)
 		}
+		fmt.Println("=====008==========")
 	}
 	return nil
 }
@@ -223,8 +237,51 @@ func (s *StoreEntry) SendAsynqWorkOrder(ctx context.Context, workOrder *model.Wo
 }
 
 // UserWorkOrderList 用户工单列表
-func (s *StoreEntry) UserWorkOrderList(ctx context.Context, userId int64) error {
+func (s *StoreEntry) UserWorkOrderList(ctx context.Context, workOrderStatus pasturePb.WorkOrderStatus_Kind, pagination *pasturePb.PaginationModel) (*pasturePb.UserWorkOrderResponse, error) {
+	currUser, err := s.GetCurrentSystemUser(ctx)
+	if err != nil {
+		return nil, xerr.WithStack(err)
+	}
 
-	// todo: 待完善
-	return nil
+	var count int64 = 0
+	userWorkOrderList := make([]*model.WorkOrderSub, 0)
+	//  SELECT * FROM `work_order_sub` WHERE is_show = 1 AND status = 2 And (exec_user_id = 2  OR FIND_IN_SET(2, set_user_ids) > 0) ORDER BY id desc LIMIT 20
+	pref := s.DB.Model(userWorkOrderList).
+		Where("is_show = ?", pasturePb.IsShow_Ok)
+
+	if workOrderStatus != pasturePb.WorkOrderStatus_Invalid {
+		pref.Where("status = ?", workOrderStatus)
+	}
+
+	if err := pref.Where(s.DB.Where("exec_user_id = ?", currUser.Id).Or("FIND_IN_SET(?, set_user_ids) > 0", currUser.Id)).
+		Order("id desc").
+		Count(&count).
+		Limit(int(pagination.PageSize)).
+		Offset(int(pagination.PageOffset)).
+		Find(&userWorkOrderList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	mMap := make(map[int64]*model.WorkOrderMaster)
+	workOderMasterList := make([]*model.WorkOrderMaster, 0)
+	if err := s.DB.Model(&model.WorkOrderMaster{}).Find(&workOderMasterList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+
+	for _, workOrderMaster := range workOderMasterList {
+		mMap[workOrderMaster.Id] = workOrderMaster
+	}
+
+	priorityMap := s.WorkOrderPriorityMap()
+
+	return &pasturePb.UserWorkOrderResponse{
+		Code:    http.StatusOK,
+		Message: "ok",
+		Data: &pasturePb.UserWorkOrderData{
+			List:     model.WorkOrderSubSlice(userWorkOrderList).ToPB(mMap, priorityMap),
+			Total:    0,
+			PageSize: pagination.PageSize,
+			Page:     pagination.Page,
+		},
+	}, nil
 }

+ 57 - 0
module/consumer/consumer.go

@@ -0,0 +1,57 @@
+package consumer
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"kpt-pasture/model"
+	"kpt-pasture/util"
+
+	"gorm.io/gorm"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"gitee.com/xuyiping_admin/pkg/xerr"
+	"go.uber.org/zap"
+
+	"github.com/hibiken/asynq"
+)
+
+// DayWorkOrder 每天工单
+func (entry *Entry) DayWorkOrder(ctx context.Context, t *asynq.Task) error {
+	var req model.TaskWorkOrderPayload
+	if err := json.Unmarshal(t.Payload(), &req); err != nil {
+		zaplog.Error("consumer", zap.Any("mark", "DayWorkOrder"), zap.Any("unmarshal", err))
+		return xerr.WithStack(err)
+	}
+
+	workOrder := &model.WorkOrderMaster{}
+	if err := entry.DB.Where("id = ?", req.WorkOrderId).First(workOrder).Error; err != nil {
+		zaplog.Error("consumer", zap.Any("mark", "DayWorkOrder"), zap.Any("WorkOrderMaster", err))
+		return xerr.WithStack(err)
+	}
+
+	localExecTime, _ := util.ConvertParseLocalUnix(workOrder.ExecTime)
+	workOrderSubList := make([]*model.WorkOrderSub, 0)
+	if err := entry.DB.Where("work_order_master_id = ?", req.WorkOrderId).
+		Where("exec_time = ?", localExecTime).
+		Where("is_show = ?", pasturePb.IsShow_Ok).
+		Find(&workOrderSubList).Error; err != nil {
+		if !errors.Is(err, gorm.ErrRecordNotFound) {
+			zaplog.Error("consumer", zap.Any("mark", "DayWorkOrder"), zap.Any("WorkOrderSub", err))
+			return xerr.WithStack(err)
+		}
+	}
+
+	if len(workOrderSubList) > 0 {
+		return nil
+	}
+
+	workOrderSubList = model.NewWorkOrderSub(workOrder)
+	if err := entry.DB.Create(workOrderSubList).Error; err != nil {
+		zaplog.Error("consumer", zap.Any("mark", "DayWorkOrder"), zap.Any("NewWorkOrderSub", err))
+	}
+
+	return nil
+}

+ 1 - 1
module/job/interface.go → module/consumer/interface.go

@@ -1,4 +1,4 @@
-package job
+package consumer
 
 import (
 	"context"

+ 51 - 1
module/crontab/crontab.go

@@ -1,5 +1,55 @@
 package crontab
 
-func (Entry *Entry) Run() error {
+import (
+	"context"
+	"fmt"
+	"kpt-pasture/model"
+	"kpt-pasture/util"
+	"time"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+func (e *Entry) demo() error {
+	fmt.Println("demo crontab ok")
+	return nil
+}
+
+// UpdateCowDayAge 更新牛的日龄
+func (e *Entry) UpdateCowDayAge() error {
+	cowList := make([]*model.Cow, 0)
+	if err := e.DB.Where("is_remove = ?", pasturePb.IsShow_Ok).Find(&cowList).Error; err != nil {
+		return err
+	}
+	for _, cow := range cowList {
+		dayAge := cow.GetDayAge()
+		if err := e.DB.Where("id = ?", cow.Id).Update("day_age", dayAge).Error; err != nil {
+			zaplog.Error("Crontab", zap.Any("UpdateCowDayAge", err))
+		}
+	}
+	return nil
+}
+
+// GenerateWorkOrder 生成工作单
+func (e *Entry) GenerateWorkOrder() error {
+	workOrderList := make([]*model.WorkOrderMaster, 0)
+	if err := e.DB.Where("is_show = ?", pasturePb.IsShow_Ok).Find(&workOrderList).Error; err != nil {
+		return err
+	}
+	for _, workOrder := range workOrderList {
+		timeUnix, err := util.ConvertParseLocalUnix(workOrder.ExecTime)
+		if timeUnix <= 0 || err != nil {
+			zaplog.Error("crontab", zap.Any("GenerateWorkOrder", err), zap.Any("execTime", workOrder.ExecTime))
+			continue
+		}
+		execTime := time.Now().Unix() - timeUnix
+		if _, err := e.AsynqClient.CtxEnqueue(context.Background(), model.NewTaskWorkOrderPayload(workOrder.Id, time.Duration(execTime)*time.Second)); err != nil {
+			zaplog.Error("PushMessage CtxEnqueue", zap.Any("Err", err))
+		}
+
+	}
 	return nil
 }

+ 3 - 1
module/crontab/interface.go

@@ -27,5 +27,7 @@ func NewCrontab(entry Entry) Crontab {
 }
 
 type Crontab interface {
-	Run() error
+	demo() error
+	UpdateCowDayAge() error
+	GenerateWorkOrder() error
 }

+ 0 - 39
module/job/job.go

@@ -1,39 +0,0 @@
-package job
-
-import (
-	"context"
-
-	"github.com/hibiken/asynq"
-)
-
-// DayWorkOrder 每天工单
-func (entry *Entry) DayWorkOrder(ctx context.Context, t *asynq.Task) error {
-	/*var req model.TaskWorkOrderPayload
-	if err := json.Unmarshal(t.Payload(), &req); err != nil {
-		zaplog.Error("DayWorkOrder", zap.Any("payload", err))
-		return xerr.WithStack(err)
-	}
-
-	workOrder := &model.WorkOrderMaster{}
-	if err := entry.DB.Where("id = ?", req.WorkOrderId).First(workOrder).Error; err != nil {
-		zaplog.Error("DayWorkOrder", zap.Any("workOrder", err))
-		return xerr.WithStack(err)
-	}
-
-	startTime, _ := util.ConvertParseLocalUnix(workOrder.ExecTime)
-	workOrderUser := make([]*model.WorkOrderUser, 0)
-	if err := entry.DB.Where("work_order_id = ?", req.WorkOrderId).
-		Where("start_time = ?", startTime).
-		Where("user_id IN ?", strings.Split(workOrder.ExecPersons, ",")).
-		Find(workOrderUser).Error; err != nil {
-		zaplog.Error("DayWorkOrder", zap.Any("workOrderUser", err))
-		return xerr.WithStack(err)
-	}
-
-	// 工单已经存在
-	if len(workOrderUser) > 0 {
-		return nil
-	}*/
-
-	return nil
-}