Browse Source

work_order: 工单维护

Yi 6 months ago
parent
commit
be6d5ae250
13 changed files with 505 additions and 228 deletions
  1. 16 5
      cmd/crontab.go
  2. 1 1
      cmd/job.go
  3. 1 0
      dep/di_crontab.go
  4. 1 1
      go.mod
  5. 4 0
      go.sum
  6. 0 192
      model/work_order.go
  7. 175 0
      model/work_order_master.go
  8. 115 0
      model/work_order_sub.go
  9. 51 0
      model/work_order_user.go
  10. 16 0
      module/backend/sql.go
  11. 91 23
      module/backend/work.go
  12. 27 6
      module/job/job.go
  13. 7 0
      util/util.go

+ 16 - 5
cmd/crontab.go

@@ -1,13 +1,12 @@
-/*
-Copyright © 2024 NAME HERE <EMAIL ADDRESS>
-*/
 package cmd
 
 import (
+	"context"
 	"fmt"
+	"kpt-pasture/config"
+	"kpt-pasture/store/kptstore"
 
 	"gitee.com/xuyiping_admin/pkg/cmd"
-
 	"github.com/spf13/cobra"
 )
 
@@ -22,5 +21,17 @@ var CrontabCmd = &cobra.Command{
 
 func init() {
 	// 每日同步脚本
-	cmd.Instant(JobCmd, "db:migrate", taskDBMigrate, "数据库迁移-执行完立刻退出")
+	cmd.Instant(JobCmd, "update:cowAge", UpdateCowDayAge, "更新牛只日龄")
+}
+
+func UpdateCowDayAge(ctx context.Context, args []string) error {
+	cfg := config.Options()
+	db := kptstore.MustMigrateStore(cfg)
+	sqlDB, err := db.DB()
+	if err != nil {
+		panic(err)
+	}
+	defer sqlDB.Close()
+
+	return nil
 }

+ 1 - 1
cmd/job.go

@@ -42,7 +42,7 @@ func taskDBMigrate(ctx context.Context, args []string) error {
 	zaplog.Info("boot migrate")
 	db := kptstore.MustMigrateStore(cfg)
 
-	if err := migrator.AutoMigrateFiles(db, "../migration"); err != nil {
+	if err := migrator.AutoMigrateFiles(db, "../migrator"); err != nil {
 		zaplog.Error("taskDBMigrate", zap.Any("err", err))
 		panic(xerr.WithStack(err))
 	}

+ 1 - 0
dep/di_crontab.go

@@ -0,0 +1 @@
+package dep

+ 1 - 1
go.mod

@@ -3,7 +3,7 @@ module kpt-pasture
 go 1.17
 
 require (
-	gitee.com/xuyiping_admin/go_proto v0.0.0-20240828092937-15bc6159f952
+	gitee.com/xuyiping_admin/go_proto v0.0.0-20240830073743-52b13a51bc4d
 	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

@@ -40,6 +40,10 @@ gitee.com/xuyiping_admin/go_proto v0.0.0-20240827035612-bee09217eaa1 h1:8J2NBwEM
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240827035612-bee09217eaa1/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240828092937-15bc6159f952 h1:OFehpdF2GliEB40foszA6VfKob5TE+YL2dqJefUl3MU=
 gitee.com/xuyiping_admin/go_proto v0.0.0-20240828092937-15bc6159f952/go.mod h1:BKrFW6YLDectlQcQk3FYKBeXvjEiodAKJ5rq7O/QiPE=
+gitee.com/xuyiping_admin/go_proto v0.0.0-20240829061040-237e8a327a2a h1:ppuGlLKWQp4GyH52Zslazul1oMRG61t1q3xD0yQKbwA=
+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/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=

+ 0 - 192
model/work_order.go

@@ -1,192 +0,0 @@
-package model
-
-import (
-	"encoding/json"
-	"kpt-pasture/http/util"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/hibiken/asynq"
-
-	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
-)
-
-const (
-	QueueWorkOrder = "workOrder"
-	TaskWorkOrder  = "event:workOrder"
-)
-
-func AsynqQueueWorkOrder() asynq.Option {
-	return asynq.Queue(QueueWorkOrder)
-}
-
-type WorkOrder struct {
-	Id                    int64                                 `json:"id"`
-	Name                  string                                `json:"name"`
-	CategoryId            pasturePb.WorkOrderCategory_Kind      `json:"categoryId"`
-	CategoryName          string                                `json:"categoryName"`
-	Priority              pasturePb.Priority_Kind               `json:"priority"`
-	Frequency             pasturePb.WorkOrderFrequency_Kind     `json:"frequency"`
-	ExecTime              string                                `json:"execTime"`
-	SubscribeUnit         pasturePb.WorkOrderSubscribeUnit_Kind `json:"subscribeUnit"`
-	ExecPersons           string                                `json:"execPersons"`
-	ExecPersonNames       string                                `json:"execPersonsNames"`
-	ExecDepartmental      string                                `json:"execDepartmental"`
-	ExecDepartmentalNames string                                `json:"execDepartmentalNames"`
-	WeekMonthValue        string                                `json:"WeekMonthValue"`
-	IsShow                pasturePb.IsShow_Kind                 `json:"isShow"`
-	Photos                string                                `json:"photos"`
-	Remarks               string                                `json:"remarks"`
-	OperationId           int64                                 `json:"operationId"`
-	OperationName         string                                `json:"operationName"`
-	CreatedAt             int64                                 `json:"createdAt"`
-	UpdatedAt             int64                                 `json:"updatedAt"`
-}
-
-func (w *WorkOrder) TableName() string {
-	return "work_order"
-}
-func NewWorkOrder(
-	req *pasturePb.WorkOrderList,
-	curUser *SystemUser,
-	systemUserList []*SystemUser,
-	deptList []*SystemDept,
-	workOrderCategoryMap map[pasturePb.WorkOrderCategory_Kind]string,
-) *WorkOrder {
-	execPersonNames := make([]string, 0)
-	execDeptNames := make([]string, 0)
-	if len(req.ExecPersons) > 0 {
-		for _, v := range req.ExecPersons {
-			for _, vv := range systemUserList {
-				if int32(vv.Id) != v {
-					continue
-				}
-				execPersonNames = append(execPersonNames, vv.Name)
-			}
-		}
-	}
-
-	for _, d := range deptList {
-		for _, dd := range req.ExecDepartmental {
-			if int32(d.Id) != dd {
-				continue
-			}
-			execDeptNames = append(execDeptNames, d.Name)
-		}
-	}
-	weekMonthValue := ""
-	if req.Frequency == pasturePb.WorkOrderFrequency_Weekly || req.Frequency == pasturePb.WorkOrderFrequency_Monthly {
-		weekMonthValue = util.Int32SliceToString(req.WeekMonthValue, ",")
-	}
-
-	return &WorkOrder{
-		Name:                  req.Name,
-		CategoryId:            req.CategoryId,
-		CategoryName:          workOrderCategoryMap[req.CategoryId],
-		Priority:              req.Priority,
-		ExecTime:              req.ExecTime,
-		SubscribeUnit:         req.SubscribeUnit,
-		ExecPersons:           util.Int32SliceToString(req.ExecPersons, ","),
-		ExecPersonNames:       strings.Join(execPersonNames, ","),
-		ExecDepartmental:      util.Int32SliceToString(req.ExecDepartmental, ","),
-		ExecDepartmentalNames: strings.Join(execDeptNames, ","),
-		WeekMonthValue:        weekMonthValue,
-		IsShow:                req.IsShow,
-		Photos:                strings.Join(req.Photos, ","),
-		Frequency:             req.Frequency,
-		Remarks:               req.Remarks,
-		OperationId:           curUser.Id,
-		OperationName:         curUser.Name,
-	}
-}
-
-type TaskWorkOrderPayload struct {
-	WorkOrderId int64 `json:"workOrderId"`
-}
-
-func NewTaskWorkOrderOption(processAt int64) []asynq.Option {
-	return []asynq.Option{
-		asynq.MaxRetry(3),
-		AsynqQueueWorkOrder(),
-		asynq.ProcessAt(time.Unix(processAt, 64)),
-	}
-}
-
-func NewTaskWorkOrderPayload(id int64, execTime time.Duration) *asynq.Task {
-	payload, _ := json.Marshal(TaskWorkOrderPayload{
-		WorkOrderId: id,
-	})
-	processAt := time.Now().Add(execTime).Unix()
-	return asynq.NewTask(TaskWorkOrder, payload, NewTaskWorkOrderOption(processAt)...)
-}
-
-type WorkOrderSlice []*WorkOrder
-
-func (w WorkOrderSlice) ToPB(
-	priorityMap map[pasturePb.Priority_Kind]string,
-	frequencyMap map[pasturePb.WorkOrderFrequency_Kind]string,
-	subscribeUnitMap map[pasturePb.WorkOrderSubscribeUnit_Kind]string,
-	weekMap map[pasturePb.Week_Kind]string,
-	monthMap map[int32]string,
-) []*pasturePb.WorkOrderList {
-	res := make([]*pasturePb.WorkOrderList, len(w))
-	for i, v := range w {
-		execPersons, execDepartmental, weekMonthValue := make([]int32, 0), make([]int32, 0), make([]int32, 0)
-		weekMonthValueName := make([]string, 0)
-		if len(v.ExecPersons) > 0 {
-			for _, personId := range strings.Split(v.ExecPersons, ",") {
-				p, _ := strconv.Atoi(personId)
-				execPersons = append(execPersons, int32(p))
-			}
-		}
-
-		if len(v.ExecDepartmental) > 0 {
-			for _, deptId := range strings.Split(v.ExecDepartmental, ",") {
-				d, _ := strconv.Atoi(deptId)
-				execDepartmental = append(execDepartmental, int32(d))
-			}
-		}
-
-		if len(v.WeekMonthValue) > 0 {
-			for _, week := range strings.Split(v.WeekMonthValue, ",") {
-				k, _ := strconv.Atoi(week)
-				if v.Frequency == pasturePb.WorkOrderFrequency_Weekly {
-					weekMonthValueName = append(weekMonthValueName, weekMap[pasturePb.Week_Kind(k)])
-				}
-				if v.Frequency == pasturePb.WorkOrderFrequency_Monthly {
-					weekMonthValueName = append(weekMonthValueName, monthMap[int32(k)])
-				}
-				weekMonthValue = append(weekMonthValue, int32(k))
-			}
-		}
-
-		res[i] = &pasturePb.WorkOrderList{
-			Id:                    int32(v.Id),
-			Name:                  v.Name,
-			CategoryId:            v.CategoryId,
-			CategoryName:          v.CategoryName,
-			Priority:              v.Priority,
-			PriorityName:          priorityMap[v.Priority],
-			Frequency:             v.Frequency,
-			FrequencyName:         frequencyMap[v.Frequency],
-			ExecTime:              v.ExecTime,
-			SubscribeUnit:         v.SubscribeUnit,
-			SubscribeUnitName:     subscribeUnitMap[v.SubscribeUnit],
-			ExecPersons:           execPersons,
-			ExecPersonsNames:      strings.Split(v.ExecPersonNames, ","),
-			ExecDepartmental:      execDepartmental,
-			ExecDepartmentalNames: strings.Split(v.ExecDepartmental, ","),
-			IsShow:                v.IsShow,
-			Photos:                strings.Split(v.Photos, ","),
-			WeekMonthValue:        weekMonthValue,
-			WeekMonthValueName:    weekMonthValueName,
-			Remarks:               v.Remarks,
-			OperatorId:            int32(v.OperationId),
-			OperatorName:          v.OperationName,
-			CreatedAt:             int32(v.CreatedAt),
-			UpdatedAt:             int32(v.UpdatedAt),
-		}
-	}
-	return res
-}

+ 175 - 0
model/work_order_master.go

@@ -0,0 +1,175 @@
+package model
+
+import (
+	"encoding/json"
+	"kpt-pasture/http/util"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/hibiken/asynq"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+const (
+	QueueWorkOrder = "workOrder"
+	TaskWorkOrder  = "event:workOrder"
+)
+
+func AsynqQueueWorkOrder() asynq.Option {
+	return asynq.Queue(QueueWorkOrder)
+}
+
+type WorkOrderMaster struct {
+	Id              int64                                 `json:"id"`
+	Name            string                                `json:"name"`
+	CategoryId      pasturePb.WorkOrderCategory_Kind      `json:"categoryId"`
+	CategoryName    string                                `json:"categoryName"`
+	Priority        pasturePb.Priority_Kind               `json:"priority"`
+	Frequency       pasturePb.WorkOrderFrequency_Kind     `json:"frequency"`
+	ExecTime        string                                `json:"execTime"`
+	SubscribeUnit   pasturePb.WorkOrderSubscribeUnit_Kind `json:"subscribeUnit"`
+	ExecPersons     string                                `json:"execPersons"`
+	ExecPersonNames string                                `json:"execPersonsNames"`
+	WeekMonthValue  string                                `json:"WeekMonthValue"`
+	IsShow          pasturePb.IsShow_Kind                 `json:"isShow"`
+	Photos          string                                `json:"photos"`
+	Remarks         string                                `json:"remarks"`
+	OperationId     int64                                 `json:"operationId"`
+	OperationName   string                                `json:"operationName"`
+	CreatedAt       int64                                 `json:"createdAt"`
+	UpdatedAt       int64                                 `json:"updatedAt"`
+}
+
+func (w *WorkOrderMaster) TableName() string {
+	return "work_order_master"
+}
+func NewWorkOrderMaster(
+	req *pasturePb.WorkOrderList,
+	curUser *SystemUser,
+	systemUserList []*SystemUser,
+	workOrderCategoryMap map[pasturePb.WorkOrderCategory_Kind]string,
+) *WorkOrderMaster {
+	execPersonNames := make([]string, 0)
+	if len(req.ExecPersons) > 0 {
+		for _, vv := range systemUserList {
+			for _, v := range req.ExecPersons {
+				if int32(vv.Id) != v {
+					continue
+				}
+				execPersonNames = append(execPersonNames, vv.Name)
+			}
+
+			for _, depId := range req.ExecDepartmental {
+				if int64(depId) != vv.DeptId {
+					continue
+				}
+				execPersonNames = append(execPersonNames, vv.Name)
+			}
+		}
+	}
+	weekMonthValue := ""
+	if req.Frequency == pasturePb.WorkOrderFrequency_Weekly || req.Frequency == pasturePb.WorkOrderFrequency_Monthly {
+		weekMonthValue = util.Int32SliceToString(req.WeekMonthValue, ",")
+	}
+
+	return &WorkOrderMaster{
+		Name:            req.Name,
+		CategoryId:      req.CategoryId,
+		CategoryName:    workOrderCategoryMap[req.CategoryId],
+		Priority:        req.Priority,
+		ExecTime:        req.ExecTime,
+		SubscribeUnit:   req.SubscribeUnit,
+		ExecPersons:     util.Int32SliceToString(req.ExecPersons, ","),
+		ExecPersonNames: strings.Join(execPersonNames, ","),
+		WeekMonthValue:  weekMonthValue,
+		IsShow:          req.IsShow,
+		Photos:          strings.Join(req.Photos, ","),
+		Frequency:       req.Frequency,
+		Remarks:         req.Remarks,
+		OperationId:     curUser.Id,
+		OperationName:   curUser.Name,
+	}
+}
+
+type TaskWorkOrderPayload struct {
+	WorkOrderId int64 `json:"workOrderId"`
+}
+
+func NewTaskWorkOrderOption(processAt int64) []asynq.Option {
+	return []asynq.Option{
+		asynq.MaxRetry(3),
+		AsynqQueueWorkOrder(),
+		asynq.ProcessAt(time.Unix(processAt, 64)),
+	}
+}
+
+func NewTaskWorkOrderPayload(id int64, execTime time.Duration) *asynq.Task {
+	payload, _ := json.Marshal(TaskWorkOrderPayload{
+		WorkOrderId: id,
+	})
+	processAt := time.Now().Add(execTime).Unix()
+	return asynq.NewTask(TaskWorkOrder, payload, NewTaskWorkOrderOption(processAt)...)
+}
+
+type WorkOrderMasterSlice []*WorkOrderMaster
+
+func (w WorkOrderMasterSlice) ToPB(
+	priorityMap map[pasturePb.Priority_Kind]string,
+	frequencyMap map[pasturePb.WorkOrderFrequency_Kind]string,
+	subscribeUnitMap map[pasturePb.WorkOrderSubscribeUnit_Kind]string,
+	weekMap map[pasturePb.Week_Kind]string,
+	monthMap map[int32]string,
+) []*pasturePb.WorkOrderList {
+	res := make([]*pasturePb.WorkOrderList, len(w))
+	for i, v := range w {
+		execPersons, weekMonthValue := make([]int32, 0), make([]int32, 0)
+		weekMonthValueName := make([]string, 0)
+		if len(v.ExecPersons) > 0 {
+			for _, personId := range strings.Split(v.ExecPersons, ",") {
+				p, _ := strconv.Atoi(personId)
+				execPersons = append(execPersons, int32(p))
+			}
+		}
+
+		if len(v.WeekMonthValue) > 0 {
+			for _, week := range strings.Split(v.WeekMonthValue, ",") {
+				k, _ := strconv.Atoi(week)
+				if v.Frequency == pasturePb.WorkOrderFrequency_Weekly {
+					weekMonthValueName = append(weekMonthValueName, weekMap[pasturePb.Week_Kind(k)])
+				}
+				if v.Frequency == pasturePb.WorkOrderFrequency_Monthly {
+					weekMonthValueName = append(weekMonthValueName, monthMap[int32(k)])
+				}
+				weekMonthValue = append(weekMonthValue, int32(k))
+			}
+		}
+
+		res[i] = &pasturePb.WorkOrderList{
+			Id:                 int32(v.Id),
+			Name:               v.Name,
+			CategoryId:         v.CategoryId,
+			CategoryName:       v.CategoryName,
+			Priority:           v.Priority,
+			PriorityName:       priorityMap[v.Priority],
+			Frequency:          v.Frequency,
+			FrequencyName:      frequencyMap[v.Frequency],
+			ExecTime:           v.ExecTime,
+			SubscribeUnit:      v.SubscribeUnit,
+			SubscribeUnitName:  subscribeUnitMap[v.SubscribeUnit],
+			ExecPersons:        execPersons,
+			ExecPersonsNames:   strings.Split(v.ExecPersonNames, ","),
+			IsShow:             v.IsShow,
+			Photos:             strings.Split(v.Photos, ","),
+			WeekMonthValue:     weekMonthValue,
+			WeekMonthValueName: weekMonthValueName,
+			Remarks:            v.Remarks,
+			OperatorId:         int32(v.OperationId),
+			OperatorName:       v.OperationName,
+			CreatedAt:          int32(v.CreatedAt),
+			UpdatedAt:          int32(v.UpdatedAt),
+		}
+	}
+	return res
+}

+ 115 - 0
model/work_order_sub.go

@@ -0,0 +1,115 @@
+package model
+
+import (
+	"fmt"
+	"kpt-pasture/util"
+	"strings"
+	"time"
+
+	"gitee.com/xuyiping_admin/pkg/logger/zaplog"
+	"go.uber.org/zap"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+// 执行时间超过当前创单时间5秒的工单,不生效
+const (
+	expireTimeSec = 5
+	secondsInDay  = 86400
+)
+
+type WorkOrderSub struct {
+	Id                 int64                          `json:"id"`
+	WorkOrderSubNumber string                         `json:"workOrderSubNumber"`
+	WorkOrderMasterId  int64                          `json:"workOrderMasterId"`
+	ExecTime           int64                          `json:"execTime"`
+	Status             pasturePb.WorkOrderStatus_Kind `json:"status"`
+	ExecUserId         int64                          `json:"execUserId"`
+	IsShow             pasturePb.IsShow_Kind          `json:"isShow"`
+	FinishTime         int64                          `json:"finishTime"`
+	Remarks            string                         `json:"remarks"`
+	CreatedAt          int64                          `json:"createdAt"`
+	UpdatedAt          int64                          `json:"updatedAt"`
+}
+
+func (w *WorkOrderSub) TableName() string {
+	return "work_order_sub"
+}
+
+func NewWorkOrderSub(req *WorkOrderMaster) []*WorkOrderSub {
+	workOrderSubList := make([]*WorkOrderSub, 0)
+	if req.IsShow == pasturePb.IsShow_No {
+		return workOrderSubList
+	}
+
+	nowTime := time.Now()
+	execTime, err := util.ConvertParseLocalUnix(req.ExecTime)
+	if err != nil {
+		zaplog.Error("NewWorkOrderSub", zap.Any("ConvertParseLocalUnix", err))
+		return workOrderSubList
+	}
+
+	isToday := execTime <= nowTime.Unix()+expireTimeSec
+	// 一次性工单就生成当天的工单记录
+	if req.Frequency == pasturePb.WorkOrderFrequency_None && isToday {
+		return workOrderSubList
+	}
+
+	switch req.Frequency {
+	case pasturePb.WorkOrderFrequency_None:
+		currentExecTime := execTime
+		workOrderSubList = append(workOrderSubList, &WorkOrderSub{
+			WorkOrderMasterId:  req.Id,
+			ExecTime:           currentExecTime,
+			ExecUserId:         0,
+			WorkOrderSubNumber: fmt.Sprintf("s%s%d", LayoutDate, time.Now().Unix()),
+			Status:             pasturePb.WorkOrderStatus_Created,
+			IsShow:             pasturePb.IsShow_Ok,
+			Remarks:            "",
+		})
+
+	case pasturePb.WorkOrderFrequency_Daily, pasturePb.WorkOrderFrequency_Weekly, pasturePb.WorkOrderFrequency_Monthly:
+		// 本月剩余天数
+		remainingDays := util.GetMonthRemainDay()
+		for i := 0; i < remainingDays; i++ {
+			if isToday && i == 0 {
+				continue
+			}
+			currentExecTime := execTime + int64(i)*secondsInDay
+			t := time.Unix(currentExecTime, 0)
+			if shouldGenerateSubOrder(req.Frequency, t, req.WeekMonthValue) {
+				sub := &WorkOrderSub{
+					WorkOrderMasterId:  req.Id,
+					ExecTime:           currentExecTime,
+					ExecUserId:         0,
+					WorkOrderSubNumber: fmt.Sprintf("s%s%d", LayoutDate, execTime),
+					Status:             pasturePb.WorkOrderStatus_Created,
+					IsShow:             pasturePb.IsShow_Ok,
+					Remarks:            "",
+				}
+				workOrderSubList = append(workOrderSubList, sub)
+			}
+		}
+	}
+
+	return workOrderSubList
+}
+
+func shouldGenerateSubOrder(frequency pasturePb.WorkOrderFrequency_Kind, t time.Time, weekMonth string) bool {
+	switch frequency {
+	case pasturePb.WorkOrderFrequency_Daily:
+		return true
+	case pasturePb.WorkOrderFrequency_Weekly, pasturePb.WorkOrderFrequency_Monthly:
+		weekMonthDays := strings.Split(weekMonth, ",")
+		dayOfWeekOrMonth := int64(t.Weekday())
+		if frequency == pasturePb.WorkOrderFrequency_Monthly {
+			dayOfWeekOrMonth = int64(t.Day())
+		}
+		for _, day := range weekMonthDays {
+			if day == fmt.Sprintf("%d", dayOfWeekOrMonth) {
+				return true
+			}
+		}
+	}
+	return false
+}

+ 51 - 0
model/work_order_user.go

@@ -0,0 +1,51 @@
+package model
+
+import (
+	"kpt-pasture/util"
+	"strconv"
+	"strings"
+
+	pasturePb "gitee.com/xuyiping_admin/go_proto/proto/go/backend/cow"
+)
+
+type WorkOrderUser struct {
+	Id                int64                          `json:"id"`
+	WorkOrderMasterId int64                          `json:"WorkOrderMasterId"`
+	WorkOrderSubId    int64                          `json:"workOrderSubId"`
+	UserId            int64                          `json:"userId"`
+	StartTime         int64                          `json:"startTime"`
+	ExecTime          int64                          `json:"execTime"`
+	EndTime           int64                          `json:"endTime"`
+	Status            pasturePb.WorkOrderStatus_Kind `json:"status"`
+	IsShow            pasturePb.IsShow_Kind          `json:"isShow"`
+	Remarks           string                         `json:"remarks"`
+	CreatedAt         int64                          `json:"createdAt"`
+	UpdatedAt         int64                          `json:"updatedAt"`
+}
+
+func (w *WorkOrderUser) TableName() string {
+	return "work_order_user"
+}
+
+func NewWorkOrderUser(master *WorkOrderMaster, sub *WorkOrderSub) []*WorkOrderUser {
+	res := make([]*WorkOrderUser, 0)
+	userList := strings.Split(master.ExecPersons, ",")
+	if len(userList) <= 0 {
+		return res
+	}
+	startTime, _ := util.ConvertParseLocalUnix(master.ExecTime)
+	for _, userIdStr := range userList {
+		userId, _ := strconv.ParseInt(userIdStr, 10, 64)
+		res = append(res, &WorkOrderUser{
+			WorkOrderMasterId: master.Id,
+			WorkOrderSubId:    sub.Id,
+			UserId:            userId,
+			StartTime:         startTime,
+			Status:            pasturePb.WorkOrderStatus_Distribute,
+			IsShow:            pasturePb.IsShow_Ok,
+			Remarks:           "",
+		})
+	}
+
+	return res
+}

+ 16 - 0
module/backend/sql.go

@@ -85,6 +85,14 @@ func (s *StoreEntry) GetPenList(ctx context.Context) ([]*model.Pen, error) {
 	return penList, nil
 }
 
+func (s *StoreEntry) GetCowList(ctx context.Context) ([]*model.Cow, error) {
+	cowList := make([]*model.Cow, 0)
+	if err := s.DB.Where("is_remove = ?", pasturePb.IsShow_Ok).Find(&cowList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return cowList, nil
+}
+
 func (s *StoreEntry) GetCowInfo(ctx context.Context, cowId int64) (*model.Cow, error) {
 	cowData := &model.Cow{Id: cowId}
 	if err := s.DB.First(cowData).Error; err != nil {
@@ -157,3 +165,11 @@ func (s *StoreEntry) ImmunizationById(ctx context.Context, id int64) (*model.Imm
 	}
 	return immunizationPlan, nil
 }
+
+func (s *StoreEntry) GetWorkOrderSubByWorkOrderId(ctx context.Context, workOrderId int64) ([]*model.WorkOrderSub, error) {
+	workOrderSubList := make([]*model.WorkOrderSub, 0)
+	if err := s.DB.Where("work_order_id = ?", workOrderId).Find(&workOrderSubList).Error; err != nil {
+		return nil, xerr.WithStack(err)
+	}
+	return workOrderSubList, nil
+}

+ 91 - 23
module/backend/work.go

@@ -18,10 +18,10 @@ import (
 )
 
 func (s *StoreEntry) OrderList(ctx context.Context, req *pasturePb.SearchWorkOrderRequest, pagination *pasturePb.PaginationModel) (*pasturePb.SearchWorkOrderResponse, error) {
-	workOrderList := make([]*model.WorkOrder, 0)
+	workOrderList := make([]*model.WorkOrderMaster, 0)
 	var count int64 = 0
 
-	pref := s.DB.Model(new(model.WorkOrder))
+	pref := s.DB.Model(new(model.WorkOrderMaster))
 	if req.Name != "" {
 		pref.Where("name like ?", fmt.Sprintf("%s%s%s", "%", req.Name, "%"))
 	}
@@ -60,7 +60,7 @@ func (s *StoreEntry) OrderList(ctx context.Context, req *pasturePb.SearchWorkOrd
 		Code:    http.StatusOK,
 		Message: "ok",
 		Data: &pasturePb.SearchWorkOrderData{
-			List:     model.WorkOrderSlice(workOrderList).ToPB(priorityMap, frequencyMap, subscribeUnitMap, weekMap, monthMap),
+			List:     model.WorkOrderMasterSlice(workOrderList).ToPB(priorityMap, frequencyMap, subscribeUnitMap, weekMap, monthMap),
 			Total:    int32(count),
 			PageSize: pagination.PageSize,
 			Page:     pagination.Page,
@@ -68,56 +68,117 @@ func (s *StoreEntry) OrderList(ctx context.Context, req *pasturePb.SearchWorkOrd
 	}, nil
 }
 
+// OrderCreateOrUpdate 创建和更新工单
+// 周期性工单【包括每天,每周,每月】一次性生成符合本月日期所有工单
+// 更新工单的时候需要重新维护子工单
 func (s *StoreEntry) OrderCreateOrUpdate(ctx context.Context, req *pasturePb.WorkOrderList) error {
 	currentUser, _ := s.GetCurrentSystemUser(ctx)
 	workOrderCategoryMap := s.WorkOrderCategoryMap()
 	systemUserList, _ := s.SystemUserList(ctx)
-	deptList, _ := s.SystemDeptList(ctx)
-	newWorkOrder := model.NewWorkOrder(req, currentUser, systemUserList, deptList, workOrderCategoryMap)
+	newWorkOrderMaster := model.NewWorkOrderMaster(req, currentUser, systemUserList, workOrderCategoryMap)
 	if req.Id <= 0 {
-		if err := s.DB.Create(newWorkOrder).Error; err != nil {
+		if err := s.DB.Transaction(func(tx *gorm.DB) error {
+			if err := tx.Create(newWorkOrderMaster).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+			newWorkOrderSubList := model.NewWorkOrderSub(newWorkOrderMaster)
+			if len(newWorkOrderSubList) > 0 {
+				if err := tx.Create(&newWorkOrderSubList).Error; err != nil {
+					return xerr.WithStack(err)
+				}
+			}
+			return nil
+		}); err != nil {
 			return xerr.WithStack(err)
 		}
-
-		// 发送异步数据
-		defer func() {
-			s.SendAsynqWorkOrder(ctx, newWorkOrder)
-		}()
 	} else {
-		if err := s.DB.Where("id = ?", req.Id).Updates(newWorkOrder).Error; err != nil {
+		if err := s.DB.Transaction(func(tx *gorm.DB) error {
+			if err := tx.Model(newWorkOrderMaster).Where("id = ?", req.Id).Updates(newWorkOrderMaster).Error; err != nil {
+				return xerr.WithStack(err)
+			}
+
+			if err := s.WorkOrderSubUpdate(tx, newWorkOrderMaster); err != nil {
+				return xerr.WithStack(err)
+			}
+
+			return nil
+		}); err != nil {
 			return xerr.WithStack(err)
 		}
+
 	}
 	return nil
 }
 
+// OrderIsShow 工单的开启和关闭
 func (s *StoreEntry) OrderIsShow(ctx context.Context, id int64) error {
-	workOrder := &model.WorkOrder{Id: id}
-	if err := s.DB.First(workOrder).Error; err != nil {
+	workOrderMaster := &model.WorkOrderMaster{Id: id}
+	if err := s.DB.First(workOrderMaster).Error; err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
 			return xerr.Custom("该工单不存在")
 		}
 		return xerr.WithStack(err)
 	}
 
-	// 开启发送消息任务,需要判断今天工单任务是否已经生成过了
-	defer func() {
-		s.SendAsynqWorkOrder(ctx, workOrder)
-	}()
-
 	isShow := pasturePb.IsShow_No
-	if workOrder.IsShow == pasturePb.IsShow_No {
+	if workOrderMaster.IsShow == pasturePb.IsShow_No {
 		isShow = pasturePb.IsShow_Ok
 	}
 
-	if err := s.DB.Model(workOrder).Update("is_show", isShow).Error; err != nil {
+	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)
+		}
+		return nil
+	}); err != nil {
 		return xerr.WithStack(err)
 	}
-
 	return nil
 }
 
-func (s *StoreEntry) SendAsynqWorkOrder(ctx context.Context, workOrder *model.WorkOrder) {
+// WorkOrderSubUpdate 子工单重新维护
+// 如果工单是开启状态,则重新生成子工单,如果工单是关闭状态,则取消子工单
+func (s *StoreEntry) WorkOrderSubUpdate(tx *gorm.DB, workOrderMaster *model.WorkOrderMaster) error {
+	if workOrderMaster.IsShow == pasturePb.IsShow_Ok {
+		newWorkOrderSubList := model.NewWorkOrderSub(workOrderMaster)
+		if err := tx.Create(&newWorkOrderSubList).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+	} else {
+		// 获取当天的零点时间
+		zeroTime := util.TimeParseLocalUnix(time.Now().Format("2006-01-02"))
+		// 先检查是否存在符合条件的记录
+		var count int64
+		if err := tx.Model(&model.WorkOrderSub{}).
+			Where("work_order_master_id = ?", workOrderMaster.Id).
+			Where("is_show = ?", pasturePb.IsShow_Ok).
+			Where("exec_time > ?", zeroTime).
+			Where("status = ?", pasturePb.WorkOrderStatus_Distribute).
+			Count(&count).Error; err != nil {
+			return xerr.WithStack(err)
+		}
+
+		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 {
+			return xerr.WithStack(err)
+		}
+	}
+	return nil
+}
+func (s *StoreEntry) SendAsynqWorkOrder(ctx context.Context, workOrder *model.WorkOrderMaster) {
 	if workOrder.IsShow == pasturePb.IsShow_No {
 		return
 	}
@@ -160,3 +221,10 @@ func (s *StoreEntry) SendAsynqWorkOrder(ctx context.Context, workOrder *model.Wo
 		zaplog.Error("PushMessage CtxEnqueue", zap.Any("Err", err))
 	}
 }
+
+// UserWorkOrderList 用户工单列表
+func (s *StoreEntry) UserWorkOrderList(ctx context.Context, userId int64) error {
+
+	// todo: 待完善
+	return nil
+}

+ 27 - 6
module/job/job.go

@@ -2,17 +2,38 @@ package job
 
 import (
 	"context"
-	"fmt"
 
 	"github.com/hibiken/asynq"
 )
 
-const (
-	DIMCacheKey = "indicators:dim"
-)
-
 // DayWorkOrder 每天工单
 func (entry *Entry) DayWorkOrder(ctx context.Context, t *asynq.Task) error {
-	fmt.Println("=========asynq=========", string(t.Payload()))
+	/*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
 }

+ 7 - 0
util/util.go

@@ -38,3 +38,10 @@ func ConvertParseLocalUnix(timeParse string) (int64, error) {
 	}
 	return theTime.Unix(), nil
 }
+
+// GetMonthRemainDay 获取当前月还剩几天
+func GetMonthRemainDay() int {
+	now := time.Now()
+	lastDayOfMonth := time.Date(now.Year(), now.Month()+1, 0, 23, 59, 59, 999999999, now.Location())
+	return int(lastDayOfMonth.Sub(now).Hours()/24) + 1
+}