diff --git a/README.md b/README.md index 1b5f3a8..861625b 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ QQ群:1127401830 [兰玉磊的技术博客](https://www.fdevops.com/) ## 特别感谢 -[go-amdin # 不错的后台开发框架](https://github.com/wenjianzhang/go-admin.git) +[go-amdin # 不错的后台开发框架](https://github.com/go-admin-team/go-admin) [vue-element-admin # 不错的前端模版框架](https://github.com/PanJiaChen/vue-element-admin) diff --git a/apis/dashboard/dashboard.go b/apis/dashboard/dashboard.go index 4c7805c..aa2ae91 100644 --- a/apis/dashboard/dashboard.go +++ b/apis/dashboard/dashboard.go @@ -1,13 +1,11 @@ package dashboard import ( - "ferry/global/orm" - "ferry/models/process" - "ferry/models/system" - "ferry/pkg/pagination" "ferry/pkg/service" "ferry/tools/app" "fmt" + "strings" + "time" "github.com/gin-gonic/gin" ) @@ -18,83 +16,75 @@ import ( func InitData(c *gin.Context) { var ( - err error - panelGroup struct { - UserTotalCount int `json:"user_total_count"` - WorkOrderTotalCount int `json:"work_order_total_count"` - UpcomingTotalCount int `json:"upcoming_total_count"` - MyUpcomingCount int `json:"my_upcoming_count"` - } - result interface{} - processOrderList []process.Info - processOrderListMap map[string][]interface{} + err error + count map[string]int // 工单数量统计 + ranks []service.Ranks + submit map[string][]interface{} + startTime string + endTime string + handle interface{} + period interface{} ) - // 查询用户总数 - err = orm.Eloquent.Model(&system.SysUser{}).Count(&panelGroup.UserTotalCount).Error + startTime = c.DefaultQuery("start_time", "") + endTime = c.DefaultQuery("end_time", "") + + if startTime == "" || endTime == "" { + // 默认为最近7天的数据 + startTime = fmt.Sprintf("%s 00:00:00", time.Now().AddDate(0, 0, -6).Format("2006-01-02")) + endTime = fmt.Sprintf("%s 23:59:59", time.Now().Format("2006-01-02")) + } else { + if strings.Contains(startTime, "T") && strings.Contains(endTime, "T") { + startTime = fmt.Sprintf("%s 00:00:00", strings.Split(startTime, "T")[0]) + endTime = fmt.Sprintf("%s 23:59:59", strings.Split(endTime, "T")[0]) + } + } + + statistics := service.Statistics{ + StartTime: startTime, + EndTime: endTime, + } + + // 查询工单类型数据统计 + count, err = statistics.WorkOrderCount(c) if err != nil { - app.Error(c, -1, err, "") + app.Error(c, -1, err, "查询工单类型数据统计失败") return } - // 查询工单总数 - err = orm.Eloquent.Model(&process.WorkOrderInfo{}).Count(&panelGroup.WorkOrderTotalCount).Error + // 查询工单数据排名 + ranks, err = statistics.WorkOrderRanks() if err != nil { - app.Error(c, -1, err, "") + app.Error(c, -1, err, "查询提交工单排名数据失败") return } - // 查询待办总数 - err = orm.Eloquent.Model(&process.WorkOrderInfo{}). - Where("is_end = 0"). - Count(&panelGroup.UpcomingTotalCount).Error + // 工单提交数据统计 + submit, err = statistics.DateRangeStatistics() if err != nil { - app.Error(c, -1, err, "") + app.Error(c, -1, err, "工单提交数据统计失败") return } - // 查询我的待办 - w := service.WorkOrder{ - Classify: 1, - GinObj: c, - } - result, err = w.PureWorkOrderList() + // 处理工单人员排行榜 + handle, err = statistics.HandlePersonRank() if err != nil { - app.Error(c, -1, err, "") - return - } - panelGroup.MyUpcomingCount = result.(*pagination.Paginator).TotalCount - - // 查询周工单统计 - statisticsData, err := service.WeeklyStatistics() - if err != nil { - app.Error(c, -1, err, fmt.Sprintf("查询周工单统计失败,%v", err.Error())) + app.Error(c, -1, err, "查询处理工单人员排行失败") return } - // 查询工单提交排名 - submitRankingData, err := service.SubmitRanking() + // 工单处理耗时排行榜 + period, err = statistics.HandlePeriodRank() if err != nil { - app.Error(c, -1, err, fmt.Sprintf("查询工单提交排名失败,%v", err.Error())) + app.Error(c, -1, err, "查询工单处理耗时排行失败") return } - // 查询最常用的流程 - err = orm.Eloquent.Model(&process.Info{}).Order("submit_count desc").Limit(10).Find(&processOrderList).Error - if err != nil { - app.Error(c, -1, err, fmt.Sprintf("查询最常用的流程失败,%v", err.Error())) - return - } - processOrderListMap = make(map[string][]interface{}) - for _, v := range processOrderList { - processOrderListMap["title"] = append(processOrderListMap["title"], v.Name) - processOrderListMap["submit_count"] = append(processOrderListMap["submit_count"], v.SubmitCount) - } - app.OK(c, map[string]interface{}{ - "panelGroup": panelGroup, - "statisticsData": statisticsData, - "submitRankingData": submitRankingData, - "processOrderList": processOrderListMap, + "count": count, + "ranks": ranks, + "submit": submit, + "handle": handle, + "period": period, }, "") } diff --git a/apis/process/workOrder.go b/apis/process/workOrder.go index f705dad..72aea23 100644 --- a/apis/process/workOrder.go +++ b/apis/process/workOrder.go @@ -215,6 +215,7 @@ func UnityWorkOrder(c *gin.Context) { Processor: userInfo.NickName, ProcessorId: tools.GetUserId(c), Remarks: "手动结束工单。", + Status: 2, }) tx.Commit() @@ -225,14 +226,16 @@ func UnityWorkOrder(c *gin.Context) { // 转交工单 func InversionWorkOrder(c *gin.Context) { var ( - err error - workOrderInfo process.WorkOrderInfo - stateList []map[string]interface{} - stateValue []byte - currentState map[string]interface{} - userInfo system.SysUser - currentUserInfo system.SysUser - params struct { + cirHistoryValue []process.CirculationHistory + err error + workOrderInfo process.WorkOrderInfo + stateList []map[string]interface{} + stateValue []byte + currentState map[string]interface{} + userInfo system.SysUser + currentUserInfo system.SysUser + costDurationValue int64 + params struct { WorkOrderId int `json:"work_order_id"` NodeId string `json:"node_id"` UserId int `json:"user_id"` @@ -306,15 +309,33 @@ func InversionWorkOrder(c *gin.Context) { return } + // 流转历史写入 + err = orm.Eloquent.Model(&cirHistoryValue). + Where("work_order = ?", params.WorkOrderId). + Find(&cirHistoryValue). + Order("create_time desc").Error + if err != nil { + tx.Rollback() + return + } + for _, t := range cirHistoryValue { + if t.Source != currentState["id"].(string) { + costDuration := time.Since(t.CreatedAt.Time) + costDurationValue = int64(costDuration) / 1000 / 1000 / 1000 + } + } + // 添加转交历史 tx.Create(&process.CirculationHistory{ - Title: workOrderInfo.Title, - WorkOrder: workOrderInfo.Id, - State: currentState["label"].(string), - Circulation: "转交", - Processor: currentUserInfo.NickName, - ProcessorId: tools.GetUserId(c), - Remarks: fmt.Sprintf("此阶段负责人已转交给《%v》", userInfo.NickName), + Title: workOrderInfo.Title, + WorkOrder: workOrderInfo.Id, + State: currentState["label"].(string), + Circulation: "转交", + Processor: currentUserInfo.NickName, + ProcessorId: tools.GetUserId(c), + Remarks: fmt.Sprintf("此阶段负责人已转交给《%v》", userInfo.NickName), + Status: 2, // 其他 + CostDuration: costDurationValue, }) tx.Commit() diff --git a/models/process/circulationHistory.go b/models/process/circulationHistory.go index 1db24de..67c6d72 100644 --- a/models/process/circulationHistory.go +++ b/models/process/circulationHistory.go @@ -11,16 +11,17 @@ import ( // 工单流转历史 type CirculationHistory struct { base.Model - Title string `gorm:"column:title; type: varchar(128)" json:"title" form:"title"` // 工单标题 - WorkOrder int `gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"` // 工单ID - State string `gorm:"column:state; type: varchar(128)" json:"state" form:"state"` // 工单状态 - Source string `gorm:"column:source; type: varchar(128)" json:"source" form:"source"` // 源节点ID - Target string `gorm:"column:target; type: varchar(128)" json:"target" form:"target"` // 目标节点ID - Circulation string `gorm:"column:circulation; type: varchar(128)" json:"circulation" form:"circulation"` // 流转ID - Processor string `gorm:"column:processor; type: varchar(45)" json:"processor" form:"processor"` // 处理人 - ProcessorId int `gorm:"column:processor_id; type: int(11)" json:"processor_id" form:"processor_id"` // 处理人ID - CostDuration string `gorm:"column:cost_duration; type: varchar(128)" json:"cost_duration" form:"cost_duration"` // 处理时长 - Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注 + Title string `gorm:"column:title; type: varchar(128)" json:"title" form:"title"` // 工单标题 + WorkOrder int `gorm:"column:work_order; type: int(11)" json:"work_order" form:"work_order"` // 工单ID + State string `gorm:"column:state; type: varchar(128)" json:"state" form:"state"` // 工单状态 + Source string `gorm:"column:source; type: varchar(128)" json:"source" form:"source"` // 源节点ID + Target string `gorm:"column:target; type: varchar(128)" json:"target" form:"target"` // 目标节点ID + Circulation string `gorm:"column:circulation; type: varchar(128)" json:"circulation" form:"circulation"` // 流转ID + Status int `gorm:"column:status; type: int(11)" json:"status" form:"status"` // 流转状态 1 同意, 0 拒绝, 2 其他 + Processor string `gorm:"column:processor; type: varchar(45)" json:"processor" form:"processor"` // 处理人 + ProcessorId int `gorm:"column:processor_id; type: int(11)" json:"processor_id" form:"processor_id"` // 处理人ID + CostDuration int64 `gorm:"column:cost_duration; type: int(11)" json:"cost_duration" form:"cost_duration"` // 处理时长 + Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注 } func (CirculationHistory) TableName() string { diff --git a/pkg/service/createWorkOrder.go b/pkg/service/createWorkOrder.go index 45ea353..8c9b5ba 100644 --- a/pkg/service/createWorkOrder.go +++ b/pkg/service/createWorkOrder.go @@ -272,6 +272,7 @@ func CreateWorkOrder(c *gin.Context) (err error) { Circulation: "新建", Processor: nameValue, ProcessorId: userInfo.UserId, + Status: 2, // 其他 }).Error if err != nil { tx.Rollback() diff --git a/pkg/service/dashboard.go b/pkg/service/dashboard.go index 4bbbeda..d63d482 100644 --- a/pkg/service/dashboard.go +++ b/pkg/service/dashboard.go @@ -3,44 +3,73 @@ package service import ( "database/sql" "ferry/global/orm" + "ferry/models/process" + "ferry/pkg/pagination" + "fmt" + "strings" + "time" + + "github.com/gin-gonic/gin" ) /* @Author : lanyulei */ -// 查询周统计数据 -func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) { +type Ranks struct { + Name string `json:"name"` + Total int `json:"total"` +} + +type Statistics struct { + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` +} + +func NewStatistics(startTime string, endTime string) *Statistics { + return &Statistics{ + StartTime: startTime, + EndTime: endTime, + } +} + +// 查询范围统计数据 +func (s *Statistics) DateRangeStatistics() (statisticsData map[string][]interface{}, err error) { var ( - datetime string - total int - overs int - processing int - sqlValue string - rows *sql.Rows + datetime string + total int + overs int + processing int + sqlValue string + rows *sql.Rows + startTime time.Time + endTime time.Time + TimeDifference int + sqlDataValue string ) - sqlValue = `SELECT + + // 计算两个时间的差 + startTime, _ = time.Parse("2006-01-02 15:04:05", s.StartTime) + endTime, _ = time.Parse("2006-01-02 15:04:05", fmt.Sprintf("%s 00:00:00", strings.Split(s.EndTime, " ")[0])) + TimeDifference = int(endTime.Sub(startTime).Hours() / 24) + + for i := 0; i < TimeDifference; i++ { + if i == 0 { + sqlDataValue += "SELECT curdate() AS click_date UNION ALL" + } else if i == TimeDifference-1 { + sqlDataValue += fmt.Sprintf(` SELECT date_sub( curdate(), INTERVAL %d DAY ) AS click_date`, i) + } else { + sqlDataValue += fmt.Sprintf(` SELECT date_sub( curdate(), INTERVAL %d DAY ) AS click_date UNION ALL`, i) + } + } + + sqlValue = fmt.Sprintf(`SELECT a.click_date, ifnull( b.total, 0 ) AS total, ifnull( b.overs, 0 ) AS overs, ifnull( b.processing, 0 ) AS processing FROM - ( - SELECT - curdate() AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 1 DAY ) AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 2 DAY ) AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 3 DAY ) AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 4 DAY ) AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 5 DAY ) AS click_date UNION ALL - SELECT - date_sub( curdate(), INTERVAL 6 DAY ) AS click_date - ) a + (%s) a LEFT JOIN ( SELECT a1.datetime AS datetime, @@ -76,7 +105,7 @@ func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) { is_end = 0 GROUP BY date( create_time )) c ON a1.datetime = c.datetime - ) b ON a.click_date = b.datetime order by a.click_date;` + ) b ON a.click_date = b.datetime order by a.click_date;`, sqlDataValue) rows, err = orm.Eloquent.Raw(sqlValue).Rows() if err != nil { return @@ -99,7 +128,7 @@ func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) { } // 查询工单提交排名 -func SubmitRanking() (submitRankingData map[string][]interface{}, err error) { +func (s *Statistics) SubmitRanking() (submitRankingData map[string][]interface{}, err error) { var ( userId int username string @@ -140,3 +169,93 @@ func SubmitRanking() (submitRankingData map[string][]interface{}, err error) { return } + +// 查询工单数量统计 +func (s *Statistics) WorkOrderCount(c *gin.Context) (countList map[string]int, err error) { + var ( + w *WorkOrder + result interface{} + ) + countList = make(map[string]int) + for _, i := range []int{1, 2, 3, 4} { + w = NewWorkOrder(i, c) + if i != 1 { + result, err = w.PureWorkOrderList() + if err != nil { + return + } + } else { + w = NewWorkOrder(i, c) + result, err = w.WorkOrderList() + if err != nil { + return + } + } + + if i == 1 { + countList["upcoming"] = result.(*pagination.Paginator).TotalCount + } else if i == 2 { + countList["my_create"] = result.(*pagination.Paginator).TotalCount + } else if i == 3 { + countList["related"] = result.(*pagination.Paginator).TotalCount + } else if i == 4 { + countList["all"] = result.(*pagination.Paginator).TotalCount + } + } + + return +} + +// 查询指定范围内的提交工单排名数据 +func (s *Statistics) WorkOrderRanks() (ranks []Ranks, err error) { + err = orm.Eloquent.Model(&process.WorkOrderInfo{}). + Joins("left join p_process_info on p_process_info.id = p_work_order_info.process"). + Select("p_process_info.name as name, count(p_work_order_info.id) as total"). + Where("p_work_order_info.create_time between ? and ?", s.StartTime, s.EndTime). + Group("p_work_order_info.process"). + Order("total desc"). + Limit(10). + Scan(&ranks).Error + return +} + +// 处理工单人员排行榜 +func (s *Statistics) HandlePersonRank() (interface{}, error) { + var ( + err error + ranks []struct { + UserID int `json:"user_id"` + Username string `json:"username"` + Nickname string `json:"nickname"` + Count int `json:"count"` + } + ) + err = orm.Eloquent.Model(&process.CirculationHistory{}). + Joins("left join sys_user on sys_user.user_id = p_work_order_circulation_history.processor_id"). + Where("p_work_order_circulation_history.source like 'receiveTask%' and p_work_order_circulation_history.status = 1 and p_work_order_circulation_history.create_time between ? and ?", s.StartTime, s.EndTime). + Select("p_work_order_circulation_history.processor_id as user_id, p_work_order_circulation_history.processor as nickname, sys_user.username as username, count(p_work_order_circulation_history.id) as count"). + Group("p_work_order_circulation_history.processor, p_work_order_circulation_history.processor_id"). + Scan(&ranks).Error + return ranks, err +} + +// 工单处理耗时排行榜 +func (s *Statistics) HandlePeriodRank() (interface{}, error) { + var ( + err error + ranks []struct { + UserID int `json:"user_id"` + Username string `json:"username"` + Nickname string `json:"nickname"` + CostDuration int `json:"cost_duration"` + } + ) + err = orm.Eloquent.Model(&process.CirculationHistory{}). + Joins("left join sys_user on sys_user.user_id = p_work_order_circulation_history.processor_id"). + Where("p_work_order_circulation_history.source like 'receiveTask%' and p_work_order_circulation_history.status = 1 and p_work_order_circulation_history.create_time between ? and ?", s.StartTime, s.EndTime). + Select("p_work_order_circulation_history.processor_id as user_id, p_work_order_circulation_history.processor as nickname, sys_user.username as username, sum(p_work_order_circulation_history.cost_duration) as cost_duration"). + Group("p_work_order_circulation_history.processor, p_work_order_circulation_history.processor_id"). + Order("cost_duration desc"). + Scan(&ranks).Error + return ranks, err +} diff --git a/pkg/service/handle.go b/pkg/service/handle.go index 3000148..cb2ebd9 100644 --- a/pkg/service/handle.go +++ b/pkg/service/handle.go @@ -53,15 +53,6 @@ type Handle struct { tx *gorm.DB } -// 时间格式化 -func fmtDuration(d time.Duration) string { - d = d.Round(time.Minute) - h := d / time.Hour - d -= h * time.Hour - m := d / time.Minute - return fmt.Sprintf("%02d小时 %02d分钟", h, m) -} - // 会签 func (h *Handle) Countersign(c *gin.Context) (err error) { var ( @@ -423,7 +414,7 @@ func (h *Handle) HandleWorkOrder( relatedPersonList []int cirHistoryValue []process.CirculationHistory cirHistoryData process.CirculationHistory - costDurationValue string + costDurationValue int64 sourceEdges []map[string]interface{} targetEdges []map[string]interface{} condExprStatus bool @@ -534,13 +525,13 @@ func (h *Handle) HandleWorkOrder( "id": h.targetStateValue["id"].(string), } + sourceEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "source") + if err != nil { + return + } + switch h.targetStateValue["clazz"] { - // 排他网关 - case "exclusiveGateway": - sourceEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "source") - if err != nil { - return - } + case "exclusiveGateway": // 排他网关 breakTag: for _, edge := range sourceEdges { edgeCondExpr := make([]map[string]interface{}, 0) @@ -588,15 +579,8 @@ func (h *Handle) HandleWorkOrder( err = errors.New("所有流转均不符合条件,请确认。") return } - // 并行/聚合网关 - case "parallelGateway": + case "parallelGateway": // 并行/聚合网关 // 入口,判断 - sourceEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "source") - if err != nil { - err = fmt.Errorf("查询流转信息失败,%v", err.Error()) - return - } - targetEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "target") if err != nil { err = fmt.Errorf("查询流转信息失败,%v", err.Error()) @@ -777,7 +761,7 @@ func (h *Handle) HandleWorkOrder( for _, t := range cirHistoryValue { if t.Source != h.stateValue["id"] { costDuration := time.Since(t.CreatedAt.Time) - costDurationValue = fmtDuration(costDuration) + costDurationValue = int64(costDuration) / 1000 / 1000 / 1000 } } @@ -799,6 +783,7 @@ func (h *Handle) HandleWorkOrder( Circulation: circulationValue, Processor: currentUserInfo.NickName, ProcessorId: tools.GetUserId(c), + Status: flowProperties, CostDuration: costDurationValue, Remarks: remarks, } @@ -843,6 +828,7 @@ func (h *Handle) HandleWorkOrder( ProcessorId: tools.GetUserId(c), Circulation: "结束", Remarks: "工单已结束", + Status: 2, // 其他状态 }).Error if err != nil { h.tx.Rollback() diff --git a/pkg/service/workOrderList.go b/pkg/service/workOrderList.go index 1e91916..a2017df 100644 --- a/pkg/service/workOrderList.go +++ b/pkg/service/workOrderList.go @@ -29,6 +29,13 @@ type workOrderInfo struct { DataClassify int `json:"data_classify"` } +func NewWorkOrder(classify int, c *gin.Context) *WorkOrder { + return &WorkOrder{ + Classify: classify, + GinObj: c, + } +} + func (w *WorkOrder) PureWorkOrderList() (result interface{}, err error) { var ( workOrderInfoList []workOrderInfo