From 16b34c939d6790fe6ebe61f8f0e85fe80ee52224 Mon Sep 17 00:00:00 2001 From: sunty <1172534965@qq.com> Date: 星期三, 29 五月 2024 14:44:16 +0800 Subject: [PATCH] 修改准备数据流程,适应较大数据量的操作 --- db/repository.go | 53 ++++------ config/config.go | 1 data/prepare.go | 37 +++---- db/elastic.go | 57 ++++++----- db/models.go | 26 ++-- rule/service.go | 90 +++++++++++------- main.go | 2 rule/engine.go | 2 task/engine.go | 2 config/app.yaml | 1 10 files changed, 144 insertions(+), 127 deletions(-) diff --git a/config/app.yaml b/config/app.yaml index ec50fea..25983e7 100644 --- a/config/app.yaml +++ b/config/app.yaml @@ -18,6 +18,7 @@ topHitsSize: 100000 cameraSize: 100000 timeInterval: 30 + batchSize: 100 log: path: /opt/vasystem/valog/ level: -1 diff --git a/config/config.go b/config/config.go index 721adbe..728e51e 100644 --- a/config/config.go +++ b/config/config.go @@ -32,6 +32,7 @@ TopHitsSize int `mapstructure: "topHitsSize"` CameraSize int `mapstructure: "cameraSize"` TimeInterval int `mapstructure: "timeInterval"` + BatchSize int `mapstructure: "batchSize"` } type app struct { diff --git a/data/prepare.go b/data/prepare.go index 8b0f675..1d38dd2 100644 --- a/data/prepare.go +++ b/data/prepare.go @@ -65,11 +65,11 @@ } // SetFrequentAddress 鏂规硶璁$畻鍑虹幇鏈�棰戠箒鐨勫嚭琛屽湴鍧�骞惰缃负甯哥敤鍦板潃 -func SetFrequentAddress(c *db.CaptureInfo) { +func GetFrequentAddress(captureDetail []db.CaptureDetail) string { outAddressCounts := make(map[string]int) inAddressCounts := make(map[string]int) // 缁熻姣忎釜鍑鸿鍦板潃鐨勫嚭鐜版鏁� - for _, detail := range c.CaptureDetail { + for _, detail := range captureDetail { if detail.Direction == "out" { outAddressCounts[detail.CaptureAddress]++ } @@ -96,12 +96,11 @@ } } } - // 灏嗗嚭鐜版鏁版渶澶氱殑鍑鸿鍦板潃璁剧疆涓哄父鐢ㄥ湴鍧� - c.FrequentAddress = frequentAddress + return frequentAddress } // processData 鍑芥暟澶勭悊鏁版嵁锛屾牴鎹姹傝繃婊ゆ帀鏁版嵁骞舵牴鎹鍒欐洿鏂扮姸鎬� -func ProcessData(captureInfos []db.CaptureInfo, personStatus []db.PersonStatus, ruleInfos []db.PersonnelStatusRule, communityID string) []db.PersonStatus { +func ProcessData(captureInfos []db.CaptureInfo, personStatus []*db.PersonStatus, ruleInfos []db.PersonnelStatusRule, communityID string) []db.PersonStatus { filteredInfos := make([]db.PersonStatus, 0) // 鏋勫缓蹇�熸煡鎵剧储寮曪紝鏂逛究鏌ユ壘瀵瑰簲鐨勪汉鍛樼姸鎬佸拰瑙勫垯 @@ -109,35 +108,31 @@ ruleIndex := make(map[string]db.PersonnelStatusRule) for _, person := range personStatus { - statusIndex[person.DocumentNumber] = person + statusIndex[person.DocumentNumber] = *person } for _, rule := range ruleInfos { ruleIndex[rule.Name] = rule } - + //fmt.Println("statusIndex: ", statusIndex) + //fmt.Println("ruleIndex: ", ruleIndex) // 澶勭悊姣忎釜鎶撴媿淇℃伅 for _, info := range captureInfos { //fmt.Println("info", info.DocumentNumber, info.Status, info.FrequentAddress) //fmt.Println("person", statusIndex[info.DocumentNumber].DocumentNumber, statusIndex[info.DocumentNumber].Status, statusIndex[info.DocumentNumber].FrequentAddress) // 妫�鏌ユ槸鍚﹀瓨鍦ㄥ搴旂殑浜哄憳鐘舵�� - person, ok := statusIndex[info.DocumentNumber] - if !ok { - // 涓嶅瓨鍦ㄥ搴旂殑浜哄憳鐘舵�佷负鏂版暟鎹� - filteredInfos = append(filteredInfos, db.PersonStatus{OrgId: info.OrgId, CommunityID: communityID, DocumentNumber: info.DocumentNumber, Status: info.Status, FrequentAddress: info.FrequentAddress}) - continue - } - + person := statusIndex[info.DocumentNumber] + //fmt.Println("person: ", person.DocumentNumber, person.Status, person.FrequentAddress, person.LastAppearanceTime, person.LastAppearanceStatusTime) // 鍒ゆ柇鐘舵�佸拰甯哥敤鍦板潃鏄惁鐩哥瓑锛屽鏋滅浉绛夊垯蹇界暐 - if (info.Status == person.Status || info.CaptureDays <= ruleIndex[person.DocumentNumber].DetectionDaysEnd) && - info.FrequentAddress == person.FrequentAddress { - continue - } - + //if (info.Status == person.Status || info.CaptureDays <= ruleIndex[person.DocumentNumber].DetectionDaysEnd) && + // info.FrequentAddress == person.FrequentAddress { + // continue + //} // 鏇存柊杩囨护鍚庣殑淇℃伅鍒楄〃 - filteredInfos = append(filteredInfos, db.PersonStatus{OrgId: info.OrgId, CommunityID: communityID, DocumentNumber: info.DocumentNumber, Status: info.Status, FrequentAddress: info.FrequentAddress}) + //fmt.Println("LastAppearanceTime: ", person.LastAppearanceTime) + filteredInfos = append(filteredInfos, db.PersonStatus{CommunityID: communityID, DocumentNumber: info.DocumentNumber, Status: info.Status, FrequentAddress: info.FrequentAddress, LastAppearanceStatusTime: person.LastAppearanceTime}) } - + //fmt.Println("filteredInfos: ", filteredInfos) return filteredInfos } diff --git a/db/elastic.go b/db/elastic.go index 5e638c9..a215726 100644 --- a/db/elastic.go +++ b/db/elastic.go @@ -4,9 +4,11 @@ "basic.com/pubsub/esutil.git" "basic.com/valib/logger.git" "encoding/json" + "fmt" "ruleModelEngine/config" "ruleModelEngine/util" "strconv" + "strings" ) func decodeDocumentInfos(docInfo []map[string]interface{}) ([]CaptureInfo, error) { @@ -15,8 +17,6 @@ var captureInfo = CaptureInfo{} captureInfo.DocumentNumber = info["key"].(string) buckets := info["top_hits"].(map[string]interface{})["hits"].(map[string]interface{})["hits"].([]interface{}) - orgId := buckets[0].(map[string]interface{})["_source"].(map[string]interface{})["orgId"].(string) - captureInfo.OrgId = orgId for _, sourceInfo := range buckets { rSourceInfo := sourceInfo.(map[string]interface{}) source := rSourceInfo["_source"].(map[string]interface{}) @@ -82,13 +82,6 @@ } } } - ], - "must_not": [ - { - "term": { - "alarmRules.ruleId": 4 - } - } ] } }, @@ -104,7 +97,8 @@ "top_hits": { "_source": [ "documentNumber", - "id" + "id", + "alarmRules.ruleId" ], "size": 1, "sort": [ @@ -120,8 +114,8 @@ } } }` - //fmt.Println(queryDSL) //fmt.Println(esURL) + //fmt.Println(queryDSL) docNumberMap := make(map[string]string) buf, err := esutil.EsReq("POST", esURL, []byte(queryDSL)) if err != nil { @@ -136,9 +130,23 @@ buckets := info["top_hits"].(map[string]interface{})["hits"].(map[string]interface{})["hits"].([]interface{}) for _, sourceInfo := range buckets { rSourceInfo := sourceInfo.(map[string]interface{}) - source := rSourceInfo["_source"].(map[string]interface{}) - documentNumber := source["documentNumber"].(string) - id := source["id"].(string) + rSource := rSourceInfo["_source"].(map[string]interface{}) + //濡傛灉璇ュぉ鏈�鍚庝竴鏉″凡缁忛璀﹁繃杩涘嚭寮傚父锛屽皢杩囨护鎺変笉鍐嶉璀� + alarmFlag := false + alarmRules := rSource["alarmRules"].([]interface{}) + for _, alarmRule := range alarmRules { + ruleId := alarmRule.(map[string]interface{})["ruleId"].(string) + //fmt.Println("ruleId", ruleId,rSource["documentNumber"].(string),rSource["id"].(string)) + if ruleId == "4" { + alarmFlag = true + break + } + } + if alarmFlag == true { + continue + } + documentNumber := rSource["documentNumber"].(string) + id := rSource["id"].(string) docNumberMap[documentNumber] = id } } @@ -306,9 +314,10 @@ return false, nil } -func Query1MDataByCommunityId(communityId string) ([]CaptureInfo, error) { +func Query1MDataByCommunityId(communityId string, documentNumber []string, days int) ([]CaptureInfo, error) { //fmt.Println(config.Elastic.DocumentSize) //fmt.Println(config.Elastic.TopHitsSize) + documentNumberStr := strings.Replace(strings.Trim(fmt.Sprint(documentNumber), "[]"), " ", "\",\"", -1) esURL := "http://" + config.Elastic.Host + ":" + config.Elastic.Port + "/" + config.Elastic.Index + "/_search" queryDSL := ` { @@ -318,7 +327,7 @@ { "range": { "picDate": { - "gte": "now-30d/d", + "gte": "now-` + strconv.Itoa(days) + `d/d", "lt": "now/d" } } @@ -327,16 +336,13 @@ "term":{ "communityId":"` + communityId + `" } + }, + { + "terms":{ + "documentNumber":["` + documentNumberStr + `"] + } } - ], - "must_not": [ - { - "term": { - "documentNumber": "" - } - } - ], - "should": [] + ] } }, "size": 0, @@ -352,7 +358,6 @@ "_source": [ "documentNumber", "picDate", - "orgId", "cameraLocation.building", "cameraLocation.unit", "cameraLocation.floor", diff --git a/db/models.go b/db/models.go index 6420353..9348dda 100644 --- a/db/models.go +++ b/db/models.go @@ -78,18 +78,19 @@ } type PersonStatus struct { - Id uint `gorm:"column:id;primary_key;auto_increment;not null;"` - OrgId string `gorm:"column:org_id;type:varchar(299);not null;default:''"` // 娲惧嚭鎵�id - CommunityID string `gorm:"uniqueIndex:idx_document_number_community_id;index:community_id_last_appearance_time;column:community_id;type:varchar(299);not null;default:''"` // 灏忓尯id - DocumentNumber string `gorm:"uniqueIndex:idx_document_number_community_id;column:document_number;type:varchar(299);not null;default:''"` // 妗f缂栧彿 - DaysAppeared int `gorm:"column:days_appeared;type:int(11);not null;default:0" json:"daysAppeared"` // 鍑虹幇澶╂暟 - Count int `gorm:"column:count;type:int;not null;default:0"` // 鎶撴媿娆℃暟 - Status string `gorm:"column:status;type:varchar(255);not null;default:''"` //鏍囩 - LastAppearanceTime int64 `gorm:"index:community_id_last_appearance_time;column:last_appearance_time;type:int;not null;default:0" json:"lastAppearanceTime"` //鏈�鍚庡嚭鐜版椂闂� - LastLocation string `gorm:"column:last_location;type:varchar(255);not null;default:''" json:"lastLocation"` //鏈�鍚庡嚭鐜板湴鐐� - FrequentAddress string `gorm:"column:frequent_address;type:varchar(255);not null;default:''" json:"frequentAddress"` //甯稿嚭鐜板湴鐐� - CreatedAt time.Time - UpdatedAt time.Time + Id uint `gorm:"column:id;primary_key;auto_increment;not null;"` + OrgId string `gorm:"column:org_id;type:varchar(299);not null;default:''"` // 娲惧嚭鎵�id + CommunityID string `gorm:"uniqueIndex:idx_document_number_community_id;index:community_id_last_appearance_time;column:community_id;type:varchar(299);not null;default:''"` // 灏忓尯id + DocumentNumber string `gorm:"uniqueIndex:idx_document_number_community_id;column:document_number;type:varchar(299);not null;default:''"` // 妗f缂栧彿 + DaysAppeared int `gorm:"column:days_appeared;type:int(11);not null;default:0" json:"daysAppeared"` // 鍑虹幇澶╂暟 + Count int `gorm:"column:count;type:int;not null;default:0"` // 鎶撴媿娆℃暟 + Status string `gorm:"column:status;type:varchar(255);not null;default:''"` //鏍囩 + LastAppearanceTime int64 `gorm:"index:community_id_last_appearance_time;column:last_appearance_time;type:int;not null;default:0" json:"lastAppearanceTime"` //鏈�鍚庡嚭鐜版椂闂� + LastAppearanceStatusTime int64 `gorm:"column:last_appearance_status_time"` + LastLocation string `gorm:"column:last_location;type:varchar(255);not null;default:''" json:"lastLocation"` //鏈�鍚庡嚭鐜板湴鐐� + FrequentAddress string `gorm:"column:frequent_address;type:varchar(255);not null;default:''" json:"frequentAddress"` //甯稿嚭鐜板湴鐐� + CreatedAt time.Time + UpdatedAt time.Time ////OrgId string `gorm:"column:org_id"` //CommunityID string `gorm:"column:communityID"` //DocumentNumber string `gorm:"column:documentNumber"` @@ -139,7 +140,6 @@ } type CaptureInfo struct { - OrgId string `json:"orgId"` //娲惧嚭鎵�Id DocumentNumber string `json:"documentNumber"` CaptureDays int `json:"captureDays"` //鎶撴媿澶╂暟 OvernightStays int `json:"overnightStays"` //杩囧澶╂暟 diff --git a/db/repository.go b/db/repository.go index 221b906..afa5a12 100644 --- a/db/repository.go +++ b/db/repository.go @@ -103,6 +103,20 @@ return personStatusList, nil } +// 鏌ヨ灏忓尯妗f琛� (鍘熸煡璇换鍔″睘鎬�) +func QueryPersonStatusWithPagination(community_id string, timeThreshold int64) ([]*PersonStatus, error) { + var db = DB + var personStatusList []*PersonStatus + result := db.Select("document_number, status, frequent_address, last_appearance_time, last_appearance_status_time"). + Where("community_id = ? AND last_appearance_time != last_appearance_status_time AND last_appearance_time > ?", community_id, timeThreshold). + Find(&personStatusList) + if result.Error != nil { + logger.Error(result.Error) + return nil, result.Error + } + return personStatusList, nil +} + // 鏌ヨ浜虹墿骞撮緞 func GetAgeById(id string) (int, error) { var db = DB @@ -276,37 +290,16 @@ var db = DB // 閬嶅巻浜哄憳淇℃伅 for _, person := range persons { - - // 妫�鏌ヨ褰曟槸鍚﹀瓨鍦� - var existingPerson PersonStatus - err := db.Where("document_number = ? AND community_id = ?", person.DocumentNumber, communityID).First(&existingPerson).Error - if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - logger.Error("Query person error:", err, person.DocumentNumber, communityID) - //fmt.Println("asdasfasfasf") - continue - //return err + err := db.Model(&PersonStatus{}). + Where("document_number = ? AND community_id = ?", person.DocumentNumber, communityID). + Updates(map[string]interface{}{ + "status": person.Status, + "frequent_address": person.FrequentAddress, + "LastAppearanceStatusTime": person.LastAppearanceStatusTime, + }).Error + if err != nil { + return err } - - // 濡傛灉璁板綍瀛樺湪锛屽垯鏇存柊 - if existingPerson.DocumentNumber != "" { - err := db.Model(&PersonStatus{}). - Where("document_number = ? AND community_id = ?", person.DocumentNumber, communityID). - Updates(map[string]interface{}{ - "status": person.Status, - "frequent_address": person.FrequentAddress, - }).Error - if err != nil { - return err - } - } - //else { - // // 濡傛灉璁板綍涓嶅瓨鍦紝鍒欐彃鍏ユ柊璁板綍 - // err := db.Create(&person).Error - // if err != nil { - // return err - // } - //} - } return nil diff --git a/main.go b/main.go index 0471c1a..9699a46 100644 --- a/main.go +++ b/main.go @@ -56,7 +56,7 @@ func main() { //db.UpdatePersonStatusByIds() - immediate := flag.Bool("immediate", false, "whether to execute immediately") + immediate := flag.Bool("immediate", true, "whether to execute immediately") flag.Parse() if *immediate { diff --git a/rule/engine.go b/rule/engine.go index 8bbb953..64cb3ba 100644 --- a/rule/engine.go +++ b/rule/engine.go @@ -122,6 +122,8 @@ continue } captureInfo = append(captureInfo, info) + } else { + captureInfo = append(captureInfo, info) } } diff --git a/rule/service.go b/rule/service.go index c77e096..2a476d2 100644 --- a/rule/service.go +++ b/rule/service.go @@ -32,7 +32,7 @@ // 鎵ц绋嬪簭鍏ュ彛 func ExecuteTask() { //杩涘嚭寮傚父甯冩帶浠诲姟锛屾殏鏃跺綊绫诲埌鏍囩璁$畻閮ㄥ垎 - fmt.Println("杩涘嚭寮傚父寮�濮嬪叆鍙o紒锛侊紒锛侊紒锛�") + //fmt.Println("杩涘嚭寮傚父寮�濮嬪叆鍙o紒锛侊紒锛侊紒锛�") tasks, err := db.GetAllTaskData() if err != nil { logger.Error("GetAllTaskData Error", err) @@ -44,20 +44,21 @@ tkInfo.Name = taskInfo.Name } } - fmt.Println("tkInfo: ", tkInfo) + //fmt.Println("tkInfo: ", tkInfo) days := config.Api.AInterval docNumIdMap, err := db.QueryLastIdByDayRange(days, days-1) if err != nil { logger.Error("QueryByDayRange err: ", err) } - fmt.Println("docNumIdMap: ", len(docNumIdMap)) + //fmt.Println("docNumIdMap: ", len(docNumIdMap)) for docNumber, id := range docNumIdMap { //fmt.Println(docNumber, id) alarmRules := make([]db.AlarmRule, 0) - flag := task.EnteringButNotLeaving(docNumber, id, days) + flag := task.EnteringButNotLeaving(docNumber, days) if flag == true { alarmRules = append(alarmRules, db.AlarmRule{RuleId: strconv.Itoa(tkInfo.Id), RuleText: tkInfo.Name, AlarmLevel: "0"}) } + //fmt.Println("alarmRules: ", id, alarmRules) addFlag, err := db.AddAlarmRules(alarmRules, id) if err != nil { logger.Error("AddAlarmRules err: ", err) @@ -88,53 +89,68 @@ //if communityID != "50010101010000001001" { // continue //} + fmt.Println("communityID: ", communityID) //鏌ヨ绀惧尯鍐呬汉鍛樻。妗堬紝鏂逛究鏁版嵁鏇存柊 - personStatus, err := db.GetDBPersonStatusData(communityID) + personStatusList, err := db.QueryPersonStatusWithPagination(communityID, 30) if err != nil { - logger.Error("GetDBPersonStatusData Error", err) + logger.Error("QueryPersonStatusWithPagination err: ", err) } - //fmt.Println(labeManage) - //fmt.Println("personStatus: ", personStatus) - //fmt.Println("CcmmunityIDs: ", cmmunityID) - //鎸夌ぞ鍖篿d鏌ヨ杩戜竴涓湀es鏁版嵁 - captureInfos, err := db.Query1MDataByCommunityId(communityID) - //fmt.Println("captureInfos: ", captureInfos) - //residentCount := 0 + // + + documentNumberIDS := make([]string, 0) + for _, personStatus := range personStatusList { + //fmt.Println("personStatus.LastAppearanceTime: ", personStatus.LastAppearanceTime) + documentNumberIDS = append(documentNumberIDS, personStatus.DocumentNumber) + //涓氬姟閫昏緫 + } + //fmt.Println("len(documentNumberIDS)", len(documentNumberIDS)) + captureInfos := make([]db.CaptureInfo, 0) + batchSize := config.Elastic.BatchSize + //fmt.Println(batchSize) + for i := 0; i < len(documentNumberIDS); i += batchSize { + end := i + batchSize + if end > len(documentNumberIDS) { + end = len(documentNumberIDS) + } + batch := documentNumberIDS[i:end] + //fmt.Println("batch: ", batch) + batchCaptureInfos, err := db.Query1MDataByCommunityId(communityID, batch, 30) + if err != nil { + logger.Error("Query1MDataByCommunityId Error", err) + } + if len(batchCaptureInfos) == 0 { + continue + } + //fmt.Println("batchCaptureInfos: ", batchCaptureInfos) + captureInfos = append(captureInfos, batchCaptureInfos...) + } + if len(captureInfos) == 0 { + continue + } + fmt.Println("鍏辫鏈夋。妗堟暟鎹潯鏁颁负锛�", len(captureInfos)) + //琛ュ叏鍒嗘瀽鎵�闇�鏁版嵁 for i := range captureInfos { - //fmt.Println(captureInfos[i].DocumentNumber) + captureDays, overnightCount := data.CalculateCaptureDays(captureInfos[i].CaptureDetail) captureInfos[i].CaptureDays = captureDays captureInfos[i].OvernightStays = overnightCount - - //if captureInfos[i].CaptureDays <= 10 && captureInfos[i].OvernightStays >= 1 { - // fmt.Println(captureInfos[i].DocumentNumber) - // fmt.Println("璇ヤ汉鍛樺嚭鐜板ぉ鏁颁负", captureInfos[i].CaptureDays) - // fmt.Println("璇ヤ汉鍛樿繃澶滃ぉ鏁颁负", captureInfos[i].OvernightStays) - //} captureInfos[i].Status = data.SetStatus(captureDays, ruleInfo) if captureInfos[i].OvernightStays >= 5 && (captureInfos[i].CaptureDays <= 14 && captureInfos[i].CaptureDays >= 5) { captureInfos[i].Status = "resident" } - //if captureInfos[i].Status == "resident" { - // residentCount++ - //} + //fmt.Println("SetStatus: ", captureInfos[i].Status) age, err := db.QueryAgeById(captureInfos[i].DocumentNumber) if err != nil { logger.Error("QueryAgeById ERROR", err) } captureInfos[i].Age = age - data.SetFrequentAddress(&captureInfos[i]) - //fmt.Println("captureInfos[i].Age: ", captureInfos[i].Age) + captureInfos[i].FrequentAddress = data.GetFrequentAddress(captureInfos[i].CaptureDetail) + //fmt.Println("CaptureDetail: ", captureInfos[i].DocumentNumber, captureInfos[i].CaptureDays, captureInfos[i].CaptureDetail) } //fmt.Println("residentCount: ", residentCount) - if err != nil { - logger.Error("MatchAllTargets Error", err) - } - if len(captureInfos) == 0 { - continue - } //fmt.Println("captureInfosQ: ", captureInfos) + for _, identity := range labeManage { switch identity.Name { case "鏈嶅姟浜哄憳": @@ -153,10 +169,14 @@ logger.Error("UpdateDBPersonLabel Error", errIdentity) } //continue - //fmt.Println("captureInfos: ", captureInfos) - postCaptureInfos := data.ProcessData(captureInfos, personStatus, ruleInfo, communityID) - //fmt.Println("postCaptureInfos: ", postCaptureInfos) - //fmt.Println("鍏辫繃婊ゆ潯鏁帮細", len(captureInfos)-len(postCaptureInfos)) + + postCaptureInfos := data.ProcessData(captureInfos, personStatusList, ruleInfo, communityID) + /*for _, inf := range postCaptureInfos { + fmt.Println("inf: ", inf.DocumentNumber, inf.Status, inf.FrequentAddress, inf.LastAppearanceStatusTime) + }*/ + //fmt.Println("鍏辨洿鏂版。妗堟暟锛�", len(postCaptureInfos)) + //fmt.Println("----->captureInfos: ", len(captureInfos)) + //continue UpdatePersonInfoErr := db.UpdatePersonInfo(postCaptureInfos, communityID) if UpdatePersonInfoErr != nil { logger.Error("MatchPermanentResidentTargets Error: ", UpdatePersonInfoErr) diff --git a/task/engine.go b/task/engine.go index c6b3d28..53c59c2 100644 --- a/task/engine.go +++ b/task/engine.go @@ -49,7 +49,7 @@ return false } -func EnteringButNotLeaving(docNumber string, id string, days int) bool { +func EnteringButNotLeaving(docNumber string, days int) bool { total, err := db.QueryTimesByDocNumberDays(days-1, docNumber) if err != nil { logger.Error("QueryTimesByDocNumberDays err: ", err) -- Gitblit v1.8.0