From a1fdc969dd20a97087e986c69fdfd25ffe115368 Mon Sep 17 00:00:00 2001 From: sunty <1172534965@qq.com> Date: 星期二, 30 四月 2024 18:10:41 +0800 Subject: [PATCH] 新增迁入迁出,属性分析,身份分析等 --- db/repository.go | 290 ++++++++++++ config/config.go | 73 +++ data/prepare.go | 122 +++++ go.mod | 46 ++ rule/service.go | 147 ++++++ main.go | 46 ++ rule/engine.go | 304 +++++++++++++ util/util.go | 285 ++++++++++++ config/app.yaml | 26 + 9 files changed, 1,339 insertions(+), 0 deletions(-) diff --git a/config/app.yaml b/config/app.yaml new file mode 100644 index 0000000..f740e99 --- /dev/null +++ b/config/app.yaml @@ -0,0 +1,26 @@ +app: + name: Capture Analysis Rule Model Engine + port: 7024 + logLevel: debug +database: + driver: mysql + host: 192.168.20.115 + port: 3306 + # username: root + # password: c++java123 + name: faceanalysis +elastic: + host: 192.168.20.115 + port: 9200 + index: ai_face_ocean + type: analysisModel + documentSize: 100000 + topHitsSize: 100000 + cameraSize: 100000 + timeInterval: 30 +log: + path: /opt/vasystem/valog/ + level: -1 + maxSize: 128 + maxBackups: 30 + maxAge: 15 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..ed8e823 --- /dev/null +++ b/config/config.go @@ -0,0 +1,73 @@ +package config + +import ( + "basic.com/valib/logger.git" + "github.com/spf13/viper" + "log" +) + +type LogConfig struct { + Path string `mapstructure:"path"` //鏃ュ織瀛樺偍璺緞 + Level int `mapstructure:"level"` //鏃ュ織绛夌骇 + MaxSize int `mapstructure:"maxSize"` //鏃ュ織鏂囦欢澶у皬涓婇檺 + MaxBackups int `mapstructure:"maxBackups"` //鏃ュ織鍘嬬缉鍖呬釜鏁� + MaxAge int `mapstructure:"maxAge"` //淇濈暀鍘嬬缉鍖呭ぉ鏁� +} + +type database struct { + Driver string `mapstructure: "driver"` + Host string `mapstructure: "host"` + Port string `mapstructure: "port"` + Name string `mapstructure: "name"` +} + +type elastic struct { + Host string `mapstructure: "host"` + Port string `mapstructure: "port"` + Index string `mapstructure: "index"` + Type string `mapstructure: "type"` + DocumentSize int `mapstructure: "documentSize"` + TopHitsSize int `mapstructure: "topHitsSize"` + CameraSize int `mapstructure: "cameraSize"` + TimeInterval int `mapstructure: "timeInterval"` +} + +type app struct { + Name string `mapstructure: "Annotation Service Application"` + Port string `mapstructure: "port"` + LogLevel string `mapstructure: "logLevel"` +} + +var LogConf = &LogConfig{} +var DataBase = &database{} +var Elastic = &elastic{} +var App = &app{} +var LogBasePath string +var LogLevel int + +func Init(env string) { + var err error + viper.SetConfigType("yaml") + viper.SetConfigName(env) + viper.AddConfigPath("config") + err = viper.ReadInConfig() + if err != nil { + log.Fatal("error on parsing configuration file", err) + } + viper.UnmarshalKey("elastic", Elastic) + viper.UnmarshalKey("database", DataBase) + viper.UnmarshalKey("app", App) + viper.UnmarshalKey("log", LogConf) + logger.SetLevel(LogConf.Level) + if viper.GetString("LogBasePath") != "" { + LogBasePath = viper.GetString("LogBasePath") + } else { + LogBasePath = "./logger/" + } + + if viper.IsSet("LogLevel") && viper.GetInt("LogLevel") >= logger.PanicLevel && viper.GetInt("LogLevel") <= logger.DebugLevel { + LogLevel = viper.GetInt("LogLevel") + } else { + LogLevel = logger.DebugLevel + } +} diff --git a/data/prepare.go b/data/prepare.go new file mode 100644 index 0000000..ac6cf9a --- /dev/null +++ b/data/prepare.go @@ -0,0 +1,122 @@ +package data + +import ( + "fmt" + "ruleModelEngine/db" + "time" +) + +// 璁$畻鎶撴媿澶╂暟 +func CalculateCaptureDays(details []db.CaptureDetail) int { + // 浣跨敤 map 鏉ュ瓨鍌ㄦ瘡澶╂槸鍚︽湁鎶撴媿璁板綍 + captureMap := make(map[string]bool) + for _, detail := range details { + // 瑙f瀽鎶撴媿鏃ユ湡 + layout := "2006-01-02 15:04:05" + captureTime, err := time.Parse(layout, detail.CaptureDate) + if err != nil { + fmt.Println("瑙f瀽鎶撴媿鏃ユ湡鏃跺嚭閿�:", err) + continue + } + // 鑾峰彇鏃ユ湡閮ㄥ垎 + date := captureTime.Format("2006-01-02") + // 鍦� map 涓爣璁拌繖涓�澶╂湁鎶撴媿璁板綍 + captureMap[date] = true + } + + // 缁熻鏈夋姄鎷嶈褰曠殑澶╂暟 + captureDays := 0 + for range captureMap { + captureDays++ + } + + return captureDays +} + +// 璁剧疆鐘舵�� +func SetStatus(captureDays int, rules []db.PersonnelStatusRule) string { + for _, rule := range rules { + if captureDays >= rule.DetectionDaysStart && captureDays <= rule.DetectionDaysEnd { + return rule.Name + } + } + return rules[1].Name +} + +// SetFrequentAddress 鏂规硶璁$畻鍑虹幇鏈�棰戠箒鐨勫嚭琛屽湴鍧�骞惰缃负甯哥敤鍦板潃 +func SetFrequentAddress(c *db.CaptureInfo) { + outAddressCounts := make(map[string]int) + inAddressCounts := make(map[string]int) + // 缁熻姣忎釜鍑鸿鍦板潃鐨勫嚭鐜版鏁� + for _, detail := range c.CaptureDetail { + if detail.Direction == "out" { + outAddressCounts[detail.CaptureAddress]++ + } + if detail.Direction == "in" { + inAddressCounts[detail.CaptureAddress]++ + } + } + + // 鎵惧埌鍑虹幇娆℃暟鏈�澶氱殑鍑鸿鍦板潃 + maxOutCount := 0 + var frequentAddress string + for address, count := range outAddressCounts { + if count > maxOutCount { + maxOutCount = count + frequentAddress = address + } + } + if frequentAddress == "" { + maxInCount := 0 + for address, count := range inAddressCounts { + if count > maxInCount { + maxInCount = count + frequentAddress = address + } + } + } + // 灏嗗嚭鐜版鏁版渶澶氱殑鍑鸿鍦板潃璁剧疆涓哄父鐢ㄥ湴鍧� + c.FrequentAddress = frequentAddress +} + +// processData 鍑芥暟澶勭悊鏁版嵁锛屾牴鎹姹傝繃婊ゆ帀鏁版嵁骞舵牴鎹鍒欐洿鏂扮姸鎬� +func ProcessData(captureInfos []db.CaptureInfo, personStatus []db.PersonStatus, ruleInfos []db.PersonnelStatusRule, communityID string) []db.PersonStatus { + filteredInfos := make([]db.PersonStatus, 0) + + // 鏋勫缓蹇�熸煡鎵剧储寮曪紝鏂逛究鏌ユ壘瀵瑰簲鐨勪汉鍛樼姸鎬佸拰瑙勫垯 + statusIndex := make(map[string]db.PersonStatus) + ruleIndex := make(map[string]db.PersonnelStatusRule) + + for _, person := range personStatus { + statusIndex[person.DocumentNumber] = person + } + + for _, rule := range ruleInfos { + ruleIndex[rule.Name] = rule + } + + // 澶勭悊姣忎釜鎶撴媿淇℃伅 + 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{CommunityID: communityID, DocumentNumber: info.DocumentNumber, Status: info.Status, FrequentAddress: info.FrequentAddress}) + continue + } + + // 鍒ゆ柇鐘舵�佸拰甯哥敤鍦板潃鏄惁鐩哥瓑锛屽鏋滅浉绛夊垯蹇界暐 + if (info.Status == person.Status || info.CaptureDays <= ruleIndex[person.DocumentNumber].DetectionDaysEnd) && + info.FrequentAddress == person.FrequentAddress { + continue + } + + // 鏇存柊杩囨护鍚庣殑淇℃伅鍒楄〃 + filteredInfos = append(filteredInfos, db.PersonStatus{CommunityID: communityID, DocumentNumber: info.DocumentNumber, Status: info.Status, FrequentAddress: info.FrequentAddress}) + + } + + return filteredInfos +} diff --git a/db/repository.go b/db/repository.go new file mode 100644 index 0000000..5cdc1d8 --- /dev/null +++ b/db/repository.go @@ -0,0 +1,290 @@ +package db + +import ( + "basic.com/valib/logger.git" + "errors" + "gorm.io/gorm" +) + +// 鏌ヨ灏忓尯琛� +func GetCommunityIDs() ([]string, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + // 鏌ヨ鏁版嵁 + var communityIDs []string + result := db.Table("domain_unit").Where("domainType = ?", 1).Pluck("id", &communityIDs) + if result.Error != nil { + return nil, result.Error + } + + return communityIDs, nil +} + +// 鏌ヨ鍏ㄩ儴鏁版嵁 +func GetAllData() ([]PersonnelStatusRule, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + var rules []PersonnelStatusRule + if err := db.Find(&rules).Error; err != nil { + return nil, err + } + + return rules, nil +} + +// 鏌ヨ浣忔埛鏃堕棿鏁版嵁 +func GetResidentData(status, communityID string) ([]Resident, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + var residents []Resident + + // 鎵ц鏌ヨ + rows, err := db.Table("person_status"). + Select("person_status.documentNumber", "person_status.communityID", "snapshot_count_summary.last_appearance_time", "snapshot_count_summary.created_at"). + Joins("INNER JOIN snapshot_count_summary ON person_status.documentNumber = snapshot_count_summary.document_number AND person_status.communityID = snapshot_count_summary.community_id"). + Where("person_status.status = ? AND person_status.communityID = ?", status, communityID). + Rows() + if err != nil { + return nil, err + } + defer rows.Close() + + // 閬嶅巻鏌ヨ缁撴灉 + for rows.Next() { + var resident Resident + err := rows.Scan(&resident.DocumentNumber, &resident.CommunityID, &resident.LastAppearanceTime, &resident.CreateAt) + if err != nil { + return nil, err + } + residents = append(residents, resident) + } + if err := rows.Err(); err != nil { + return nil, err + } + + return residents, nil +} + +// 鏌ヨ浜虹墿灞炴�� +func GetDBPersonStatusData(id string) ([]PersonStatus, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + // 鏌ヨ鏁版嵁 + var personStatusList []PersonStatus + if err := db.Table("person_status"). + Select("documentNumber, status, frequentAddress"). + Where("communityID = ?", id). + Find(&personStatusList).Error; err != nil { + return nil, err + } + + return personStatusList, nil +} + +// 鏍规嵁绀惧尯id鍜屼綇鎴峰睘鎬ф煡璇綇鎴锋。妗堢紪鍙� +func GetDocNumberFromPersonStatus(id, status string) ([]string, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + // 鏌ヨ鏁版嵁 + var personStatusList []PersonStatus + if err := db.Table("person_status"). + Select("documentNumber, status, frequentAddress"). + Where("communityID = ? AND status = ?", id, status). + Find(&personStatusList).Error; err != nil { + return nil, err + } + + docNum := make([]string, 0) + for _, ps := range personStatusList { + docNum = append(docNum, ps.DocumentNumber) + } + + return docNum, nil +} + +// 鏌ヨ浜虹墿韬唤灞炴�ц〃 +func GetLabelManageIdentity(IdentityType int) ([]LabelManage, error) { + db, err := ConnectDB() + if err != nil { + return nil, err + } + + // 鏌ヨ鏁版嵁 + var labelManageIdentity []LabelManage + if err := db.Table("label_manage"). + Select("id, name, valid_days"). + Where("type = ?", IdentityType). + Find(&labelManageIdentity).Error; err != nil { + return nil, err + } + + return labelManageIdentity, nil +} + +// UpdatePersonInfo 鏇存柊鎴栨彃鍏ュ涓汉鍛樹俊鎭� +func UpdateMoveInout(personsMoveInout []MoveInout) error { + // 鏁版嵁搴撹繛鎺ヤ俊鎭� + db, err := ConnectDB() + if err != nil { + return err + } + // 閬嶅巻浜哄憳淇℃伅 + for _, personMoveInout := range personsMoveInout { + + // 妫�鏌ヨ褰曟槸鍚﹀瓨鍦� + var existingPerson MoveInout + err := db.Where("document_number = ? AND community_id = ?", personMoveInout.DocumentNumber, personMoveInout.CommunityID).First(&existingPerson).Error + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + logger.Error("Query person error:", err, personMoveInout.DocumentNumber, personMoveInout.CommunityID) + //fmt.Println("asdasfasfasf") + continue + //return err + } + + // 濡傛灉璁板綍瀛樺湪锛屽垯鏇存柊 + if existingPerson.DocumentNumber != "" { + if existingPerson.Status != "Verified" { + err := db.Model(&MoveInout{}). + Where("document_number = ? AND community_id = ?", personMoveInout.DocumentNumber, personMoveInout.CommunityID). + Updates(map[string]interface{}{ + "status": personMoveInout.Status, + "move_out_date": personMoveInout.MoveOutDate, + }).Error + if err != nil { + return err + } + } else { + err := db.Model(&MoveInout{}). + Where("document_number = ? AND community_id = ?", personMoveInout.DocumentNumber, personMoveInout.CommunityID). + Updates(map[string]interface{}{ + "move_out_date": personMoveInout.MoveOutDate, + }).Error + if err != nil { + return err + } + } + } else { + // 濡傛灉璁板綍涓嶅瓨鍦紝鍒欐彃鍏ユ柊璁板綍 + err := db.Create(&personsMoveInout).Error + if err != nil { + return err + } + } + + } + + return nil +} + +// UpdatePersonInfo 鏇存柊鎴栨彃鍏ュ涓汉鍛樿韩浠戒俊鎭� +func UpdateDBPersonLabel(personsIdentity []Identity) error { + // 鏁版嵁搴撹繛鎺ヤ俊鎭� + db, err := ConnectDB() + if err != nil { + return err + } + // 閬嶅巻浜哄憳淇℃伅 + for _, personIdentity := range personsIdentity { + + // 妫�鏌ヨ褰曟槸鍚﹀瓨鍦� + var existingPerson Identity + err := db.Where("dbtablepersons_id = ? AND community_id = ? AND label_id = ?", + personIdentity.DocumentNumber, + personIdentity.CommunityID, + personIdentity.LabelId, + ).First(&existingPerson).Error + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + logger.Error("Query person error:", err, + personIdentity.DocumentNumber, + personIdentity.CommunityID, + personIdentity.LabelId) + //fmt.Println("asdasfasfasf") + continue + //return err + } + + // 濡傛灉璁板綍瀛樺湪锛屽垯鏇存柊 + if existingPerson.DocumentNumber != "" { + err := db.Model(&Identity{}). + Where("dbtablepersons_id = ? AND community_id = ? AND label_id = ?", + personIdentity.DocumentNumber, + personIdentity.CommunityID, + personIdentity.LabelId, + ). + Updates(map[string]interface{}{ + "expire_time": personIdentity.ExpireTime, + }).Error + if err != nil { + return err + } + } else { + // 濡傛灉璁板綍涓嶅瓨鍦紝鍒欐彃鍏ユ柊璁板綍 + err := db.Create(&personIdentity).Error + if err != nil { + return err + } + } + + } + + return nil +} + +// UpdatePersonInfo 鏇存柊鎴栨彃鍏ュ涓汉鍛樹俊鎭� +func UpdatePersonInfo(persons []PersonStatus, communityID string) error { + // 鏁版嵁搴撹繛鎺ヤ俊鎭� + db, err := ConnectDB() + if err != nil { + return err + } + // 閬嶅巻浜哄憳淇℃伅 + for _, person := range persons { + + // 妫�鏌ヨ褰曟槸鍚﹀瓨鍦� + var existingPerson PersonStatus + err := db.Where("documentNumber = ? AND communityID = ?", 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 + } + + // 濡傛灉璁板綍瀛樺湪锛屽垯鏇存柊 + if existingPerson.DocumentNumber != "" { + err := db.Model(&PersonStatus{}). + Where("documentNumber = ? AND communityID = ?", person.DocumentNumber, communityID). + Updates(map[string]interface{}{ + "status": person.Status, + "frequentAddress": person.FrequentAddress, + }).Error + if err != nil { + return err + } + } else { + // 濡傛灉璁板綍涓嶅瓨鍦紝鍒欐彃鍏ユ柊璁板綍 + err := db.Create(&person).Error + if err != nil { + return err + } + } + + } + + return nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..d3cde2e --- /dev/null +++ b/go.mod @@ -0,0 +1,46 @@ +module ruleModelEngine + +go 1.20 + +require ( + basic.com/pubsub/esutil.git v0.0.0-20240401091908-7a10b30099c6 + basic.com/valib/logger.git v0.0.0-20220225105132-5cf6309c132f + github.com/spf13/viper v1.18.2 + gorm.io/driver/mysql v1.5.6 + gorm.io/gorm v1.25.9 +) + +require ( + basic.com/pubsub/protomsg.git v0.0.0-20230210092337-5f1e6cdae7c3 // indirect + filippo.io/edwards25519 v1.1.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/natefinch/lumberjack v2.0.0+incompatible // indirect + github.com/pelletier/go-toml/v2 v2.2.1 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/main.go b/main.go new file mode 100644 index 0000000..b03808f --- /dev/null +++ b/main.go @@ -0,0 +1,46 @@ +package main + +import ( + "ruleModelEngine/config" + //"annotation_service/db" + "basic.com/valib/logger.git" + "flag" + "ruleModelEngine/rule" + "time" +) + +var env = flag.String("app", "app", "read database info") + +func init() { + config.Init(*env) + var logFile = config.LogConf.Path + "annotation_service.log" + logger.InitLogger(logFile, config.LogConf.Level, config.LogConf.MaxSize, config.LogConf.MaxBackups, config.LogConf.MaxAge) + logger.Info("loginit success !") + +} + +func main() { + //db.UpdatePersonStatusByIds() + immediate := flag.Bool("immediate", true, "whether to execute immediately") + flag.Parse() + + if *immediate { + logger.Info("Executing immediately...") + rule.ExecuteTask() + } + + now := time.Now() + next := time.Date(now.Year(), now.Month(), now.Day()+1, 1, 0, 0, 0, now.Location()) + duration := next.Sub(now) + + timer := time.NewTimer(duration) + logger.Info("The program has started and will execute at one o'clock in the early morning every night.") + for { + <-timer.C + logger.Info("Executing at 1 AM...") + rule.ExecuteTask() + next = next.Add(24 * time.Hour) + timer.Reset(next.Sub(time.Now())) + } + +} diff --git a/rule/engine.go b/rule/engine.go new file mode 100644 index 0000000..9409760 --- /dev/null +++ b/rule/engine.go @@ -0,0 +1,304 @@ +package rule + +import ( + "basic.com/valib/logger.git" + "errors" + "fmt" + "math" + "ruleModelEngine/db" + "strings" + "time" +) + +func processResidentStatus(residents []db.Resident) []db.MoveInout { + // 鑾峰彇褰撳墠鏃ユ湡鐨勫勾鏈堟棩 + now := time.Now() + currentDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()) + moveInout := make([]db.MoveInout, 0) + + // 閬嶅巻Resident缁撴瀯浣撳垏鐗� + for _, resident := range residents { + //fmt.Println("gogogogo!!!!!!!!!!!!!!!!!!!!!!!!!!!") + // 灏嗗瓧绗︿覆绫诲瀷鐨勬椂闂磋浆鎹负time.Time绫诲瀷锛屽苟鍙繚鐣欏勾鏈堟棩 + lastAppearanceTime := time.Unix(resident.LastAppearanceTime, 0) + lastAppearanceDate := time.Date(lastAppearanceTime.Year(), lastAppearanceTime.Month(), lastAppearanceTime.Day(), 0, 0, 0, 0, lastAppearanceTime.Location()) + //lastAppearanceTime, err := time.Parse("2006-01-02", resident.LastAppearanceTime) + datePart := strings.Split(resident.CreateAt, "T")[0] + createdAt, err := time.Parse("2006-01-02", datePart) + if err != nil { + fmt.Println(err) + // 澶勭悊鏃堕棿瑙f瀽閿欒 + // 鍙互閫夋嫨璺宠繃璇ユ潯鏁版嵁鎴栬�呰褰曟棩蹇� + continue + } + // 璁$畻LastAppearanceTime鍜孋reateAt璺濈褰撳墠鏃ユ湡鐨勫ぉ鏁� + daysSinceLastAppearance := currentDate.Sub(lastAppearanceDate).Hours() / 24 + daysSinceCreateAt := currentDate.Sub(createdAt).Hours() / 24 + + moveType := "MoveIn" + status := "" + //fmt.Println("daysSinceLastAppearance: ", daysSinceLastAppearance) + //fmt.Println("daysSinceCreateAt: ", daysSinceCreateAt) + // 鍒ゆ柇鏄惁涓虹枒浼兼惉绂绘垨纭鎼 + if daysSinceLastAppearance > 15 { + moveType = "MoveOut" + status = "Pending" + } else if daysSinceLastAppearance > 30 { + moveType = "MoveOut" + status = "Confirmed" + } + + // 鍒ゆ柇鏄惁涓虹枒浼煎叆浣忔垨纭鍏ヤ綇 + if moveType == "MoveIn" { + if daysSinceCreateAt > 15 { + status = "Pending" + } else if daysSinceCreateAt > 30 { + status = "Confirmed" + } + } + + //fmt.Println("status: ", status) + moveInout = append(moveInout, db.MoveInout{ + DocumentNumber: resident.DocumentNumber, + CommunityID: resident.CommunityID, + MoveInDate: createdAt, // 瀛樺偍骞存湀鏃� + MoveOutDate: &lastAppearanceDate, // 瀛樺偍骞存湀鏃� + MoveType: moveType, + Status: status, + }) + } + + return moveInout +} + +func ProcessRuleEngine(personInfos []db.CaptureInfo, ruleInfo []db.PersonnelStatusRule, cmmunityID string) (bool, error) { + ruleInfoCheck := checkRuleValidity(ruleInfo) + if ruleInfoCheck == false { + logger.Error("瑙勫垯搴撴暟鎹紓甯�") + return false, errors.New("瑙勫垯搴撴暟鎹紓甯�") + } + fmt.Println("娓呮礂鍓嶏細 ", len(personInfos)) + logger.Info("瑙勫垯绠楁硶鎵ц瀹屾瘯锛侊紒锛�") + return true, nil +} + +func CreateLinearModel(personInfos []db.CaptureInfo, communityID string, threshold float64, validDays int) ([]db.Identity, []db.CaptureInfo) { + identity := make([]db.Identity, 0) + captureInfo := make([]db.CaptureInfo, 0) + for _, info := range personInfos { + if info.Status == "resident" { + addrData := make(map[string]int) + //fmt.Println("DocumentNumber: ", info.DocumentNumber) + for _, addr := range info.CaptureDetail { + if !strings.Contains(addr.CaptureAddress, "F") || addr.Direction == "in" { + continue + } + addrData[addr.CaptureAddress]++ + } + if len(addrData) < 5 { + captureInfo = append(captureInfo, info) + continue + } + addrDataArray := mapIntToFloatSlice(addrData) + derivative := firstDerivative(addrDataArray) + //fmt.Println(addrData) + stdDev := stdDeviation(derivative) + if len(addrDataArray) > 10 { + stdDev = stdDev / 2 + } + + if stdDev < threshold { + //fmt.Println(addrDataArray) + //fmt.Println("threshold: ", threshold) + //fmt.Println("stdDev: ", stdDev) + info.Status = "fieldworker" + captureInfo = append(captureInfo, info) + identity = append(identity, db.Identity{ + CommunityID: communityID, + DocumentNumber: info.DocumentNumber, + LabelId: 3, + ExpireTime: GetCurrentDateAddDaysTimestamp(validDays)}) + continue + } + captureInfo = append(captureInfo, info) + } + + } + //addressLinearData := [] + return identity, captureInfo +} + +func totalDirection(personInfos []db.CaptureInfo) { + // 缁熻姣忎釜鍦板潃鐨勮繘鍑烘儏鍐� + addrData := make(map[string]map[string]int) // 浣跨敤map[string]map[string]int鏉ュ瓨鍌ㄦ瘡涓湴鍧�鐨勮繘鍑烘儏鍐� + for _, info := range personInfos { + if info.Status == "resident" { + for _, addr := range info.CaptureDetail { + if !strings.Contains(addr.CaptureAddress, "F") { + continue + } + // 妫�鏌ヨ鍦板潃鏄惁宸茬粡鍦╝ddrData涓� + if _, ok := addrData[addr.CaptureAddress]; !ok { + addrData[addr.CaptureAddress] = make(map[string]int) + } + // 鏇存柊璇ュ湴鍧�鐨勮繘鍑烘儏鍐� + addrData[addr.CaptureAddress][addr.Direction]++ + } + } + } + fmt.Println(addrData) +} + +func CreateProcessModel(personInfos []db.CaptureInfo, days int) { + timeTOIndex := getIndexForTime(24 * 60) + dateTOIndex := getIndexForDate(days) + //fmt.Println(dateTOIndex) + inModelMatrix := make([][]int, days) + for i := range inModelMatrix { + inModelMatrix[i] = make([]int, 24*60) + } + outModelMatrix := make([][]int, days) + for i := range outModelMatrix { + outModelMatrix[i] = make([]int, 24*60) + } + for _, info := range personInfos { + //fmt.Println("info: ", info) + for _, captureInfo := range info.CaptureDetail { + captrueDateTime := captureInfo.CaptureDate + dateTimeObj, err := time.Parse("2006-01-02 15:04:05", captrueDateTime) + if err != nil { + logger.Error("Parse time error", err) + return + } + if isWeekend(dateTimeObj) { + continue + } + datePart := dateTimeObj.Format("2006-01-02") + timePart := dateTimeObj.Format("15:04") + switch captureInfo.Direction { + case "out": + outModelMatrix[dateTOIndex[datePart]][timeTOIndex[timePart]] = 1 + case "in": + inModelMatrix[dateTOIndex[datePart]][timeTOIndex[timePart]] = 1 + default: + continue + } + } + } + fmt.Println(outModelMatrix) + fmt.Println(inModelMatrix) +} + +// 璁$畻涓�闃跺鏁� +func firstDerivative(data []float64) []float64 { + n := len(data) + derivative := make([]float64, n) + + for i := 0; i < n-1; i++ { + derivative[i] = data[i+1] - data[i] + } + + return derivative +} + +// 璁$畻瀵兼暟鐨勬爣鍑嗗樊 +func stdDeviation(data []float64) float64 { + n := len(data) + mean := 0.0 + + // 璁$畻骞冲潎鍊� + for _, value := range data { + mean += value + } + mean /= float64(n) + + // 璁$畻鏂瑰樊 + sum := 0.0 + for _, value := range data { + sum += math.Pow(value-mean, 2) + } + variance := sum / float64(n) + + return math.Sqrt(variance) +} + +// 灏� map[string]int 杞崲涓� []float64 +func mapIntToFloatSlice(addrData map[string]int) []float64 { + var data []float64 + + // 閬嶅巻 addrData锛屽皢鍏跺�艰浆鎹负 float64 绫诲瀷鍚庢坊鍔犲埌 data 涓� + for _, v := range addrData { + data = append(data, float64(v)) + } + + return data +} + +// GetCurrentDateAddDaysTimestamp 杩斿洖褰撳墠鏃ユ湡鍔犱笂鎸囧畾澶╂暟鍚庣殑鏃堕棿鎴筹紙绉掞級 +func GetCurrentDateAddDaysTimestamp(days int) int64 { + // 鑾峰彇褰撳墠鏃堕棿 + now := time.Now() + + // 鍙繚鐣欏勾鏈堟棩閮ㄥ垎 + currentYear, currentMonth, currentDay := now.Date() + + // 鏋勫缓褰撳ぉ闆剁偣鏃堕棿 + zeroTime := time.Date(currentYear, currentMonth, currentDay, 0, 0, 0, 0, now.Location()) + + // 灏嗗ぉ鏁拌浆鎹负Duration绫诲瀷 + duration := time.Duration(days) * 24 * time.Hour + + // 璁$畻鎸囧畾澶╂暟鍚庣殑鏃堕棿 + futureTime := zeroTime.Add(duration) + + // 杩斿洖鏃堕棿鎴筹紙绉掞級 + return futureTime.Unix() +} + +// 鍑忓幓鎸囧畾澶╂暟鍚庣殑鏃ユ湡 +func subtractDays(days int) time.Time { + // 鑾峰彇褰撳墠鏃堕棿 + now := time.Now() + + // 鍑忓幓鎸囧畾澶╂暟 + previousDays := now.AddDate(0, 0, -days) + + return previousDays +} + +// 鍒ゆ柇缁欏畾鏃ユ湡鏄惁鏄懆鏈� +func isWeekend(date time.Time) bool { + dayOfWeek := date.Weekday() + return dayOfWeek == time.Saturday || dayOfWeek == time.Sunday +} + +// 鑾峰彇涓棩鏈熷唴鎬诲ぉ鏁板搴旂殑绱㈠紩 +func getIndexForDate(days int) map[string]int { + startDate := subtractDays(days) + endDate := subtractDays(1) + arraySize := days + layout := "2006-01-02" + dateTOIndex := make(map[string]int) + for i := 0; i <= int(endDate.Sub(startDate).Hours()/24); i++ { + date := startDate.AddDate(0, 0, i) + offsetDays := int(date.Sub(startDate).Hours() / 24) + arrayIndex := offsetDays % arraySize + dateTOIndex[date.Format(layout)] = arrayIndex + } + + return dateTOIndex +} + +// 鑾峰彇涓�澶╀腑姣忓垎閽熺殑绱㈠紩浣嶇疆鏄犲皠 +func getIndexForTime(arraySize int) map[string]int { + timeTOIndex := make(map[string]int) + + for i := 0; i < arraySize; i++ { + hour := i / 60 + minute := i % 60 + timeStr := fmt.Sprintf("%02d:%02d", hour, minute) + timeTOIndex[timeStr] = i + } + + return timeTOIndex +} diff --git a/rule/service.go b/rule/service.go new file mode 100644 index 0000000..4de8888 --- /dev/null +++ b/rule/service.go @@ -0,0 +1,147 @@ +package rule + +import ( + "basic.com/valib/logger.git" + "fmt" + "ruleModelEngine/data" + "ruleModelEngine/db" +) + +//func PrintFilteredPersonnelInfo(filteredPersonnelInfo []db.PersonnelInfo) { +// for _, pi := range filteredPersonnelInfo { +// fmt.Printf("DocumentNumber: %s\n", pi.DocumentNumber) +// fmt.Printf("TotalCaptureCount: %d\n", pi.TotalCaptureCount) +// fmt.Printf("TotalCaptureDays: %d\n", pi.TotalCaptureDays) +// fmt.Printf("PersonnelStatus: %s\n", pi.PersonnelStatus) +// +// fmt.Println("CaptureDetails:") +// for _, cd := range pi.CaptureDetails { +// fmt.Printf(" Date: %s\n", cd.Date) +// fmt.Printf(" TotalCaptureCount: %d\n", cd.TotalCaptureCount) +// fmt.Println(" Captures:") +// for _, c := range cd.Captures { +// fmt.Printf(" DataTime: %s\n", c.DataTime) +// fmt.Printf(" Location: %s\n", c.Location) +// } +// } +// fmt.Println() +// } +//} + +// 璁$畻甯哥敤鍦板潃 +//func assignFrequentAddress(personnelInfo []db.PersonnelInfo) []db.PersonnelInfo { +// for i := range personnelInfo { +// addressCounts := make(map[string]int) +// var maxAddress string +// maxCount := 0 +// for _, detail := range personnelInfo[i].CaptureDetails { +// for _, capture := range detail.Captures { +// addressCounts[capture.Location]++ +// if addressCounts[capture.Location] > maxCount { +// maxCount = addressCounts[capture.Location] +// maxAddress = capture.Location +// } +// } +// } +// personnelInfo[i].FrequentAddress = maxAddress +// } +// return personnelInfo +//} + +// 妫�鏌ヨ鍒欒〃涔﹀惁瀛樺湪寮傚父 +func checkRuleValidity(rules []db.PersonnelStatusRule) bool { + for i := 0; i < len(rules); i++ { + for j := i + 1; j < len(rules); j++ { + ruleI := rules[i] + ruleJ := rules[j] + //fmt.Println(ruleI.DetectionDaysStart,ruleI.DetectionDaysEnd) + //fmt.Println(ruleJ.DetectionDaysStart,ruleJ.DetectionDaysEnd) + if (ruleI.DetectionDaysStart <= ruleJ.DetectionDaysEnd && ruleI.DetectionDaysStart >= ruleJ.DetectionDaysStart) || + (ruleI.DetectionDaysEnd <= ruleJ.DetectionDaysEnd && ruleI.DetectionDaysEnd >= ruleJ.DetectionDaysStart) || + (ruleI.DetectionDaysStart <= ruleJ.DetectionDaysStart && ruleI.DetectionDaysEnd >= ruleJ.DetectionDaysEnd) { + return false + } + } + } + return true +} + +// 鎵ц绋嬪簭鍏ュ彛 +func ExecuteTask() { + ruleInfo, err := db.GetAllData() + if err != nil { + logger.Error("GetAllData Error", err) + } + fmt.Println("ruleInfo: ", ruleInfo) + communityIDs, err := db.GetCommunityIDs() + //fmt.Println("communityIDs:", communityIDs) + if err != nil { + logger.Error("GetCommunityIDs Error", err) + } + for _, communityID := range communityIDs { + //鏌ヨ绀惧尯鍐呬汉鍛樻。妗堬紝鏂逛究鏁版嵁鏇存柊 + personStatus, err := db.GetDBPersonStatusData(communityID) + if err != nil { + logger.Error("GetDBPersonStatusData Error", err) + } + labeManage, err := db.GetLabelManageIdentity(2) + if err != nil { + logger.Error("GetDBPersonStatusData Error", err) + } + //fmt.Println(labeManage) + //fmt.Println("personStatus: ", personStatus) + //fmt.Println("CcmmunityIDs: ", cmmunityID) + //鎸夌ぞ鍖篿d鏌ヨ杩戜竴涓湀es鏁版嵁 + captureInfos, err := db.Query1MDataByCommunityId(communityID) + //fmt.Println("captureInfos: ", captureInfos) + for i := range captureInfos { + captureDays := data.CalculateCaptureDays(captureInfos[i].CaptureDetail) + captureInfos[i].CaptureDays = captureDays + //fmt.Println("璇ヤ汉鍛樺嚭鐜板ぉ鏁颁负", captureInfos[i].CaptureDays) + captureInfos[i].Status = data.SetStatus(captureDays, ruleInfo) + data.SetFrequentAddress(&captureInfos[i]) + } + 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 "鏈嶅姟浜哄憳": + identity, attribute := CreateLinearModel(captureInfos, communityID, 2.68, identity.ValidDays) + errIdentity := db.UpdateDBPersonLabel(identity) + if errIdentity != nil { + logger.Error("UpdateDBPersonLabel Error", errIdentity) + } + captureInfos = attribute + + } + } + //CreateProcessModel(captureInfos, 30) + //continue + //fmt.Println("captureInfos: ", captureInfos) + postCaptureInfos := data.ProcessData(captureInfos, personStatus, ruleInfo, communityID) + //fmt.Println("postCaptureInfos: ", postCaptureInfos) + //fmt.Println("鍏辫繃婊ゆ潯鏁帮細", len(captureInfos)-len(postCaptureInfos)) + UpdatePersonInfoErr := db.UpdatePersonInfo(postCaptureInfos, communityID) + if UpdatePersonInfoErr != nil { + logger.Error("MatchPermanentResidentTargets Error: ", UpdatePersonInfoErr) + } + + resident, DocNumberErr := db.GetResidentData("resident", communityID) + if DocNumberErr != nil { + logger.Error("GetDocNumberFromPersonStatus Error: ", DocNumberErr) + } + //fmt.Println(resident) + mio := processResidentStatus(resident) + //fmt.Println("mip: ", mio) + UpdateMoveInoutErr := db.UpdateMoveInout(mio) + if UpdateMoveInoutErr != nil { + logger.Error("UpdateMoveInoutErr Error: ", UpdateMoveInoutErr) + } + } + +} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..de40e86 --- /dev/null +++ b/util/util.go @@ -0,0 +1,285 @@ +package util + +import ( + "basic.com/valib/logger.git" + "bytes" + "encoding/json" + "errors" + "fmt" + "math" + "os/exec" + "strconv" + "time" +) + +// 鑴氭湰灏佽 +func RunScript(str string) string { + cmd := exec.Command("sh", "-c", str) + var out bytes.Buffer + cmd.Stdout = &out + err := cmd.Run() + if err != nil { + return "杩愯澶辫触" + } + return out.String() +} + +func Sourcelist(buf []byte) (sources []map[string]interface{}, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return nil, errors.New("http response interface can not change map[string]interface{}") + } + + middle, ok := out["hits"].(map[string]interface{}) + if !ok { + return nil, errors.New("first hits change error!") + } + for _, in := range middle["hits"].([]interface{}) { + tmpbuf, ok := in.(map[string]interface{}) + if !ok { + fmt.Println("change to source error!") + continue + } + source, ok := tmpbuf["_source"].(map[string]interface{}) + if !ok { + fmt.Println("change _source error!") + continue + } + sources = append(sources, source) + } + return sources, nil +} + +func SourceTotal(buf []byte) (total int, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return -1, errors.New("http response interface can not change map[string]interface{}") + } + + middle, ok := out["hits"].(map[string]interface{}) + if !ok { + return -1, errors.New("first total change error!") + } + + tmp, b := middle["total"].(map[string]interface{}) + if b != true { + v := middle["total"].(float64) + t := int(v) + return t, nil + } + value := tmp["value"].(float64) + total = int(value) + return total, nil +} + +func SourceCreated(buf []byte) (result bool, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return false, errors.New("http response interface can not change map[string]interface{}") + } + + middle, ok := out["result"].(string) + if !ok { + return false, errors.New("first total change error!") + } + if middle == "created" || middle == "updated" { + result = true + } + return result, nil +} + +func SourceDeleted(buf []byte) (total int, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return -1, errors.New("http response interface can not change map[string]interface{}") + } + + middle, ok := out["deleted"].(float64) + if !ok { + return -1, errors.New("first total change error!") + } + total = int(middle) + return total, nil +} + +func SourceUpdated(buf []byte) (total int, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return -1, errors.New("http response interface can not change map[string]interface{}") + } + //fmt.Println(out) + middle, ok := out["updated"].(float64) + if !ok { + return -1, errors.New("first total change error!") + } + total = int(middle) + return total, nil +} + +func SourceAggregationList(buf []byte) (sources []map[string]interface{}, err error) { + var info interface{} + json.Unmarshal(buf, &info) + out, ok := info.(map[string]interface{}) + if !ok { + return nil, errors.New("http response interface can not change map[string]interface{}") + } + + middle, ok := out["aggregations"].(map[string]interface{}) + if !ok { + return nil, errors.New("first hits change error!") + } + + documentAggregations := middle["group_by_documentnumber"].(map[string]interface{}) + buckets := documentAggregations["buckets"].([]interface{}) + if len(buckets) == 0 { + return nil, nil + } + for _, in := range buckets { + tmpbuf, ok := in.(map[string]interface{}) + if !ok { + return nil, errors.New("") + } + sources = append(sources, tmpbuf) + } + return sources, nil +} + +func StartTimer(f func()) { + for { + f() + now := time.Now() + // 璁$畻涓嬩竴涓浂鐐� + next := now.Add(time.Hour * 24) + next = time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()) + t := time.NewTimer(next.Sub(now)) + <-t.C + t.Stop() + } +} + +// 鏃ユ湡鎹㈢畻宸ュ叿 +func GetTimeArr(earliestDate string) int { + now := time.Now().Format("2006-01-02") + fmt.Println(earliestDate, now) + a, _ := time.Parse("2006-01-02", earliestDate) + b, _ := time.Parse("2006-01-02", now) + d := a.Sub(b) + date := d.Hours() / 24 + return int(math.Abs(date)) +} + +// 鏃ユ湡閬嶅巻宸ュ叿 +// GetBetweenDates 鏍规嵁寮�濮嬫棩鏈熷拰缁撴潫鏃ユ湡璁$畻鍑烘椂闂存鍐呮墍鏈夋棩鏈� +// 鍙傛暟涓烘棩鏈熸牸寮忥紝濡傦細2020-01-01 +func GetBetweenDates(sdate, edate string) []string { + d := []string{} + timeFormatTpl := "2006-01-02" + if len(timeFormatTpl) != len(sdate) { + timeFormatTpl = timeFormatTpl[0:len(sdate)] + } + date, err := time.Parse(timeFormatTpl, sdate) + if err != nil { + // 鏃堕棿瑙f瀽锛屽紓甯� + return d + } + date2, err := time.Parse(timeFormatTpl, edate) + if err != nil { + // 鏃堕棿瑙f瀽锛屽紓甯� + return d + } + if date2.Before(date) { + // 濡傛灉缁撴潫鏃堕棿灏忎簬寮�濮嬫椂闂达紝寮傚父 + return d + } + // 杈撳嚭鏃ユ湡鏍煎紡鍥哄畾 + timeFormatTpl = "2006-01-02" + date2Str := date2.Format(timeFormatTpl) + d = append(d, date.Format(timeFormatTpl)) + for { + date = date.AddDate(0, 0, 1) + dateStr := date.Format(timeFormatTpl) + d = append(d, dateStr) + if dateStr == date2Str { + break + } + } + return d +} + +func IpIntToString(ipInt int) string { + ipSegs := make([]string, 4) + var len int = len(ipSegs) + buffer := bytes.NewBufferString("") + for i := 0; i < len; i++ { + tempInt := ipInt & 0xFF + ipSegs[len-i-1] = strconv.Itoa(tempInt) + + ipInt = ipInt >> 8 + } + for i := 0; i < len; i++ { + buffer.WriteString(ipSegs[i]) + if i < len-1 { + buffer.WriteString(".") + } + } + return buffer.String() +} + +// 璁$畻鏃堕棿闃堝�� +func CheckTimeDifference(timestampStr1 string, timestampStr2 string, intervalInMinutes int) bool { + layout := "2006-01-02 15:04:05" + timestampStr1 = timestampStr1[:19] + timestampStr2 = timestampStr2[:19] + // 灏嗗瓧绗︿覆瑙f瀽涓烘椂闂� + time1, err := time.Parse(layout, timestampStr1) + if err != nil { + fmt.Println("鏃堕棿瑙f瀽澶辫触:", err) + return false + } + time2, err := time.Parse(layout, timestampStr2) + if err != nil { + fmt.Println("鏃堕棿瑙f瀽澶辫触:", err) + return false + } + + // 璁$畻鏃堕棿宸� + diff := time2.Sub(time1) + + // 妫�鏌ユ椂闂村樊鏄惁灏忎簬绛変簬鎸囧畾鐨勯棿闅� + if diff.Minutes() <= float64(intervalInMinutes) { + return true + } else { + return false + } +} + +func CalculateDays(startDate string, endDate string) int { + layout := "2006-01-02" // 鏃ユ湡鏍煎紡 + startTime, err := time.Parse(layout, startDate) + if err != nil { + // 閿欒澶勭悊 + logger.Error("Error parsing start date:", err) + return 0 + } + + endTime, err := time.Parse(layout, endDate) + if err != nil { + // 閿欒澶勭悊 + logger.Error("Error parsing end date:", err) + return 0 + } + + duration := endTime.Sub(startTime) + days := int(duration.Hours() / 24) // 灏嗘椂闂村樊杞崲涓哄ぉ鏁� + return days +} -- Gitblit v1.8.0