You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
392 lines
9.3 KiB
392 lines
9.3 KiB
package model |
|
|
|
import ( |
|
"encoding/json" |
|
"fmt" |
|
"strconv" |
|
"time" |
|
|
|
"go-common/app/admin/main/aegis/model/common" |
|
modtask "go-common/app/admin/main/aegis/model/task" |
|
"go-common/library/log" |
|
xtime "go-common/library/time" |
|
) |
|
|
|
//hash fields |
|
const ( |
|
Dispatch = "ds" |
|
Delay = "dy" |
|
Submit = "st_%d_%d" // 参数1:提交状态(任务提交,资源提交,任务关闭) 参数2:提交前属于谁 |
|
Release = "rl" |
|
RscState = "rs_%d" |
|
UseTime = "ut" |
|
SetKey = "report_set" |
|
|
|
In = "in" |
|
Out = "out" |
|
|
|
//type |
|
TypeMeta = int8(0) |
|
TypeTotal = int8(1) |
|
TypeCreate = int8(2) //每日任务增量 |
|
TypeDaySubmit = int8(3) //每日任务提交信息 |
|
TypeDayUserSubmit = int8(4) //每日审核人员提交信息 |
|
) |
|
|
|
//TaskReport 任务报表 |
|
type TaskReport struct { |
|
ID int64 `json:"id"` |
|
BusinessID int64 `json:"business_id"` |
|
FlowID int64 `json:"flow_id"` |
|
Type int8 `json:"type"` |
|
UID int64 `json:"uid" gorm:"column:uid"` |
|
StatDate time.Time `json:"stat_date"` |
|
Content string `json:"content"` |
|
} |
|
|
|
func (t *TaskReport) TableName() string { |
|
return "task_report" |
|
} |
|
|
|
//DayPager 按天分页 |
|
type DayPager struct { |
|
Pn string `json:"pn"` |
|
IsFirst bool `json:"is_first"` //第一页 |
|
IsLast bool `json:"is_last"` //最后一页 |
|
} |
|
|
|
//ReportSubmitRes . |
|
type ReportSubmitRes struct { |
|
Order []string `json:"order"` //表格列顺序 |
|
Header map[string]string `json:"header"` //表格头 |
|
Rows [][]map[string]string `json:"rows"` //表格内容体 |
|
} |
|
|
|
//ReportSubmitItem . |
|
type ReportSubmitItem struct { |
|
StatDate string `json:"stat_date"` |
|
CreateCnt int64 `json:"create_cnt"` |
|
UID int64 `json:"uid"` |
|
Username string `json:"username"` |
|
AvgDur string `json:"avg_dur"` |
|
AvgUtime string `json:"avg_utime"` |
|
FlowID int64 `json:"flow_id"` |
|
SubmitCnt int64 `json:"submit_cnt"` |
|
StateCnt map[string]*ReportSubmitState `json:"state_cnt"` |
|
} |
|
|
|
//ReportSubmitState . |
|
type ReportSubmitState struct { |
|
Cnt int64 `json:"cnt"` |
|
Ratio string `json:"ratio"` |
|
} |
|
|
|
type ReportSubmitContent struct { |
|
RscStates map[string]int64 `json:"rsc_states"` |
|
AvgDur int64 `json:"avg_dur"` |
|
AvgUtime int64 `json:"avg_utime"` |
|
TotalDur int64 `json:"omitempty,total_dur"` |
|
TotalUtime int64 `json:"omitempty,total_utime"` |
|
} |
|
|
|
//OptReport 参数 |
|
type OptReport struct { |
|
BizID int64 `form:"business_id" validate:"required"` |
|
FlowID int64 `form:"flow_id"` |
|
UName string `form:"username"` |
|
Bt string `form:"bt"` |
|
Btime time.Time |
|
Et string `form:"et"` |
|
Etime time.Time |
|
Type int8 `form:"type"` |
|
} |
|
|
|
//OptReportSubmit 参数 |
|
type OptReportSubmit struct { |
|
BizID int64 `form:"business_id" validate:"required"` |
|
FlowID int64 `form:"flow_id"` |
|
Bt string `form:"bt"` |
|
Et string `form:"et"` |
|
Pn string `form:"pn"` |
|
} |
|
|
|
//ReportFlowItem 流量 |
|
type ReportFlowItem struct { |
|
Hour string `json:"hour"` |
|
In int64 `json:"in"` |
|
Out int64 `json:"out"` |
|
} |
|
|
|
//MetaItem . |
|
type MetaItem struct { |
|
DS int64 // 领取 |
|
RL int64 // 释放 |
|
RP int64 // 通过 |
|
ST int64 // 提交 |
|
UT int64 // 时长 |
|
} |
|
|
|
//ReportMeta . |
|
type ReportMeta struct { |
|
Mtime xtime.Time |
|
Content string |
|
UID int64 |
|
Type int8 |
|
Uname string |
|
} |
|
|
|
//Genform 元数据的加工函数,进出审核表格 |
|
func Genform(res map[string][]*ReportFlowItem) (form map[string][24]*ReportFlowItem) { |
|
form = make(map[string][24]*ReportFlowItem) |
|
|
|
date := func(hour string) (d string, h string) { |
|
d, h = hour[:10], hour[11:] |
|
return |
|
} |
|
|
|
emptyflow := func() (res [24]*ReportFlowItem) { |
|
res = [24]*ReportFlowItem{} |
|
for i := 0; i < 24; i++ { |
|
res[i] = &ReportFlowItem{Hour: fmt.Sprintf("%.2d", i)} |
|
} |
|
return res |
|
} |
|
|
|
mapcheck := make(map[string]int64) //完全没数据的就不展示了 |
|
for uname, arr := range res { |
|
for _, item := range arr { |
|
d, h := date(item.Hour) |
|
hi, _ := strconv.Atoi(h) |
|
key := fmt.Sprintf("%s %s", uname, d) |
|
if arr, ok := form[key]; ok { |
|
arr[hi].In = item.In |
|
arr[hi].Out = item.Out |
|
form[key] = arr |
|
} else { |
|
data := emptyflow() |
|
data[hi].In = item.In |
|
data[hi].Out = item.Out |
|
form[key] = data |
|
} |
|
mapcheck[key] += (item.In + item.Out) |
|
} |
|
} |
|
|
|
for k, v := range mapcheck { |
|
if v == 0 { |
|
delete(form, k) |
|
} |
|
} |
|
|
|
return |
|
} |
|
|
|
//Gentempres 元数据的加工函数,进出审核柱状图1 |
|
func Gentempres(opt *OptReport, mnames map[int64]string, metas []*ReportMeta) (tempres map[string]map[int64]*ReportFlowItem) { |
|
var funcin, funcout caculate |
|
if opt.Type == TypeMeta { |
|
funcin = metaIn |
|
funcout = metaOut |
|
} else { |
|
funcin = totalIn |
|
funcout = totalOut |
|
} |
|
|
|
tempres = make(map[string]map[int64]*ReportFlowItem) |
|
for _, meta := range metas { |
|
metaM := make(map[string]int64) |
|
if err := json.Unmarshal([]byte(meta.Content), &metaM); err != nil { |
|
log.Error("json.Unmarshal (%s) error(%v)", meta.Content, err) |
|
continue |
|
} |
|
|
|
var in, out int64 |
|
in += funcin(metaM, meta) |
|
out += funcout(metaM, meta) |
|
hour := meta.Mtime.Time().Format("2006-01-02 15") |
|
|
|
item := &ReportFlowItem{ |
|
Hour: hour, |
|
In: in, |
|
Out: out, |
|
} |
|
if val, ok := tempres[hour]; ok { |
|
if v, ok := val[meta.UID]; ok { |
|
v.In += in |
|
v.Out += out |
|
tempres[hour][meta.UID] = v |
|
} else { |
|
tempres[hour][meta.UID] = item |
|
} |
|
} else { |
|
tempres[hour] = map[int64]*ReportFlowItem{ |
|
meta.UID: item, |
|
} |
|
} |
|
} |
|
return |
|
} |
|
|
|
type caculate func(map[string]int64, *ReportMeta) int64 |
|
|
|
func totalIn(metaM map[string]int64, meta *ReportMeta) (in int64) { |
|
return metaM[In] |
|
} |
|
func totalOut(metaM map[string]int64, meta *ReportMeta) (in int64) { |
|
return metaM[Out] |
|
} |
|
|
|
func metaIn(metaM map[string]int64, meta *ReportMeta) (in int64) { |
|
if ds, ok := metaM[Dispatch]; ok { //领取到的 |
|
in += ds |
|
} |
|
if rl, ok := metaM[Release]; ok { //释放的 |
|
in -= rl |
|
} |
|
return |
|
} |
|
|
|
func metaOut(metaM map[string]int64, meta *ReportMeta) (out int64) { |
|
if st, ok := metaM[fmt.Sprintf(Submit, modtask.TaskStateSubmit, meta.UID)]; ok { //本人提交的 |
|
out = st |
|
} |
|
return |
|
} |
|
|
|
//Genres 元数据的加工函数,进出审核柱状图2 |
|
func Genres(opt *OptReport, tempres map[string]map[int64]*ReportFlowItem, mnames map[int64]string) (res map[string][]*ReportFlowItem) { |
|
res = make(map[string][]*ReportFlowItem) |
|
for i := opt.Btime; i.Format("2006-01-02 15") != opt.Etime.Format("2006-01-02 15"); i = i.Add(+1 * time.Hour) { |
|
hour := i.Format("2006-01-02 15") |
|
rt := &ReportFlowItem{ |
|
Hour: hour, |
|
} |
|
if val, ok := tempres[hour]; ok { |
|
var tin, tout int64 |
|
if opt.Type == TypeMeta { |
|
for uid, uname := range mnames { |
|
if item, ok := val[uid]; ok { |
|
rt = item |
|
tin += item.In |
|
tout += item.Out |
|
} else { |
|
rt.In = 0 |
|
rt.Out = 0 |
|
} |
|
addtores(res, uname, *rt) |
|
} |
|
} else { |
|
if item, ok := val[0]; ok { |
|
rt = item |
|
tin += item.In |
|
tout += item.Out |
|
} |
|
} |
|
|
|
rt.In = tin |
|
rt.Out = tout |
|
addtores(res, "total", *rt) |
|
} else { |
|
addtores(res, "total", *rt) |
|
for _, uname := range mnames { |
|
addtores(res, uname, *rt) |
|
} |
|
} |
|
} |
|
|
|
for k, v := range res { |
|
if opt.UName != "" && k != opt.UName { |
|
delete(res, k) |
|
continue |
|
} |
|
var ( |
|
insum, outsum = int64(0), int64(0) |
|
) |
|
for _, item := range v { |
|
insum += item.In |
|
outsum += item.Out |
|
} |
|
if insum == 0 && outsum == 0 { |
|
delete(res, k) |
|
} |
|
} |
|
return |
|
} |
|
|
|
func addtores(res map[string][]*ReportFlowItem, key string, val ReportFlowItem) { |
|
if total, ok := res[key]; ok { |
|
res[key] = append(total, &val) |
|
} else { |
|
res[key] = []*ReportFlowItem{&val} |
|
} |
|
} |
|
|
|
//GenMemberStat 个人24小时统计报表 |
|
func GenMemberStat(metas []*ReportMeta, passState int64) (res map[int64][]interface{}, err error) { |
|
tempres := make(map[int64]*MetaItem) |
|
for _, meta := range metas { |
|
metaM := make(map[string]int64) |
|
if err = json.Unmarshal([]byte(meta.Content), &metaM); err != nil { |
|
log.Error("json.Unmarshal (%s) error(%v)", meta.Content, err) |
|
err = nil |
|
continue |
|
} |
|
|
|
tmt := &MetaItem{ |
|
DS: metaM[Dispatch], |
|
RL: metaM[Release], |
|
RP: metaM[fmt.Sprintf(RscState, passState)], |
|
ST: metaM[fmt.Sprintf(Submit, modtask.TaskStateSubmit, meta.UID)], |
|
UT: metaM[UseTime], |
|
} |
|
|
|
if val, ok := tempres[meta.UID]; ok { |
|
val.DS += tmt.DS |
|
val.RL += tmt.RL |
|
val.RP += tmt.RP |
|
val.ST += tmt.ST |
|
val.UT += tmt.UT |
|
} else { |
|
tempres[meta.UID] = tmt |
|
} |
|
} |
|
|
|
for uid, v := range tempres { |
|
fmt.Printf("uid:%d v:%+v\n", uid, v) |
|
} |
|
res = make(map[int64][]interface{}) |
|
for uid, meta := range tempres { |
|
var ( |
|
completerate, passrate, avgtime = "", "", "" |
|
) |
|
total := meta.DS - meta.RL |
|
if total <= 0 { |
|
total = 0 |
|
completerate, passrate, avgtime = "0%", "0%", "00:00:00" |
|
} else { |
|
cRate := float32(meta.ST) / float32(total) |
|
pRate := float32(meta.RP) / float32(total) |
|
at := int64(0) |
|
if meta.ST > 0 { |
|
at = meta.UT / meta.ST |
|
} |
|
if cRate < 0.0 || cRate > 1.0 { |
|
completerate = "100%" |
|
} else { |
|
completerate = fmt.Sprintf("%.2f%%", cRate*100) |
|
} |
|
if pRate < 0.0 || pRate > 1.0 { |
|
passrate = "100%" |
|
} else { |
|
passrate = fmt.Sprintf("%.2f%%", pRate*100) |
|
} |
|
if at < 0 { |
|
avgtime = "00:00:00" |
|
} else { |
|
avgtime = common.ParseWaitTime(at) |
|
} |
|
} |
|
res[uid] = []interface{}{total, completerate, passrate, avgtime} |
|
} |
|
return |
|
}
|
|
|