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, 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) 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 }