From 3c033759200ad7c02dd59521b1aebbbdc35b98fa Mon Sep 17 00:00:00 2001
From: zhaoqingang <zhaoqg0118@163.com>
Date: 星期六, 08 二月 2025 16:18:50 +0800
Subject: [PATCH] 长场景人员分析模型

---
 models/locationAnalysis.go |  467 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 445 insertions(+), 22 deletions(-)

diff --git a/models/locationAnalysis.go b/models/locationAnalysis.go
index e9fbc23..03982a7 100644
--- a/models/locationAnalysis.go
+++ b/models/locationAnalysis.go
@@ -1,7 +1,17 @@
 package models
 
 import (
+	"bytes"
+	"context"
+	"encoding/json"
+	"errors"
 	"fmt"
+	"github.com/elastic/go-elasticsearch/v6"
+	//"log"
+	"model-engine/config"
+	"model-engine/pkg/set"
+	"strconv"
+	"strings"
 	"time"
 
 	"model-engine/db"
@@ -10,36 +20,46 @@
 )
 
 type LocationModel struct {
-	AreaIds       map[string]struct{}
-	Building      string       // 妤兼爧
-	AlarmType     db.AlarmType // 棰勮鏂瑰紡
-	KeyPersonType string       // 浜哄憳绫诲瀷
-	PersonLabel   string
-	PersonCount   int // 浜烘暟, 鍑虹幇鐨勫悓绫诲瀷浜哄憳鏁伴噺
-	Appearances   int // 鍑虹幇娆℃暟,
-	Duration      int // 鏃堕棿鑼冨洿, 鍗曚綅澶�
-	Task          *db.ModelTask
+	AreaIds        []interface{} `json:"-"`
+	OrgIds         []interface{} `json:"-"`
+	Building       string        // 妤兼爧
+	Floor          string
+	AlarmType      db.AlarmType // 棰勮鏂瑰紡
+	KeyPersonType  string       // 浜哄憳绫诲瀷
+	PersonLabel    string       // 浜哄憳鏍囩
+	PersonIdentity []string     // 浜哄憳韬唤
+	Duration       int          // 鏃堕棿鑼冨洿
+	Appearances    int          // 鍑虹幇娆℃暟,
+	StartTime      int          // 鏃堕棿鑼冨洿, 寮�濮嬫椂闂�
+	EndTime        int          // 鏃堕棿鑼冨洿, 缁撴潫鏃堕棿
+	Task           *db.ModelTask
 }
 
 func (m *LocationModel) Init(task *db.ModelTask) error {
-	m.AreaIds = make(map[string]struct{})
-	for _, a := range task.DomainUnitIds {
-		m.AreaIds[a] = struct{}{}
+	//m.AreaIds = make(map[string]struct{})
+	//for _, a := range task.DomainUnitIds {
+	//	m.AreaIds[a] = struct{}{}
+	//}
+	if len(task.DomainUnitIds) == 0 {
+		return errors.New("empty domain set")
+	}
+	orgIds, areaIds, err := service.GetOrgIdsAndAreaIdsByDomainUnitIds(task.DomainUnitIds)
+	if err != nil {
+		return err
 	}
 
 	m.Task = task
+	m.OrgIds = orgIds
+	m.AreaIds = areaIds
 	m.Building = task.Building
 	m.AlarmType = task.AlarmType
+	m.PersonIdentity = []string{"stranger", "visitor", "resident"} //task.IdentityType
 	m.KeyPersonType = task.PersonType
 	m.PersonLabel = task.PersonLabel
-
+	if task.IdentityType != "" {
+		m.PersonIdentity = strings.Split(task.IdentityType, ",")
+	}
 	for _, v := range task.Rules {
-		if v.Alias == "personCount" {
-			if val, ok := v.Value.(float64); ok {
-				m.PersonCount = int(val)
-			}
-		}
-
 		if v.Alias == "appearances" {
 			if val, ok := v.Value.(float64); ok {
 				m.Appearances = int(val)
@@ -49,6 +69,13 @@
 		if v.Alias == "duration" {
 			if val, ok := v.Value.(float64); ok {
 				m.Duration = int(val)
+			}
+		}
+		if v.Alias == "timeRange" {
+			if val, ok := v.Value.(string); ok {
+				ages := strings.Split(val, ",")
+				m.StartTime, _ = strconv.Atoi(ages[0])
+				m.EndTime, _ = strconv.Atoi(ages[1])
 			}
 		}
 	}
@@ -63,11 +90,173 @@
 	return nil
 }
 
+type LocationRecord struct {
+	IDCard          string `json:"idCard"`
+	PicDate         string `json:"picDate"`
+	DocumentNumbers []string
+	CommunityId     string `json:"communityId"`
+	OrgId           string `json:"orgId"`
+	Building        string `json:"building"`
+	Floor           string `json:"floor"`
+	AppearCount     int    `gorm:"type:int;" json:"appearCount"` // 鍑虹幇娆℃暟
+	//AppearInterval int    `gorm:"type:int;" json:"appearInterval"` // 鍑虹幇闂撮殧锛屽崟浣嶄负绉�
+}
+
+type LocationPersonInfo struct {
+	Id             string `json:"id"`
+	DocumentNumber string `json:"document_number"`
+	PersonType     string `json:"person_type"`
+	//CommunityId        string `json:"community_id"`
+	//OrgId              string `json:"org_id"`
+	//PersonName         string `json:"person_name"`
+	//IdCard             string `json:"id_card"`
+	LastAppearanceTime int64 `json:"last_appearance_time"`
+	//LastDirection      string `json:"last_direction"`
+	//LastLocation       string `json:"last_location"`
+}
+
+//var (
+//	processed        sync.Map                           // 瀛樺偍宸插鐞嗚褰�
+//	cleanupThreshold = time.Now().Add(-100 * time.Hour) // 瀹氫箟涓�涓椂闂寸獥鍙o紝鍋囪鍙繚瀛樻渶杩�100灏忔椂鐨勮褰�
+//)
+
 func (m *LocationModel) Run() error {
-	// 鏍规嵁閰嶇疆鐨勬椂闂存澶╂暟, 杩囨护瑙勫垯閰嶇疆鐨勭浉鍚岄噸鐐逛汉鍛樼被鍨嬪拰鐩稿悓鏍囩浜哄憳棰戠箒鍑虹幇鐨勬ゼ灞傚湴鍧�
+	// 鏍规嵁閰嶇疆鐨勬椂闂存澶╂暟, 姣忓ぉ鐨勬椂闂磋寖鍥村唴锛� 閲嶇偣浜哄憳绫诲瀷鎴栬�呯壒瀹氭爣绛句汉鍛樺嚭鐜扮殑妤煎眰娆℃暟瓒呰繃闃堝��
 
 	results := make([]*db.ModelTaskResults, 0)
+	var baseFilter, labelFilter, keyFilter, lastFilter []LocationPersonInfo
+	var document_number_map map[string]LocationPersonInfo
+	var document_number_list []string
+	err := db.GetDB().Raw(`
+		SELECT
+			s.document_number,
+-- 			s.community_id,
+-- 			s.org_id,
+-- 			p.person_name,
+-- 			p.id_card,
+			s.last_appearance_time,
+-- 			s.last_direction,
+-- 			s.last_location 
+		FROM
+			snapshot_count_summary AS s
+			JOIN person AS p ON p.id = s.document_number
+		WHERE
+			p.id_card != "" 
+			AND (p.community_id IN ?
+			OR p.org_id IN ?)
+			AND p.status IN ?
+		`, m.AreaIds, m.OrgIds, m.PersonIdentity).Scan(&baseFilter).Error
+	if err != nil {
+		logger.Warnf(err.Error())
+	}
 
+	if len(baseFilter) == 0 {
+		return fmt.Errorf("no results found that match the age condition %s - %s ", m.AreaIds, m.OrgIds)
+	}
+
+	logger.Debugf("task %s match age result %d", m.Task.Name, len(baseFilter))
+	for _, i := range baseFilter {
+		if _, ok := document_number_map[i.DocumentNumber]; !ok {
+			document_number_list = append(document_number_list, i.DocumentNumber)
+		}
+		document_number_map[i.DocumentNumber] = i
+	}
+	if m.PersonLabel != "" {
+		labels := strings.Split(m.PersonLabel, ",")
+		err := db.GetDB().Raw(`
+		SELECT
+			p.id
+		FROM
+			person AS p
+			JOIN person_label AS l ON p.id = l.person_id 
+		WHERE
+			p.id IN ?
+			AND	l.label_id IN ?
+		`, document_number_list, labels).Scan(&labelFilter).Error
+		if err != nil {
+			logger.Warnf(err.Error())
+		}
+
+		if len(labelFilter) == 0 {
+			return fmt.Errorf("no results found that match the label condition %s", m.PersonLabel)
+		}
+
+		logger.Debugf("task %s match label result %d", m.Task.Name, len(labelFilter))
+	}
+
+	document_number_list = []string{}
+	for _, i := range labelFilter {
+
+		document_number_list = append(document_number_list, i.Id)
+
+	}
+
+	if m.KeyPersonType != "" {
+		keyTypes := strings.Split(m.KeyPersonType, ",")
+		err := db.GetDB().Raw(`
+		SELECT
+			p.id,
+			k.person_type
+		FROM
+			person AS p
+			JOIN key_person AS k ON k.id_card = p.id_card 
+		WHERE
+			p.id IN ?
+			AND k.person_type IN ?
+		`, m.StartTime, keyTypes).Scan(&keyFilter).Error
+		if err != nil {
+			logger.Warnf(err.Error())
+		}
+		if len(keyFilter) == 0 {
+			return fmt.Errorf("no results found that match the key condition %s", m.KeyPersonType)
+		}
+
+		logger.Debugf("task %s match key person result %d", m.Task.Name, len(keyFilter))
+	}
+
+	logger.Debugf("task %s last result %d", m.Task.Name, len(lastFilter))
+	document_number_list = []string{}
+	for _, i := range keyFilter {
+		document_number_list = append(document_number_list, i.Id)
+		person := document_number_map[i.DocumentNumber]
+		person.PersonType = i.PersonType
+		document_number_map[i.DocumentNumber] = person
+	}
+	records, err := queryEsLocation(db.GetEsClient(), m, document_number_list)
+	if err != nil {
+		return err
+	}
+	domains, err := domainToLocation(records)
+	if err != nil {
+		return err
+	}
+	var tagTypes []string
+	var lastAppearanceTime int64
+	for _, record := range records {
+		tagTypes = []string{}
+		for _, personId := range record.DocumentNumbers {
+			tagTypes = append(tagTypes, document_number_map[personId].PersonType)
+			lastAppearanceTime = document_number_map[personId].LastAppearanceTime
+		}
+		_, typeNames, err := service.GetPersonTypeNameByTypes(tagTypes)
+		if err != nil {
+			return err
+		}
+		event := strings.Join(typeNames, ",")
+		result := &db.ModelTaskResults{
+			Title:         m.Task.Name,
+			Event:         m.eventFormat(event, record.AppearCount),
+			ModelID:       m.Task.ModelID,
+			ModelTaskID:   m.Task.ID,
+			CommunityId:   record.CommunityId,
+			OrgID:         record.OrgId,
+			ObjectIds:     strings.Join(record.DocumentNumbers, ","),
+			Location:      fmt.Sprintf("%s%s%s", domains[record.CommunityId].Name, record.Building, record.Floor),
+			PicDate:       time.Unix(lastAppearanceTime, 0).Format("2006-01-02 15:04:05"),
+			FirstPersonID: record.DocumentNumbers[0],
+		}
+		results = append(results, result)
+	}
 	logger.Debugf("task %s last filter result %d", m.Task.Name, len(results))
 	return service.SaveTaskResults(results)
 }
@@ -83,6 +272,240 @@
 	return nil
 }
 
-func (m *LocationModel) eventFormat() string {
-	return ""
+func (m *LocationModel) eventFormat(event string, AppearCount int) string {
+	return fmt.Sprintf("%s浜哄憳杩涘嚭%d娆�", event, AppearCount)
+}
+
+func queryEsLocation(esClient *elasticsearch.Client, locationModel *LocationModel, documentNumbers []string) ([]*LocationRecord, error) {
+	var buf bytes.Buffer
+	nowTime := time.Now()
+	startTime := nowTime.Add(-time.Duration(locationModel.Duration) * 24 * time.Hour)
+
+	// 鏋勫缓杩囨护鏉′欢
+	var filters []map[string]interface{}
+	documentNumberFilter := map[string]interface{}{
+		"terms": map[string]interface{}{
+			"documentNumber": documentNumbers,
+		},
+	}
+	filters = append(filters, documentNumberFilter)
+
+	if len(locationModel.OrgIds) > 0 || len(locationModel.AreaIds) > 0 {
+		// 鑾峰彇鏁版嵁鏉冮檺杩囨护鏉′欢
+		authFilters := GetDomainFilters(locationModel.OrgIds, locationModel.AreaIds)
+		filters = append(filters, authFilters...)
+	}
+
+	// 鍦板潃杩囨护
+	if locationModel.Building != "" || locationModel.Floor != "" {
+		var addrParams map[string]interface{}
+		if locationModel.Floor != "" {
+			addrParams = map[string]interface{}{"bool": map[string]interface{}{
+				"must": []interface{}{
+					map[string]interface{}{
+						"term": map[string]interface{}{
+							"cameraLocation.building": locationModel.Building,
+						}},
+					map[string]interface{}{
+						"term": map[string]interface{}{
+							"cameraLocation.floor": locationModel.Floor,
+						}},
+				},
+			}}
+		} else if locationModel.Building != "" {
+			addrParams = map[string]interface{}{
+				"term": map[string]interface{}{
+					"cameraLocation.building": locationModel.Building,
+				}}
+		}
+		filters = append(filters, addrParams)
+	}
+
+	//// 閲嶇偣浜哄憳杩囨护
+	//if len(locationModel.KeyPersonType) > 0 {
+	//	filters = append(filters, map[string]interface{}{
+	//		"terms": map[string]interface{}{
+	//			"keyPersonType": strings.Split(locationModel.KeyPersonType, ","),
+	//		},
+	//	})
+	//}
+
+	// 鏃堕棿鑼冨洿
+	//filters = append(filters, map[string]interface{}{
+	//	"range": map[string]interface{}{
+	//		"picDate": map[string]interface{}{
+	//			"gte": start.Format(time.DateTime),
+	//			"lt":  now.Format(time.DateTime),
+	//		},
+	//	},
+	//})
+	for date := startTime; date.Before(nowTime); date = date.Add(24 * time.Hour) {
+		start := time.Date(date.Year(), date.Month(), date.Day(), locationModel.StartTime, 0, 0, 0, date.Location())
+		end := time.Date(date.Year(), date.Month(), date.Day(), locationModel.EndTime, 0, 0, 0, date.Location())
+
+		filters = append(filters, map[string]interface{}{
+			"range": map[string]interface{}{
+				"picDate": map[string]interface{}{
+					"gte": start.Format(time.RFC3339),
+					"lte": end.Format(time.RFC3339),
+				},
+			},
+		})
+	}
+
+	query := map[string]interface{}{
+		"query": map[string]interface{}{
+			"bool": map[string]interface{}{
+				"filter": filters,
+			},
+		},
+		"aggs": map[string]interface{}{
+			"orgs": map[string]interface{}{ // 鍏堣仛鍚坥rgId
+				"terms": map[string]interface{}{
+					"field": "orgId", // 鑱氬悎orgId
+					"size":  10000,
+				},
+				"aggs": map[string]interface{}{
+					"community": map[string]interface{}{ // 鍦╫rgId鑱氬悎涓嬭仛鍚坈ommunityId
+						"terms": map[string]interface{}{
+							"field": "communityId", // 鑱氬悎communityId
+							"size":  10000,
+						},
+						"aggs": map[string]interface{}{
+							"location": map[string]interface{}{ // 鍦╟ommunityId涓嬭仛鍚坆uilding
+								"terms": map[string]interface{}{
+									"field": "cameraLocation.building", // 鑱氬悎妤兼爧
+									"size":  10000,
+								},
+								"aggs": map[string]interface{}{
+									"floor": map[string]interface{}{ // 鍦╞uilding涓嬭仛鍚坒loor
+										"terms": map[string]interface{}{
+											"field": "cameraLocation.floor", // 鑱氬悎妤煎眰
+											"size":  10000,
+										},
+										"aggs": map[string]interface{}{
+											"filter_floor": map[string]interface{}{
+												"bucket_selector": map[string]interface{}{
+													"buckets_path": map[string]interface{}{
+														"eventCount": "_count",
+													},
+													"script": map[string]interface{}{
+														"source": "params.eventCount >= params.threshold",
+														"params": map[string]interface{}{
+															"threshold": locationModel.Appearances,
+														},
+													},
+												},
+											},
+											"document_numbers": map[string]interface{}{ // 鏂板鎸� documentNumber 鑱氬悎
+												"terms": map[string]interface{}{
+													"field": "documentNumber",
+													"size":  10000,
+												},
+											},
+										},
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+		"size": 0,
+	}
+
+	if err := json.NewEncoder(&buf).Encode(query); err != nil {
+		return nil, fmt.Errorf("error encoding query: %s", err)
+	}
+
+	res, err := esClient.Search(
+		esClient.Search.WithContext(context.Background()),
+		esClient.Search.WithIndex(config.EsInfo.EsIndex.AiOcean.IndexName),
+		esClient.Search.WithDocumentType(config.EsInfo.EsIndex.AiOcean.IndexType),
+		esClient.Search.WithBody(&buf),
+		esClient.Search.WithTrackTotalHits(true),
+		esClient.Search.WithPretty(),
+	)
+	if err != nil {
+		return nil, fmt.Errorf("error getting response: %s", err)
+	}
+	defer res.Body.Close()
+
+	// Check for a successful status code (2xx range)
+	if res.IsError() {
+		return nil, fmt.Errorf("error getting response: %s", res.String())
+	}
+
+	var result map[string]interface{}
+	if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
+		return nil, fmt.Errorf("error parsing response body: %s", err)
+	}
+
+	// 瑙f瀽鑱氬悎缁撴灉
+	var records []*LocationRecord
+	if aggs, ok := result["aggregations"].(map[string]interface{}); ok {
+		if orgBuckets, ok := aggs["orgs"].(map[string]interface{})["buckets"].([]interface{}); ok {
+			for _, orgBucket := range orgBuckets {
+				orgId := orgBucket.(map[string]interface{})["key"].(string)
+
+				// 瑙f瀽鎸塩ommunityId鐨勮仛鍚堢粨鏋�
+				if communityBuckets, ok := orgBucket.(map[string]interface{})["community"].(map[string]interface{})["buckets"].([]interface{}); ok {
+					for _, communityBucket := range communityBuckets {
+						communityId := communityBucket.(map[string]interface{})["key"].(string)
+
+						// 瑙f瀽鎸塨uilding鐨勮仛鍚堢粨鏋�
+						if locationBuckets, ok := communityBucket.(map[string]interface{})["location"].(map[string]interface{})["buckets"].([]interface{}); ok {
+							for _, locationBucket := range locationBuckets {
+								building := locationBucket.(map[string]interface{})["key"].(string)
+
+								// 瑙f瀽鎸塮loor鐨勮仛鍚堢粨鏋�
+								if floorBuckets, ok := locationBucket.(map[string]interface{})["floor"].(map[string]interface{})["buckets"].([]interface{}); ok {
+									for _, floorBucket := range floorBuckets {
+										floor := floorBucket.(map[string]interface{})["key"].(string)
+										appearCount := floorBucket.(map[string]interface{})["filter_floor"].(int)
+										// 鏋勫缓 LocationRecord 缁撴瀯浣�
+										var persons []string
+										if docNumBuckets, ok := floorBucket.(map[string]interface{})["document_numbers"].(map[string]interface{})["buckets"].([]interface{}); ok {
+											for _, docNumBucket := range docNumBuckets {
+												persons = append(persons, docNumBucket.(map[string]interface{})["key"].(string))
+											}
+										}
+										record := &LocationRecord{
+											//PicDate:        timestamp,
+											DocumentNumbers: persons,
+											CommunityId:     communityId,
+											Building:        building,
+											Floor:           floor,
+											OrgId:           orgId,
+											AppearCount:     appearCount,
+										}
+
+										records = append(records, record)
+									}
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return records, nil
+}
+
+func domainToLocation(records []*LocationRecord) (map[string]*db.DomainUnit, error) {
+	if len(records) == 0 {
+		return nil, nil
+	}
+	domainIds := set.NewStringSet()
+	for _, record := range records {
+		domainIds.Add(record.CommunityId)
+	}
+	domains, err := service.GetUnitsMapByIds(domainIds.Elements())
+	if err != nil {
+		return nil, err
+	}
+	return domains, nil
 }

--
Gitblit v1.8.0