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 {
|
//fmt.Println("gogogogo!!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
// 将字符串类型的时间转换为time.Time类型,并只保留年月日
|
lastAppearanceTime := time.Unix(resident.LastAppearanceTime, 0)
|
lastAppearanceDate := time.Date(lastAppearanceTime.Year(), lastAppearanceTime.Month(), lastAppearanceTime.Day(), 0, 0, 0, 0, lastAppearanceTime.Location())
|
//lastAppearanceTime, err := time.Parse("2006-01-02", resident.LastAppearanceTime)
|
datePart := strings.Split(resident.CreateAt, "T")[0]
|
createdAt, err := time.Parse("2006-01-02", datePart)
|
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, // 存储年月日
|
MoveOutDate: &lastAppearanceDate, // 存储年月日
|
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 int) ([]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"
|
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
|
}
|