Merge pull request #145 from lanyulei/dev

feat: 重写工单统计。
This commit is contained in:
lanyulei 2021-03-14 21:57:55 +08:00 committed by GitHub
commit 2e99e52385
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 262 additions and 137 deletions

View File

@ -98,7 +98,7 @@ QQ群1127401830
[兰玉磊的技术博客](https://www.fdevops.com/) [兰玉磊的技术博客](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) [vue-element-admin # 不错的前端模版框架](https://github.com/PanJiaChen/vue-element-admin)

View File

@ -1,13 +1,11 @@
package dashboard package dashboard
import ( import (
"ferry/global/orm"
"ferry/models/process"
"ferry/models/system"
"ferry/pkg/pagination"
"ferry/pkg/service" "ferry/pkg/service"
"ferry/tools/app" "ferry/tools/app"
"fmt" "fmt"
"strings"
"time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -19,82 +17,74 @@ import (
func InitData(c *gin.Context) { func InitData(c *gin.Context) {
var ( var (
err error err error
panelGroup struct { count map[string]int // 工单数量统计
UserTotalCount int `json:"user_total_count"` ranks []service.Ranks
WorkOrderTotalCount int `json:"work_order_total_count"` submit map[string][]interface{}
UpcomingTotalCount int `json:"upcoming_total_count"` startTime string
MyUpcomingCount int `json:"my_upcoming_count"` endTime string
} handle interface{}
result interface{} period interface{}
processOrderList []process.Info
processOrderListMap map[string][]interface{}
) )
// 查询用户总数 startTime = c.DefaultQuery("start_time", "")
err = orm.Eloquent.Model(&system.SysUser{}).Count(&panelGroup.UserTotalCount).Error 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 { if err != nil {
app.Error(c, -1, err, "") app.Error(c, -1, err, "查询工单类型数据统计失败")
return return
} }
// 查询工单总数 // 查询工单据排名
err = orm.Eloquent.Model(&process.WorkOrderInfo{}).Count(&panelGroup.WorkOrderTotalCount).Error ranks, err = statistics.WorkOrderRanks()
if err != nil { if err != nil {
app.Error(c, -1, err, "") app.Error(c, -1, err, "查询提交工单排名数据失败")
return return
} }
// 查询待办总数 // 工单提交数据统计
err = orm.Eloquent.Model(&process.WorkOrderInfo{}). submit, err = statistics.DateRangeStatistics()
Where("is_end = 0").
Count(&panelGroup.UpcomingTotalCount).Error
if err != nil { if err != nil {
app.Error(c, -1, err, "") app.Error(c, -1, err, "工单提交数据统计失败")
return return
} }
// 查询我的待办 // 处理工单人员排行榜
w := service.WorkOrder{ handle, err = statistics.HandlePersonRank()
Classify: 1,
GinObj: c,
}
result, err = w.PureWorkOrderList()
if err != nil { if err != nil {
app.Error(c, -1, err, "") 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()))
return return
} }
// 查询工单提交排名 // 工单处理耗时排行榜
submitRankingData, err := service.SubmitRanking() period, err = statistics.HandlePeriodRank()
if err != nil { if err != nil {
app.Error(c, -1, err, fmt.Sprintf("查询工单提交排名失败,%v", err.Error())) app.Error(c, -1, err, "查询工单处理耗时排行失败")
return 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{}{ app.OK(c, map[string]interface{}{
"panelGroup": panelGroup, "count": count,
"statisticsData": statisticsData, "ranks": ranks,
"submitRankingData": submitRankingData, "submit": submit,
"processOrderList": processOrderListMap, "handle": handle,
"period": period,
}, "") }, "")
} }

View File

@ -215,6 +215,7 @@ func UnityWorkOrder(c *gin.Context) {
Processor: userInfo.NickName, Processor: userInfo.NickName,
ProcessorId: tools.GetUserId(c), ProcessorId: tools.GetUserId(c),
Remarks: "手动结束工单。", Remarks: "手动结束工单。",
Status: 2,
}) })
tx.Commit() tx.Commit()
@ -225,6 +226,7 @@ func UnityWorkOrder(c *gin.Context) {
// 转交工单 // 转交工单
func InversionWorkOrder(c *gin.Context) { func InversionWorkOrder(c *gin.Context) {
var ( var (
cirHistoryValue []process.CirculationHistory
err error err error
workOrderInfo process.WorkOrderInfo workOrderInfo process.WorkOrderInfo
stateList []map[string]interface{} stateList []map[string]interface{}
@ -232,6 +234,7 @@ func InversionWorkOrder(c *gin.Context) {
currentState map[string]interface{} currentState map[string]interface{}
userInfo system.SysUser userInfo system.SysUser
currentUserInfo system.SysUser currentUserInfo system.SysUser
costDurationValue int64
params struct { params struct {
WorkOrderId int `json:"work_order_id"` WorkOrderId int `json:"work_order_id"`
NodeId string `json:"node_id"` NodeId string `json:"node_id"`
@ -306,6 +309,22 @@ func InversionWorkOrder(c *gin.Context) {
return 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{ tx.Create(&process.CirculationHistory{
Title: workOrderInfo.Title, Title: workOrderInfo.Title,
@ -315,6 +334,8 @@ func InversionWorkOrder(c *gin.Context) {
Processor: currentUserInfo.NickName, Processor: currentUserInfo.NickName,
ProcessorId: tools.GetUserId(c), ProcessorId: tools.GetUserId(c),
Remarks: fmt.Sprintf("此阶段负责人已转交给《%v》", userInfo.NickName), Remarks: fmt.Sprintf("此阶段负责人已转交给《%v》", userInfo.NickName),
Status: 2, // 其他
CostDuration: costDurationValue,
}) })
tx.Commit() tx.Commit()

View File

@ -17,9 +17,10 @@ type CirculationHistory struct {
Source string `gorm:"column:source; type: varchar(128)" json:"source" form:"source"` // 源节点ID 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 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 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"` // 处理人 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 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"` // 处理时长 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"` // 备注 Remarks string `gorm:"column:remarks; type: longtext" json:"remarks" form:"remarks"` // 备注
} }

View File

@ -272,6 +272,7 @@ func CreateWorkOrder(c *gin.Context) (err error) {
Circulation: "新建", Circulation: "新建",
Processor: nameValue, Processor: nameValue,
ProcessorId: userInfo.UserId, ProcessorId: userInfo.UserId,
Status: 2, // 其他
}).Error }).Error
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()

View File

@ -3,14 +3,38 @@ package service
import ( import (
"database/sql" "database/sql"
"ferry/global/orm" "ferry/global/orm"
"ferry/models/process"
"ferry/pkg/pagination"
"fmt"
"strings"
"time"
"github.com/gin-gonic/gin"
) )
/* /*
@Author : lanyulei @Author : lanyulei
*/ */
// 查询周统计数据 type Ranks struct {
func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) { 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 ( var (
datetime string datetime string
total int total int
@ -18,29 +42,34 @@ func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) {
processing int processing int
sqlValue string sqlValue string
rows *sql.Rows 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, a.click_date,
ifnull( b.total, 0 ) AS total, ifnull( b.total, 0 ) AS total,
ifnull( b.overs, 0 ) AS overs, ifnull( b.overs, 0 ) AS overs,
ifnull( b.processing, 0 ) AS processing ifnull( b.processing, 0 ) AS processing
FROM FROM
( (%s) a
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
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
a1.datetime AS datetime, a1.datetime AS datetime,
@ -76,7 +105,7 @@ func WeeklyStatistics() (statisticsData map[string][]interface{}, err error) {
is_end = 0 is_end = 0
GROUP BY GROUP BY
date( create_time )) c ON a1.datetime = c.datetime 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() rows, err = orm.Eloquent.Raw(sqlValue).Rows()
if err != nil { if err != nil {
return 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 ( var (
userId int userId int
username string username string
@ -140,3 +169,93 @@ func SubmitRanking() (submitRankingData map[string][]interface{}, err error) {
return 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
}

View File

@ -53,15 +53,6 @@ type Handle struct {
tx *gorm.DB 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) { func (h *Handle) Countersign(c *gin.Context) (err error) {
var ( var (
@ -423,7 +414,7 @@ func (h *Handle) HandleWorkOrder(
relatedPersonList []int relatedPersonList []int
cirHistoryValue []process.CirculationHistory cirHistoryValue []process.CirculationHistory
cirHistoryData process.CirculationHistory cirHistoryData process.CirculationHistory
costDurationValue string costDurationValue int64
sourceEdges []map[string]interface{} sourceEdges []map[string]interface{}
targetEdges []map[string]interface{} targetEdges []map[string]interface{}
condExprStatus bool condExprStatus bool
@ -534,13 +525,13 @@ func (h *Handle) HandleWorkOrder(
"id": h.targetStateValue["id"].(string), "id": h.targetStateValue["id"].(string),
} }
switch h.targetStateValue["clazz"] {
// 排他网关
case "exclusiveGateway":
sourceEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "source") sourceEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "source")
if err != nil { if err != nil {
return return
} }
switch h.targetStateValue["clazz"] {
case "exclusiveGateway": // 排他网关
breakTag: breakTag:
for _, edge := range sourceEdges { for _, edge := range sourceEdges {
edgeCondExpr := make([]map[string]interface{}, 0) edgeCondExpr := make([]map[string]interface{}, 0)
@ -588,15 +579,8 @@ func (h *Handle) HandleWorkOrder(
err = errors.New("所有流转均不符合条件,请确认。") err = errors.New("所有流转均不符合条件,请确认。")
return 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") targetEdges, err = h.processState.GetEdge(h.targetStateValue["id"].(string), "target")
if err != nil { if err != nil {
err = fmt.Errorf("查询流转信息失败,%v", err.Error()) err = fmt.Errorf("查询流转信息失败,%v", err.Error())
@ -777,7 +761,7 @@ func (h *Handle) HandleWorkOrder(
for _, t := range cirHistoryValue { for _, t := range cirHistoryValue {
if t.Source != h.stateValue["id"] { if t.Source != h.stateValue["id"] {
costDuration := time.Since(t.CreatedAt.Time) 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, Circulation: circulationValue,
Processor: currentUserInfo.NickName, Processor: currentUserInfo.NickName,
ProcessorId: tools.GetUserId(c), ProcessorId: tools.GetUserId(c),
Status: flowProperties,
CostDuration: costDurationValue, CostDuration: costDurationValue,
Remarks: remarks, Remarks: remarks,
} }
@ -843,6 +828,7 @@ func (h *Handle) HandleWorkOrder(
ProcessorId: tools.GetUserId(c), ProcessorId: tools.GetUserId(c),
Circulation: "结束", Circulation: "结束",
Remarks: "工单已结束", Remarks: "工单已结束",
Status: 2, // 其他状态
}).Error }).Error
if err != nil { if err != nil {
h.tx.Rollback() h.tx.Rollback()

View File

@ -29,6 +29,13 @@ type workOrderInfo struct {
DataClassify int `json:"data_classify"` 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) { func (w *WorkOrder) PureWorkOrderList() (result interface{}, err error) {
var ( var (
workOrderInfoList []workOrderInfo workOrderInfoList []workOrderInfo