sunty
2024-09-09 28c5cf41c5064579dd62f79fd40dc5990e9565b2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
package data
 
import (
    "fmt"
    "regexp"
    "ruleModelEngine/config"
    "ruleModelEngine/db"
    "strings"
    "time"
)
 
// 计算抓拍天数
func CalculateCaptureDays(details []db.CaptureDetail) (int, int) {
    // 使用 map 来存储每天是否有抓拍记录
    captureMap := make(map[string]bool)
    //pointDate := ""
    //pointTime := ""
    //pointDirection := ""
    overnightCount := 0
    for i, detail := range details {
        // 解析抓拍日期
        layout := "2006-01-02 15:04:05"
        captureTime, err := time.Parse(layout, detail.CaptureDate)
        if err != nil {
            fmt.Println("解析抓拍日期时出错:", err)
            continue
        }
        //fmt.Println(captureTime, detail.Direction)
        // 获取日期部分
        date := captureTime.Format("2006-01-02")
        //time := captureTime.Format("15:04:05")
        // 在 map 中标记这一天有抓拍记录
        captureMap[date] = true
        if i == len(details)-1 {
            break
        }
 
        // 第一个验证条件:当前为 in,时间在下午 16 点以后
        currTime, _ := time.Parse("2006-01-02 15:04:05", detail.CaptureDate)
        if detail.Direction == "in" && currTime.Hour() >= 16 {
            // 第二个验证条件:下一个为 out,时间在上午 5 点之后 12 点之前
            nextDetail := details[i+1]
            nextTime, _ := time.Parse("2006-01-02 15:04:05", nextDetail.CaptureDate)
            nextDay := nextTime.AddDate(0, 0, -1).Format("2006-01-02")
            if nextDetail.Direction == "out" && nextTime.Hour() >= 5 && nextTime.Hour() < 12 && nextDay == detail.CaptureDate[:10] {
                overnightCount++
            }
        }
    }
 
    // 统计有抓拍记录的天数
    captureDays := 0
    for range captureMap {
        captureDays++
    }
 
    return captureDays, overnightCount
}
 
// 设置状态
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
}
 
// 获取常用地址
func getfrAddress(pointAddress map[string]int) string {
    maxCount := 0
    maxCountAddress := ""
    re1F, _ := regexp.Compile(`\b1F\b`)
    reNoF, _ := regexp.Compile(`^[^F]*$`)
    reNegF := regexp.MustCompile(`-`)
    for address, count := range pointAddress {
        matchFlag1F := re1F.MatchString(address)
        matchFlagNoF := reNoF.MatchString(address)
        matchFlagNegF := reNegF.MatchString(address)
        if address == "" || matchFlag1F || matchFlagNoF || matchFlagNegF {
            continue
        }
        if count > maxCount {
            maxCount = count
            maxCountAddress = address
        }
    }
    return maxCountAddress
}
 
// SetFrequentAddress 方法计算出现最频繁的出行地址并设置为常用地址
func GetFrequentAddress(captureDetail []db.CaptureDetail) string {
    // 统计每个出行地址的出现次数
    //pointDate 靶标日期,pointAddress靶标地址
    pointDate := ""
    pointAddress := make(map[string]int, 0)
    //dateAddress 按日期汇总单日常用地址
    dateAddress := make(map[string]string, 0)
    //floor1Address 后备地址,为1楼地址
    floor1Address := ""
    //captureDays 按日期去重统计总抓拍天数
    captureDays := make(map[string]int, 0)
    //frequentAddress 最终设置的常用地址
    var frequentAddress string
    //正则 判断地址是否包含1F字样,完全匹配
    re1F, _ := regexp.Compile(`\b1F\b`)
    reNoF, _ := regexp.Compile(`^[^F]*$`)
    // 遍历所有抓拍数据
    for index, detail := range captureDetail {
        //fmt.Println("抓拍地址:", detail.CaptureAddress)
        //fmt.Println("抓拍日期:", detail.CaptureDate)
        //dateStr截取的日期,为YYYY-MM-DD
        dateStr := strings.Split(detail.CaptureDate, " ")[0]
        //去重统计抓拍天数 captureDays
        captureDays[dateStr]++
        //匹配是否是1F
        matchFlag1F := re1F.MatchString(detail.CaptureAddress)
        matchFlagNoF := reNoF.MatchString(detail.CaptureAddress)
        if matchFlag1F {
            //如果是, 提前设置好备用地址 floor1Address = detail.CaptureAddress
            floor1Address = detail.CaptureAddress
        }
        if matchFlagNoF && floor1Address == "" {
            //如果是, 提前设置好备用地址 floor1Address = detail.CaptureAddress
            floor1Address = detail.CaptureAddress
        }
        //fmt.Println("now floor1Address:", floor1Address,"\tnow detail CaptureAddress",detail.CaptureAddress)
        //判断日期标是否满足条件 判断日期标是否为空,若为空表示是第一个元素,初始化日期靶标
        if pointDate == "" {
            //初始化日期靶标为当前日期
            pointDate = dateStr
            //把当前日期的
            pointAddress[detail.CaptureAddress]++
            //若为最后一条则计算单日常用地址
            if index == len(captureDetail)-1 {
                //fmt.Println("处理前pointAddress表:", pointAddress)
                //这代表仅有一条数据,将会直接把当前地址加入dateAddress 日期汇总单日常用地址表内 下方continue会直接结束循环
                //否则会初始化pointDate和pointAddress
                dateAddress[pointDate] = detail.CaptureAddress
            }
            continue
        }
        //若日期标相等则为同一天
        if pointDate == dateStr {
            //pointAddress靶标日期数组对应楼层地址数+1
            pointAddress[detail.CaptureAddress]++
            //若为最后一条则计算单日常用地址
            if index == len(captureDetail)-1 {
                //fmt.Println("处理前pointAddress表:", pointAddress)
                //计算单日常用地址 若地址内都为1F或者空地址,会返回空地址
                dateAddress[pointDate] = getfrAddress(pointAddress)
                continue
            }
            //不相等则为新的日期,首先计算出当日的常用地址,然后重新初始化日期标和地址标数组
        } else {
            //若为最后一条则计算单日常用地址
            if index == len(captureDetail)-1 {
                //把当前最后一条加入    pointAddress靶标地址
                pointAddress[detail.CaptureAddress]++
                //
                //fmt.Println("处理前pointAddress表:", pointAddress)
                //计算单日常用地址 若地址内都为1F或者空地址,会返回空地址
                dateAddress[pointDate] = getfrAddress(pointAddress)
                continue
            }
            //不为最后一条表示这是新的一天的抓拍地址数据,需要计算之前一天的单日常用地址
            dateAddress[pointDate] = getfrAddress(pointAddress)
            //初始化新的一天的靶标日期和pointAddress 靶标地址数组
            pointDate = dateStr
            pointAddress = make(map[string]int, 0)
        }
    }
    //fmt.Println("抓拍地址待处理结构表:", dateAddress)
    //fmt.Println("dareAddress len: ", len(dateAddress))
    //判断抓拍的非1楼非空非地下的天数占比总天数
    if float64(len(dateAddress))/float64(len(captureDays))*100 >= float64(config.Api.CapAddrDaysThreshold) {
        //fmt.Println("非1楼非空抓拍占比: ", float64(len(dateAddress))/float64(len(captureDays))*100)
        //初始化地址列表,按地址计数
        addressCount := make(map[string]int)
        //遍历之前汇总的单日常用地址列表
        for _, address := range dateAddress {
            //对应地址数+1
            addressCount[address]++
        }
        //复制常用地址给最终结果,若地址内都为1F或者空地址,会返回空地址
        frequentAddress = getfrAddress(addressCount)
    } else {
        //若不满足非1楼非空抓拍占比,则直接把备用地址(1F)赋值给最终结果
        frequentAddress = floor1Address
    }
    //若存在满足非1楼非空抓拍占比,但是最终结果是空地址,则赋值备用地址(1F)为最终结果
    if frequentAddress == "" {
        frequentAddress = floor1Address
    }
    //fmt.Println("in address: ", inAddressCounts)
    //fmt.Println("out address: ", outAddressCounts)
    //fmt.Println("final address: ", frequentAddress)
    return frequentAddress
}
 
// processData 函数处理数据,根据要求过滤掉数据并根据规则更新状态
func ProcessData(captureInfos []db.CaptureInfo, personStatus []*db.PersonStatus, ruleInfos []db.PersonnelStatusRule, statusNo map[string]int, 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
    }
    //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 := 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
        //}
        // 更新过滤后的信息列表
        //fmt.Println("LastAppearanceTime: ", person.LastAppearanceTime)
        if statusNo[person.Status] > statusNo[info.Status] {
            continue
        }
        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
}