package rule import ( "basic.com/valib/logger.git" "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 { // 将字符串类型的时间转换为time.Time类型,并只保留年月日 lastAppearanceTime := time.Unix(resident.LastAppearanceTime, 0) lastAppearanceDate := time.Date(lastAppearanceTime.Year(), lastAppearanceTime.Month(), lastAppearanceTime.Day(), 0, 0, 0, 0, lastAppearanceTime.Location()) createdAt, err := time.Parse("2006-01-02", resident.CreateAt) if err != nil { fmt.Println(err) // 处理时间解析错误 // 可以选择跳过该条数据或者记录日志 continue } // 计算LastAppearanceTime和CreateAt距离当前日期的天数 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" } } if status == "" { continue } //fmt.Println("status: ", status) moveInout = append(moveInout, db.MoveInout{ DocumentNumber: resident.DocumentNumber, CommunityID: resident.CommunityId, MoveInDate: createdAt.Format("2006-01-02"), // 存储年月日 MoveOutDate: lastAppearanceDate.Format("2006-01-02"), // 存储年月日 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, labelId string) ([]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" info.Status = "visitor" captureInfo = append(captureInfo, info) identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelId, ExpireTime: GetCurrentDateAddDaysTimestamp(validDays)}) continue } captureInfo = append(captureInfo, info) } else { 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 } // 检查该地址是否已经在addrData中 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, communityID string, labelManage []db.LabelManage) []db.Identity { labelIndexMap := make(map[string]int) for index, labelManageInfo := range labelManage { labelIndexMap[labelManageInfo.Name] = index } identity := make([]db.Identity, 0) for _, info := range personInfos { 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) } //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) continue //return n/**/il, err } 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 } } countMaxRows, colS, colE := CountMaxRows(outModelMatrix, 30, 30) logger.Info("规律出勤时间段:", colS, colE, "\t次数: ", countMaxRows) if countMaxRows >= 10 { if info.Age <= 22 && info.Age >= 7 { lIndex := labelIndexMap["学生"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } else if info.Age >= 23 && info.Age <= 60 { lIndex := labelIndexMap["上班族"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } else if info.Age > 60 { lIndex := labelIndexMap["离退休人员"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } //fmt.Println("上班族", info.DocumentNumber, info.Age, countMaxRows, colS, colE) } else { countMaxRows, colS, colE := CountMaxRows(inModelMatrix, 30, 30) logger.Info("规律出勤时间段:", colS, colE, "\t次数: ", countMaxRows) if countMaxRows >= 10 { if info.Age <= 22 && info.Age >= 7 { lIndex := labelIndexMap["学生"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } else if info.Age >= 23 && info.Age <= 60 { lIndex := labelIndexMap["上班族"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } else if info.Age > 60 { lIndex := labelIndexMap["离退休人员"] identity = append(identity, db.Identity{ CommunityID: communityID, DocumentNumber: info.DocumentNumber, LabelId: labelManage[lIndex].ID, ExpireTime: GetCurrentDateAddDaysTimestamp(labelManage[lIndex].ValidDays)}) } //fmt.Println("上班族", info.DocumentNumber, info.Age, countMaxRows, colS, colE) } } //fmt.Println("outModelMatrix: ", outModelMatrix) //fmt.Println("inModelMatrix: ", inModelMatrix) } return identity } // 计算一阶导数 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 }