zhaoqingang
2025-02-19 fca319958029fa924308e50cb61202d7d6ff5008
models/accessRegularity.go
@@ -15,16 +15,17 @@
type RegularityModel struct {
   OrgIds        []interface{} `json:"-"`
   AreaIds       []interface{} `json:"-"`
   IdentityType  string        // 人员身份类型 陌生人, 访客, 住户
   IdentityType  []string      // 人员身份类型 陌生人, 访客, 住户
   KeyPersonType string        // 重点人员类型
   PersonLabel   string        // 人员身份标签
   Task          *db.ModelTask
   AlarmType     db.AlarmType // 预警方式
   Appearances int // 出现次数
   Duration    int // 时间范围, 单位天
   StartHour   int // 开始计算的时间 配置为小时 23 - 02 表示第一天23点 - 第二天的2点
   EndHour     int // 结束时间
   Appearances   int    // 出现次数
   Duration      int    // 时间范围, 单位天
   LastDirection string // 最后一次出行的方向
   StartHour     int    // 开始计算的时间 配置为小时 23 - 02 表示第一天23点 - 第二天的2点
   EndHour       int    // 结束时间
}
func (m *RegularityModel) Init(task *db.ModelTask) error {
@@ -42,7 +43,19 @@
   m.AlarmType = task.AlarmType
   m.KeyPersonType = task.PersonType
   m.PersonLabel = task.PersonLabel
   m.IdentityType = task.IdentityType
   m.LastDirection = "out"
   if task.IdentityType != "" {
      for _, t := range strings.Split(task.IdentityType, ",") {
         if t == "all" {
            m.IdentityType = []string{"stranger", "visitor", "resident"}
            break
         } else {
            m.IdentityType = append(m.IdentityType, t)
         }
      }
   } else {
      m.IdentityType = []string{"stranger", "visitor", "resident"}
   }
   for _, v := range task.Rules {
      if v.Alias == "timeRange" {
@@ -64,6 +77,12 @@
            m.Duration = int(val)
         }
      }
      if v.Alias == "direction" {
         if val, ok := v.Value.(string); ok {
            m.LastDirection = val
         }
      }
   }
   // 默认计算30天的数据
@@ -75,13 +94,74 @@
      return fmt.Errorf("task id:%s, %s timeRange Time range setting error. %+v", task.ID, task.Name, task.Rules)
   }
   logger.Debugf("LocationModel init finish ...task id:%s, name:%s, rule:%+v", task.ID, task.Name, m)
   logger.Debugf("AccessRegularity init finish ...task id:%s, name:%s, rule:%+v", task.ID, task.Name, m)
   return nil
}
func (m *RegularityModel) Run() error {
   results := make([]*db.ModelTaskResults, 0)
   baseFilter := make([]PersonInfo, 0)
   // 查找指定时间范围内出行过的档案编号
   now := time.Now()
   startDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, -m.Duration)
   endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
   err := db.GetDB().Raw(`
      SELECT
         document_number,
         frequent_address,
         community_id,
         org_id
      FROM
         snapshot_count_summary
      WHERE
         last_appearance_time > ?
         AND (community_id IN ?
         OR org_id IN ?)
         AND status IN ?
      `, startDate.Unix(), m.AreaIds, m.OrgIds, m.IdentityType).Scan(&baseFilter).Error
   if err != nil {
      logger.Warnf(err.Error())
   }
   logger.Debugf("task %s base filter result %d", m.Task.Name, len(baseFilter))
   esCli := db.GetEsClient()
   // 调用es分析此人的出行规律是否符合条件, 返回符合条件的次数和最后一次符合条件的时间
   sTime := startDate.Format(time.DateTime)
   eTime := endDate.Format(time.DateTime)
   for _, p := range baseFilter {
      captures, err := service.QueryEsRecord(esCli, sTime, eTime, nil, []interface{}{p.CommunityId}, []string{p.DocumentNumber})
      //logger.Debugf("task %s person %s captures %d", m.Task.Name, p.DocumentNumber, len(captures))
      if len(captures) == 0 || err != nil {
         continue
      }
      //logger.Debugf("task %s person %s captures %+v", m.Task.Name, p.DocumentNumber, captures[0])
      // 根据抓拍时间和出入方向,计算符合规则内的出入次数
      hitCount, pd := countValidDays(captures, m.StartHour, m.EndHour, m.LastDirection)
      if hitCount > m.Appearances {
         // 写数据库
         result := &db.ModelTaskResults{
            Title:         m.Task.Name,
            Event:         fmt.Sprintf("%s - %s 时间段内, %s %d次", sTime, eTime, m.Task.Name, hitCount),
            ModelID:       m.Task.ModelID,
            ModelTaskID:   m.Task.ID,
            CommunityId:   p.CommunityId,
            OrgID:         p.OrgId,
            ObjectIds:     p.DocumentNumber,
            Location:      p.FrequentAddress,
            PicDate:       pd,
            FirstPersonID: p.DocumentNumber,
         }
         results = append(results, result)
      }
   }
   logger.Debugf("task %s last filter result %d", m.Task.Name, len(results))
   return service.SaveTaskResults(results)
@@ -94,10 +174,71 @@
func (m *RegularityModel) Shutdown() error {
   // 清理资源
   fmt.Println("Shutting down LocationModel Model")
   fmt.Println("Shutting down accessRegularityS Model")
   return nil
}
func (m *RegularityModel) eventFormat() string {
   return ""
}
func countValidDays(records []*service.ESRecordInfo, startHour, endHour int, direction string) (int, string) {
   layout := "2006-01-02 15:04:05"             // 时间格式
   lastDirectionMap := make(map[string]string) // 记录最后一条 Direction
   lastTimeMap := make(map[string]time.Time)   // 记录最后一条时间
   lastPicDate := ""
   // 判断是否跨天
   var isCrossDay bool
   if endHour < startHour {
      isCrossDay = true
   }
   for _, record := range records {
      // 解析时间
      t, err := time.ParseInLocation(layout, record.PicDate, time.Local)
      if err != nil {
         fmt.Println("解析时间失败:", err)
         continue
      }
      // 获取小时
      hour := t.Hour()
      var key string
      if !isCrossDay {
         // 判断时间范围,并归属到某一天
         if hour >= startHour && hour < endHour {
            key = t.Format("2006-01-02")
         } else {
            continue // 不在统计范围内
         }
      } else {
         // 判断时间范围,并归属到某一天
         if hour >= startHour { // 21:00-23:59 归属当天
            key = t.Format("2006-01-02")
         } else if hour < endHour { // 00:00-02:59 归属前一天
            key = t.AddDate(0, 0, -1).Format("2006-01-02")
         } else {
            continue // 不在统计范围内
         }
      }
      // 记录该时间段内的最后一条数据
      if lastTime, exists := lastTimeMap[key]; !exists || t.After(lastTime) {
         lastTimeMap[key] = t
         lastDirectionMap[key] = record.CameraLocation.Direction
         lastPicDate = record.PicDate
      }
   }
   // 统计符合条件
   count := 0
   for _, dir := range lastDirectionMap {
      if dir == direction || direction == "" {
         count++
      }
   }
   return count, lastPicDate
}